source: trunk/GSASII.py @ 2290

Last change on this file since 2290 was 2290, checked in by vondreele, 6 years ago

complex problem - fix summing of GE images; needed new item in Image Controls to save which reader was used. Change to OnImportGeneric? to keep self.formatName; GetImageData? put it in the call & check it with the available readers & GetImageZ to get it out of the Image Controls & apply it in GetImageData?.
Add a test on file size for GE files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 183.3 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2016-05-25 20:39:41 +0000 (Wed, 25 May 2016) $
6# $Author: vondreele $
7# $Revision: 2290 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2290 2016-05-25 20:39:41Z vondreele $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. Restarting GSAS-II')
59    print('*******************************************************')         
60    loc = os.path.dirname(__file__)
61    import subprocess
62    subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
63    sys.exit()
64   
65# load the GSAS routines
66import GSASIIpath
67GSASIIpath.SetVersionNumber("$Revision: 2290 $")
68import GSASIIIO as G2IO
69import GSASIIgrid as G2gd
70import GSASIIctrls as G2G
71import GSASIIplot as G2plt
72import GSASIIpwd as G2pwd
73import GSASIIpwdGUI as G2pdG
74import GSASIIphsGUI as G2phsG
75import GSASIIimgGUI as G2imG
76import GSASIIspc as G2spc
77import GSASIIstrMain as G2stMn
78import GSASIIstrIO as G2stIO
79import GSASIImath as G2mth
80import GSASIImapvars as G2mv
81import GSASIIobj as G2obj
82import GSASIIlattice as G2lat
83import GSASIIlog as log
84
85__version__ = '0.2.0'
86
87# PATCH: for Mavericks (OS X 10.9.x), wx produces an annoying warning about LucidaGrandeUI.
88# In case stderr has been suppressed there, redirect python error output to stdout. Nobody
89# else should care much about this.
90sys.stderr = sys.stdout
91
92def create(parent):
93    return GSASII(parent)
94
95def SetDefaultDData(dType,histoName,NShkl=0,NDij=0):
96    if dType in ['SXC','SNC']:
97        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
98            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
99            'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
100            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}],
101            'Flack':[0.0,False]}
102    elif dType == 'SNT':
103        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
104            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
105            'Extinction':['Lorentzian','None', {
106            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}]}
107    elif 'P' in dType:
108        return {'Histogram':histoName,'Show':False,'Scale':[1.0,False],
109            'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{},[],0.1],
110            'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
111                [1.,1.,1.,0.,0.,0.],6*[False,]],
112            'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
113                NShkl*[0.01,],NShkl*[False,]],
114            'HStrain':[NDij*[0.0,],NDij*[False,]],                         
115            'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
116
117class GSASII(wx.Frame):
118    '''Define the main GSAS-II frame and its associated menu items
119    '''
120    def MenuBinding(self,event):
121        '''Called when a menu is clicked upon; looks up the binding in table
122        '''
123        log.InvokeMenuCommand(event.GetId(),self,event)
124           
125    def Bind(self,eventtype,handler,*args,**kwargs):
126        '''Override the Bind function so that we can wrap calls that will be logged.
127       
128        N.B. This is a bit kludgy. Menu bindings with an id are wrapped and
129        menu bindings with an object and no id are not.
130        '''
131        if eventtype == wx.EVT_MENU and 'id' in kwargs:
132            menulabels = log.SaveMenuCommand(kwargs['id'],self,handler)
133            if menulabels:
134                wx.Frame.Bind(self,eventtype,self.MenuBinding,*args,**kwargs)
135                return
136        wx.Frame.Bind(self,eventtype,handler,*args,**kwargs)     
137   
138    def _Add_FileMenuItems(self, parent):
139        item = parent.Append(
140            help='Open a GSAS-II project file (*.gpx)', id=wx.ID_ANY,
141            kind=wx.ITEM_NORMAL,text='&Open project...')
142        self.Bind(wx.EVT_MENU, self.OnFileOpen, id=item.GetId())
143        item = parent.Append(
144            help='Save project under current name', id=wx.ID_ANY,
145            kind=wx.ITEM_NORMAL,text='&Save project')
146        self.Bind(wx.EVT_MENU, self.OnFileSave, id=item.GetId())
147        item = parent.Append(
148            help='Save current project to new file', id=wx.ID_ANY,
149            kind=wx.ITEM_NORMAL,text='Save project as...')
150        self.Bind(wx.EVT_MENU, self.OnFileSaveas, id=item.GetId())
151        item = parent.Append(
152            help='Create empty new project, saving current is optional', id=wx.ID_ANY,
153            kind=wx.ITEM_NORMAL,text='&New project')
154        self.Bind(wx.EVT_MENU, self.OnFileClose, id=item.GetId())
155        item = parent.Append(wx.ID_PREFERENCES, text = "&Preferences")
156        self.Bind(wx.EVT_MENU, self.OnPreferences, item)
157        if GSASIIpath.GetConfigValue('debug'):
158            def OnIPython(event):
159                GSASIIpath.IPyBreak()
160            item = parent.Append(wx.ID_ANY, text = "IPython Console")
161            self.Bind(wx.EVT_MENU, OnIPython, item)
162        item = parent.Append(
163            help='Exit from GSAS-II', id=wx.ID_ANY,
164            kind=wx.ITEM_NORMAL,text='&Exit')
165        self.Bind(wx.EVT_MENU, self.OnFileExit, id=item.GetId())
166       
167    def _Add_DataMenuItems(self,parent):
168        item = parent.Append(
169            help='',id=wx.ID_ANY,
170            kind=wx.ITEM_NORMAL,
171            text='Read image data...')
172        self.Bind(wx.EVT_MENU, self.OnImageRead, id=item.GetId())
173        item = parent.Append(
174            help='',id=wx.ID_ANY,
175            kind=wx.ITEM_NORMAL,
176            text='Read Powder Pattern Peaks...')
177        self.Bind(wx.EVT_MENU, self.OnReadPowderPeaks, id=item.GetId())
178        item = parent.Append(
179            help='',id=wx.ID_ANY,
180            kind=wx.ITEM_NORMAL,
181            text='Sum powder data')
182        self.Bind(wx.EVT_MENU, self.OnPwdrSum, id=item.GetId())
183        item = parent.Append(
184            help='',id=wx.ID_ANY,
185            kind=wx.ITEM_NORMAL,
186            text='Sum image data')
187        self.Bind(wx.EVT_MENU, self.OnImageSum, id=item.GetId())
188        item = parent.Append(
189            help='',id=wx.ID_ANY,
190            kind=wx.ITEM_NORMAL,
191            text='Add new phase')
192        self.Bind(wx.EVT_MENU, self.OnAddPhase, id=item.GetId())
193        item = parent.Append(
194            help='',id=wx.ID_ANY,
195            kind=wx.ITEM_NORMAL,
196            text='Delete phase')
197        self.Bind(wx.EVT_MENU, self.OnDeletePhase, id=item.GetId())
198        item = parent.Append(
199            help='',id=wx.ID_ANY,
200            kind=wx.ITEM_NORMAL,
201            text='Rename data') 
202        self.Bind(wx.EVT_MENU, self.OnRenameData, id=item.GetId())
203        item = parent.Append(
204            help='',id=wx.ID_ANY,
205            kind=wx.ITEM_NORMAL,
206            text='Delete data')
207        self.Bind(wx.EVT_MENU, self.OnDataDelete, id=item.GetId())
208               
209    def _Add_CalculateMenuItems(self,parent):
210        item = parent.Append(help='Make new PDFs from selected powder patterns', 
211            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='Make new PDFs')
212        self.MakePDF.append(item)
213#        item.Enable(False)
214        self.Bind(wx.EVT_MENU, self.OnMakePDFs, id=item.GetId())
215       
216        item = parent.Append(help='View least squares parameters', 
217            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='&View LS parms')
218        self.Bind(wx.EVT_MENU, self.ShowLSParms, id=item.GetId())
219       
220        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
221            text='&Refine')
222        if len(self.Refine): # extend state for new menus to match main (on mac)
223            state = self.Refine[0].IsEnabled()
224        else:
225            state = False
226        item.Enable(state)
227        self.Refine.append(item)
228        self.Bind(wx.EVT_MENU, self.OnRefine, id=item.GetId())
229       
230        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
231            text='Sequential refine')
232        if len(self.SeqRefine): # extend state for new menus to match main (on mac)
233            state = self.SeqRefine[0].IsEnabled()
234        else:
235            state = False
236        item.Enable(state)
237        self.SeqRefine.append(item) # save menu obj for use in self.EnableSeqRefineMenu
238        self.Bind(wx.EVT_MENU, self.OnSeqRefine, id=item.GetId())
239       
240    def _init_Imports(self):
241        '''import all the G2phase*.py & G2sfact*.py & G2pwd*.py files that
242        are found in the path
243        '''
244
245        self.ImportPhaseReaderlist = []
246        self._init_Import_routines('phase',self.ImportPhaseReaderlist,'Phase')
247        self.ImportSfactReaderlist = []
248        self._init_Import_routines('sfact',self.ImportSfactReaderlist,'Struct_Factor')
249        self.ImportPowderReaderlist = []
250        self._init_Import_routines('pwd',self.ImportPowderReaderlist,'Powder_Data')
251        self.ImportSmallAngleReaderlist = []
252        self._init_Import_routines('sad',self.ImportSmallAngleReaderlist,'SmallAngle_Data')
253        self.ImportImageReaderlist = []
254        self._init_Import_routines('img',self.ImportImageReaderlist,'Images')
255        self.ImportMenuId = {}
256
257    def _init_Import_routines(self,prefix,readerlist,errprefix):
258        '''import all the import readers matching the prefix
259        '''
260        #path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file
261        #pathlist = sys.path[:]
262        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
263        #path2GSAS2 = os.path.join(
264        #    os.path.dirname(os.path.realpath(__file__)), # location of this file
265        #    'imports')
266        pathlist = sys.path[:]
267        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
268        if '.' not in pathlist: pathlist.append('.') # insert the directory where G2 is started
269
270        filelist = []
271        for path in pathlist:
272            for filename in glob.iglob(os.path.join(
273                path,
274                "G2"+prefix+"*.py")):
275                filelist.append(filename)   
276                #print 'debug: found',filename
277        filelist = sorted(list(set(filelist))) # remove duplicates
278        for filename in filelist:
279            path,rootname = os.path.split(filename)
280            pkg = os.path.splitext(rootname)[0]
281            try:
282                fp = None
283                fp, fppath,desc = imp.find_module(pkg,[path,])
284                pkg = imp.load_module(pkg,fp,fppath,desc)
285                for clss in inspect.getmembers(pkg): # find classes defined in package
286                    if clss[0].startswith('_'): continue
287                    if inspect.isclass(clss[1]):
288                        # check if we have the required methods
289                        for m in 'Reader','ExtensionValidator','ContentsValidator':
290                            if not hasattr(clss[1],m): break
291                            if not callable(getattr(clss[1],m)): break
292                        else:
293                            reader = clss[1]() # create an import instance
294                            if reader.UseReader:
295                                readerlist.append(reader)
296            except AttributeError:
297                print 'Import_'+errprefix+': Attribute Error '+str(filename)
298            #except ImportError:
299            #    print 'Import_'+errprefix+': Error importing file '+str(filename)
300            except Exception,errmsg:
301                print('\nImport_'+errprefix+': Error importing file '+str(filename))
302                print('Error message: '+str(errmsg)+'\n')
303            if fp: fp.close()
304
305    def EnableSeqRefineMenu(self):
306        '''Enable or disable the sequential refinement menu items based on the
307        contents of the Controls 'Seq Data' item (if present)
308        '''
309        controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
310        if controls.get('Seq Data'):
311            for i in self.SeqRefine: i.Enable(True)
312        else:
313            for i in self.SeqRefine: i.Enable(False)
314
315    def PreviewFile(self,filename,fp):
316        'confirm we have the right file'
317        rdmsg = 'File '+str(filename)+' begins:\n\n'
318        for i in range(3):
319            rdmsg += fp.readline()
320        rdmsg += '\n\nDo you want to read this file?'
321        if not all([ord(c) < 128 and ord(c) != 0 for c in rdmsg]): # show only if ASCII
322            rdmsg = 'File '+str(
323                filename)+' is a binary file. Do you want to read this file?'
324        # it would be better to use something that
325        # would resize better, but this will do for now
326        dlg = wx.MessageDialog(
327            self, rdmsg,
328            'Is this the file you want?', 
329            wx.YES_NO | wx.ICON_QUESTION,
330            )
331        dlg.SetSize((700,300)) # does not resize on Mac
332        result = wx.ID_NO
333        try:
334            result = dlg.ShowModal()
335        finally:
336            dlg.Destroy()
337        if result == wx.ID_NO: return True
338        return False
339   
340    def OnImportGeneric(self,reader,readerlist,label,multiple=False,
341                        usedRanIdList=[],Preview=True,
342                        load2Tree=False):
343        '''Used for all imports, including Phases, datasets, images...
344
345        Called from :meth:`GSASII.OnImportPhase`, :meth:`GSASII.OnImportImage`,
346        :meth:`GSASII.OnImportSfact`, :meth:`GSASII.OnImportPowder` and
347        :meth:`GSASII.OnImportSmallAngle`
348
349        Uses reader_objects subclassed from :class:`GSASIIIO.ImportPhase`,
350        :class:`GSASIIIO.ImportStructFactor`,
351        :class:`GSASIIIO.ImportPowderData`,
352        :class:`GSASIIIO.ImportSmallAngleData` or
353        :class:`GSASIIIO.ImportImage`.
354        If a specific reader is specified, only that method will be called,
355        but if no reader is specified, every one that is potentially
356        compatible (by file extension) will be tried on the file(s)
357        selected in the Open File dialog.
358
359        :param reader_object reader: This will be a reference to
360          a particular object to be used to read a file or None,
361          if every appropriate reader should be used.
362
363        :param list readerlist: a list of reader objects appropriate for
364          the current read attempt. At present, this will be either
365          self.ImportPhaseReaderlist, self.ImportSfactReaderlist
366          self.ImportPowderReaderlist or self.ImportImageReaderlist
367          (defined in _init_Imports from the files found in the path),
368          but in theory this list could be tailored.
369          Used only when reader is None.
370
371        :param str label: string to place on the open file dialog:
372          Open `label` input file
373
374        :param bool multiple: True if multiple files can be selected
375          in the file dialog. False is default. At present True is used
376          only for reading of powder data.
377
378        :param list usedRanIdList: an optional list of random Ids that
379          have been used and should not be reused
380
381        :param bool Preview: indicates if a preview of the file should
382          be shown. Default is True, but set to False for image files
383          which are all binary.
384
385        :param bool load2Tree: indicates if the file should be loaded
386          into the data tree immediately (used for images only). True
387          only when called from :meth:`OnImportImage`; causes return
388          value to change to a list of True values rather than
389          reader objects.
390
391        :returns: a list of reader objects (rd_list) that were able
392          to read the specified file(s). This list may be empty.
393        '''
394        self.lastimport = ''
395        self.zipfile = None
396        singlereader = True
397        if reader is None:
398            singlereader = False
399            multiple = False
400            #print "use all formats"
401            choices = "any file (*.*)|*.*"
402            choices += "|zip archive (.zip)|*.zip"
403            extdict = {}
404            # compile a list of allowed extensions
405            for rd in readerlist:
406                fmt = rd.formatName
407                for extn in rd.extensionlist:
408                    if not extdict.get(extn): extdict[extn] = []
409                    extdict[extn] += [fmt,]
410            for extn in sorted(extdict.keys(),cmp=lambda x,y: cmp(x.lower(), y.lower())):
411                fmt = ''
412                for f in extdict[extn]:
413                    if fmt != "": fmt += ', '
414                    fmt += f
415                choices += "|" + fmt + " file (*" + extn + ")|*" + extn
416        else:
417            readerlist = [reader,]
418            # compile a list of allowed extensions
419            choices = reader.formatName + " file ("
420            w = ""
421            for extn in reader.extensionlist:
422                if w != "": w += ";"
423                w += "*" + extn
424            choices += w + ")|" + w
425            choices += "|zip archive (.zip)|*.zip"
426            if not reader.strictExtension:
427                choices += "|any file (*.*)|*.*"
428        # get the file(s)
429        if multiple:
430            mode = style=wx.OPEN | wx.MULTIPLE
431        else:
432            mode = style=wx.OPEN
433        filelist = G2G.GetImportFile(self,message="Choose "+label+" input file",
434                    defaultFile="",wildcard=choices, style=mode)
435        rd_list = []
436        filelist1 = []
437        for filename in filelist:
438            # is this a zip file?
439            if os.path.splitext(filename)[1].lower() == '.zip':
440                extractedfiles = G2IO.ExtractFileFromZip(
441                    filename,parent=self,
442                    multipleselect=True)
443                if extractedfiles is None: continue # error or Cancel
444                if extractedfiles != filename:
445                    self.zipfile = filename # save zip name
446                    filelist1 += extractedfiles
447                    continue
448            filelist1.append(filename)
449        filelist = filelist1
450        Start = True    #1st time read - clear selections below
451        for filename in filelist:
452            # is this a zip file?
453            if os.path.splitext(filename)[1].lower() == '.zip':
454                extractedfile = G2IO.ExtractFileFromZip(filename,parent=self)
455                if extractedfile is None: continue # error or Cancel
456                if extractedfile != filename:
457                    filename,self.zipfile = extractedfile,filename # now use the file that was created
458            # determine which formats are compatible with this file
459            primaryReaders = []
460            secondaryReaders = []
461            for rd in readerlist:
462                flag = rd.ExtensionValidator(filename)
463                if flag is None: 
464                    secondaryReaders.append(rd)
465                elif flag:
466                    primaryReaders.append(rd)
467            if len(secondaryReaders) + len(primaryReaders) == 0 and reader:
468                self.ErrorDialog('Not supported','The selected reader cannot read file '+filename)
469                return []
470            elif len(secondaryReaders) + len(primaryReaders) == 0:
471                self.ErrorDialog('No Format','No matching format for file '+filename)
472                return []
473
474            fp = None
475            msg = ''
476            fp = open(filename,'Ur')
477            if len(filelist) == 1 and Preview:
478                if self.PreviewFile(filename,fp): return []
479            self.lastimport = filename # this is probably not what I want to do -- it saves only the
480            # last name in a series. See rd.readfilename for a better name.
481
482            # try the file first with Readers that specify the
483            # file's extension and later with ones that merely allow it
484            errorReport = ''
485            for rd in primaryReaders+secondaryReaders:
486                if Start:   #clear old bank selections to allow new ones to be selected by user
487                    rd.selections = []
488                    Start = False
489                rd.ReInitialize() # purge anything from a previous read
490                fp.seek(0)  # rewind
491                rd.errors = "" # clear out any old errors
492                if not rd.ContentsValidator(fp): # rejected on cursory check
493                    errorReport += "\n  "+rd.formatName + ' validator error'
494                    if rd.errors: 
495                        errorReport += ': '+rd.errors
496                    continue 
497                repeat = True
498                rdbuffer = {} # create temporary storage for file reader
499                block = 0
500                while repeat: # loop if the reader asks for another pass on the file
501                    block += 1
502                    repeat = False
503                    fp.seek(0)  # rewind
504                    rd.objname = os.path.basename(filename)
505                    flag = False
506                    if GSASIIpath.GetConfigValue('debug'):
507                        flag = rd.Reader(filename,fp,self,
508                                         buffer=rdbuffer,
509                                         blocknum=block,
510                                         usedRanIdList=usedRanIdList,
511                                         )
512                    else:
513                        try:
514                            flag = rd.Reader(filename,fp,self,
515                                             buffer=rdbuffer,
516                                             blocknum=block,
517                                             usedRanIdList=usedRanIdList,
518                                             )
519                        except rd.ImportException as detail:
520                            rd.errors += "\n  Read exception: "+str(detail)
521                        except Exception as detail:
522                            import traceback
523                            rd.errors += "\n  Unhandled read exception: "+str(detail)
524                            rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
525                    if flag: # this read succeeded
526                        rd.readfilename = filename
527                        if load2Tree:   #images only
528                            if rd.repeatcount == 1 and not rd.repeat: # skip image number if only one in set
529                                rd.Data['ImageTag'] = None
530                            else:
531                                rd.Data['ImageTag'] = rd.repeatcount
532                            rd.Data['formatName'] = rd.formatName
533                            G2IO.LoadImage2Tree(rd.readfilename,self,rd.Comments,rd.Data,rd.Npix,rd.Image)
534                            rd_list.append(True) # save a stub the result before it is written over
535                            del rd.Image
536                        else:                                                   
537                            rd_list.append(copy.deepcopy(rd)) # save the result before it is written over
538                        if rd.repeat:
539                            repeat = True
540                        continue
541                    errorReport += '\n'+rd.formatName + ' read error'
542                    if rd.errors:
543                        errorReport += ': '+rd.errors
544                if rd_list: # read succeeded, was there a warning or any errors?
545                    if rd.warnings:
546                        self.ErrorDialog('Read Warning','The '+ rd.formatName+
547                                         ' reader reported a warning message:\n\n'+
548                                         rd.warnings)
549                    break # success in reading, try no further
550            else:
551                if singlereader:
552                    self.ErrorDialog('Read Error','The '+ rd.formatName+
553                                     ' reader was not able to read file '+filename+msg+
554                                     '\n\nError message(s):\n'+errorReport
555                                     )
556                else:
557                    self.ErrorDialog('Read Error','No reader was able to read file '+filename+msg+
558                                     '\n\nError messages:\n'+errorReport
559                                     )
560            if fp: fp.close()
561        return rd_list
562
563    def _Add_ImportMenu_Phase(self,parent):
564        '''configure the Import Phase menus accord to the readers found in _init_Imports
565        '''
566        submenu = wx.Menu()
567        item = parent.AppendMenu(wx.ID_ANY, 'Phase',
568            submenu, help='Import phase data')
569        for reader in self.ImportPhaseReaderlist:
570            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
571                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
572            self.ImportMenuId[item.GetId()] = reader
573            self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
574        item = submenu.Append(wx.ID_ANY,
575                              help='Import phase data, use file to try to determine format',
576                              kind=wx.ITEM_NORMAL,
577                              text='guess format from file')
578        self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
579       
580    def OnImportPhase(self,event):
581        '''Called in response to an Import/Phase/... menu item
582        to read phase information.
583        dict self.ImportMenuId is used to look up the specific
584        reader item associated with the menu item, which will be
585        None for the last menu item, which is the "guess" option
586        where all appropriate formats will be tried.
587        '''
588        # look up which format was requested
589        reqrdr = self.ImportMenuId.get(event.GetId())
590       
591        # make a list of phase names, ranId's and the histograms used in those phases
592        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
593        phaseNameList = usedHistograms.keys() # phase names in use
594        usedHKLFhists = [] # used single-crystal histograms
595        for p in usedHistograms:
596            for h in usedHistograms[p]:
597                if h.startswith('HKLF ') and h not in usedHKLFhists:
598                    usedHKLFhists.append(h)
599        rdlist = self.OnImportGeneric(reqrdr,
600                                  self.ImportPhaseReaderlist,
601                                  'phase',usedRanIdList=phaseRIdList)
602        if len(rdlist) == 0: return
603        # for now rdlist is only expected to have one element
604        # but below will allow multiple phases to be imported
605        # if ever the import routines ever implement multiple phase reads.
606        self.CheckNotebook()
607        newPhaseList = []
608        for rd in rdlist:
609            PhaseName = ''
610            dlg = wx.TextEntryDialog( # allow editing of phase name
611                                    self, 'Enter the name for the new phase',
612                                    'Edit phase name', rd.Phase['General']['Name'],
613                                    style=wx.OK)
614            while PhaseName == '':
615                dlg.CenterOnParent()
616                if dlg.ShowModal() == wx.ID_OK:
617                    PhaseName = dlg.GetValue().strip()
618                else:
619                    dlg.Destroy()
620                    return
621            dlg.Destroy()
622            # make new phase names unique
623            rd.Phase['General']['Name'] = G2obj.MakeUniqueLabel(PhaseName,phaseNameList)
624            PhaseName = rd.Phase['General']['Name'][:]
625            newPhaseList.append(PhaseName)
626            print 'Read phase '+str(PhaseName)+' from file '+str(self.lastimport)
627            if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
628                sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
629            else:
630                sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
631            psub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
632            self.PatternTree.SetItemPyData(psub,rd.Phase)
633            self.PatternTree.Expand(self.root) # make sure phases are seen
634            self.PatternTree.Expand(sub) 
635            self.PatternTree.Expand(psub)
636            self.PatternTree.SelectItem(psub) # show the page to complete the initialization (yuk!)
637            wx.Yield() # make sure call of GSASII.OnPatternTreeSelChanged happens before we go on
638
639            if rd.Constraints:
640                sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') # was created in CheckNotebook if needed
641                Constraints = self.PatternTree.GetItemPyData(sub)               
642                # TODO: make sure that NEWVAR names are unique here?
643                for i in rd.Constraints:
644                    if type(i) is dict:
645                        #for j in i: print j,' --> ',i[j]
646                        if '_Explain' not in Constraints: Constraints['_Explain'] = {}
647                        Constraints['_Explain'].update(i)
648                        continue
649                    Constraints['Phase'].append(i)
650        if not newPhaseList: return # somehow, no new phases
651        # get a list of existing histograms
652        PWDRlist = []
653        HKLFlist = []
654        if self.PatternTree.GetCount():
655            item, cookie = self.PatternTree.GetFirstChild(self.root)
656            while item:
657                name = self.PatternTree.GetItemText(item)
658                if name.startswith('PWDR ') and name not in PWDRlist:
659                    PWDRlist.append(name)
660                if name.startswith('HKLF ') and name not in HKLFlist:
661                    HKLFlist.append(name)
662                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
663        TextList = PWDRlist + HKLFlist
664        if not len(TextList): return # no data loaded yet
665        header = 'Select histogram(s) to add to new phase(s):'
666        for phaseName in newPhaseList:
667            header += '\n  '+str(phaseName)
668
669        notOK = True
670        while notOK:
671            result = G2G.ItemSelector(TextList,self,header,header='Add histogram(s)',multiple=True)
672            if not result: return
673            # check that selected single crystal histograms are not already in use!
674            used = [TextList[i] for i in result if TextList[i] in usedHKLFhists]
675            #for i in result:
676            #    if TextList[i] in usedHKLFhists: used.append(TextList[i])
677            if used:
678                msg = 'The following single crystal histogram(s) are already in use'
679                for i in used:
680                    msg += '\n  '+str(i)
681                msg += '\nAre you sure you want to add them to this phase? '
682                msg += 'Associating a single crystal dataset to >1 histogram is usually an error, '
683                msg += 'so No is suggested here.'
684                if self.ErrorDialog('Likely error',msg,self,wtype=wx.YES_NO) == wx.ID_YES: notOK = False
685            else:
686                notOK = False
687        # connect new phases to histograms
688        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
689        if not sub:
690            raise Exception('ERROR -- why are there no phases here?')
691        wx.BeginBusyCursor()
692        item, cookie = self.PatternTree.GetFirstChild(sub)
693        while item: # loop over (new) phases
694            phaseName = self.PatternTree.GetItemText(item)
695            data = self.PatternTree.GetItemPyData(item)
696            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
697            if phaseName not in newPhaseList: continue
698            generalData = data['General']
699            SGData = generalData['SGData']
700            Super = generalData.get('Super',0)
701            SuperVec = []
702            if Super:
703                SuperVec = np.array(generalData['SuperVec'][0])
704            UseList = data['Histograms']
705            NShkl = len(G2spc.MustrainNames(SGData))
706            NDij = len(G2spc.HStrainNames(SGData))
707            for i in result:
708                histoName = TextList[i]
709                if histoName in HKLFlist:
710                    #redo UpdateHKLFdata(histoName) here:
711                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
712                    refDict,reflData = self.PatternTree.GetItemPyData(Id)
713                    G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
714                    Super = reflData.get('Super',0)
715                    for iref,ref in enumerate(reflData['RefList']):
716                        hkl = ref[:3]
717                        if Super:
718                            H = list(hkl+SuperVec*ref[3])
719                        else:
720                            H = hkl
721                        ref[4+Super] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
722                        iabsnt = G2spc.GenHKLf(H,SGData)[0]
723                        if iabsnt:  #flag space gp. absences
724                            if Super: 
725                                if not ref[2+Super]:
726                                    ref[3+Super] = 0
727                                else:
728                                    ref[3+Super] = 1    #twin id
729                            else:
730                                ref[3] = 0
731                    UseList[histoName] = SetDefaultDData(reflData['Type'],histoName)
732                elif histoName in PWDRlist:
733                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
734                    refList = self.PatternTree.GetItemPyData(
735                        G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
736                    refList[generalData['Name']] = {}
737                    UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
738                else:
739                    raise Exception('Unexpected histogram '+str(histoName))
740        wx.EndBusyCursor()
741        return # success
742       
743    def _Add_ImportMenu_Image(self,parent):
744        '''configure the Import Image menus accord to the readers found in _init_Imports
745        '''
746        submenu = wx.Menu()
747        item = parent.AppendMenu(wx.ID_ANY, 'Image',
748            submenu, help='Import image file')
749        for reader in self.ImportImageReaderlist:
750            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
751                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
752            self.ImportMenuId[item.GetId()] = reader
753            self.Bind(wx.EVT_MENU, self.OnImportImage, id=item.GetId())
754        item = submenu.Append(wx.ID_ANY,
755                              help='Import image data, use file to try to determine format',
756                              kind=wx.ITEM_NORMAL,
757                              text='guess format from file')
758        self.Bind(wx.EVT_MENU, self.OnImportImage, id=item.GetId())
759       
760    def OnImportImage(self,event):
761        '''Called in response to an Import/Image/... menu item
762        to read an image from a file. Like all the other imports,
763        dict self.ImportMenuId is used to look up the specific
764        reader item associated with the menu item, which will be
765        None for the last menu item, which is the "guess" option
766        where all appropriate formats will be tried.
767
768        A reader object is filled each time an image is read.
769        '''
770        self.CheckNotebook()
771        # look up which format was requested
772        reqrdr = self.ImportMenuId.get(event.GetId())
773        rdlist = self.OnImportGeneric(reqrdr,
774                    self.ImportImageReaderlist,
775                    'image',multiple=True,Preview=False,
776                    load2Tree=True)
777        if rdlist: 
778            self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,self.Image,'Image Controls'))             #show last image to have beeen read
779                   
780    def _Add_ImportMenu_Sfact(self,parent):
781        '''configure the Import Structure Factor menus accord to the readers found in _init_Imports
782        '''
783        submenu = wx.Menu()
784        item = parent.AppendMenu(wx.ID_ANY, 'Structure Factor',
785            submenu, help='Import Structure Factor data')
786        for reader in self.ImportSfactReaderlist:
787            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,               
788                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
789            self.ImportMenuId[item.GetId()] = reader
790            self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
791        item = submenu.Append(wx.ID_ANY,
792            help='Import Structure Factor, use file to try to determine format',
793            kind=wx.ITEM_NORMAL,
794            text='guess format from file')
795        self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
796
797    def OnImportSfact(self,event):
798        '''Called in response to an Import/Structure Factor/... menu item
799        to read single crystal datasets.
800        dict self.ImportMenuId is used to look up the specific
801        reader item associated with the menu item, which will be
802        None for the last menu item, which is the "guess" option
803        where all appropriate formats will be tried.
804        '''
805        # get a list of existing histograms
806        HKLFlist = []
807        if self.PatternTree.GetCount():
808            item, cookie = self.PatternTree.GetFirstChild(self.root)
809            while item:
810                name = self.PatternTree.GetItemText(item)
811                if name.startswith('HKLF ') and name not in HKLFlist:
812                    HKLFlist.append(name)
813                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
814        # look up which format was requested
815        reqrdr = self.ImportMenuId.get(event.GetId())
816        rdlist = self.OnImportGeneric(reqrdr,self.ImportSfactReaderlist,
817            'Structure Factor',multiple=True)
818        if len(rdlist) == 0: return
819        self.CheckNotebook()
820        newHistList = []
821        for rd in rdlist:
822            HistName = rd.objname
823            if len(rdlist) <= 2: 
824                dlg = wx.TextEntryDialog( # allow editing of Structure Factor name
825                    self, 'Enter the name for the new Structure Factor',
826                    'Edit Structure Factor name', HistName,
827                    style=wx.OK)
828                dlg.CenterOnParent()
829                if dlg.ShowModal() == wx.ID_OK:
830                    HistName = dlg.GetValue()
831                dlg.Destroy()
832            HistName = 'HKLF '+HistName
833            # make new histogram names unique
834            if len(rd.Banks):
835                for Bank in rd.Banks:
836                    valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
837                    HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
838                    print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
839                    Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
840                    if not Bank['RefDict'].get('FF'):
841                        Bank['RefDict']['FF'] = {}
842                    self.PatternTree.SetItemPyData(Id,[valuesdict,Bank['RefDict']])
843                    Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
844                    self.PatternTree.SetItemPyData(Sub,copy.copy(rd.Parameters))
845                    self.PatternTree.SetItemPyData(
846                        self.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
847                    newHistList.append(HistName)
848            else:
849                valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
850                HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
851                print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
852                if not rd.RefDict.get('FF'):
853                    rd.RefDict['FF'] = {}
854                Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
855                self.PatternTree.SetItemPyData(Id,[valuesdict,rd.RefDict])
856                Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
857                self.PatternTree.SetItemPyData(Sub,rd.Parameters)
858                self.PatternTree.SetItemPyData(
859                    self.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
860                newHistList.append(HistName)
861               
862            self.PatternTree.SelectItem(Id)
863            self.PatternTree.Expand(Id)
864            self.Sngl = True
865
866        if not newHistList: return # somehow, no new histograms
867        # make a list of phase names
868        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
869        phaseNameList = usedHistograms.keys() # phase names in use
870        if not phaseNameList: return # no phases yet, nothing to do
871        header = 'Select phase(s) to add the new\nsingle crystal dataset(s) to:'
872        for Name in newHistList:
873            header += '\n  '+str(Name)
874        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
875        if not result: return
876        # connect new phases to histograms
877        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
878        if not sub:
879            raise Exception('ERROR -- why are there no phases here?')
880        wx.BeginBusyCursor()
881        item, cookie = self.PatternTree.GetFirstChild(sub)
882        iph = -1
883        while item: # loop over (new) phases
884            iph += 1
885            phaseName = self.PatternTree.GetItemText(item)
886            data = self.PatternTree.GetItemPyData(item)
887            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
888            if iph not in result: continue
889            generalData = data['General']
890            SGData = generalData['SGData']
891            Super = generalData.get('Super',0)
892            SuperVec = []
893            if Super:
894                SuperVec = np.array(generalData['SuperVec'][0])
895            UseList = data['Histograms']
896            for histoName in newHistList:
897                #redo UpdateHKLFdata(histoName) here:
898                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
899                refDict,reflData = self.PatternTree.GetItemPyData(Id)
900                UseList[histoName] = SetDefaultDData(reflData['Type'],histoName)
901                G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
902                if 'TwMax' in reflData:     #nonmerohedral twins present
903                    UseList[histoName]['Twins'] = []
904                    for iT in range(reflData['TwMax'][0]+1):
905                        if iT in reflData['TwMax'][1]:
906                            UseList[histoName]['Twins'].append([False,0.0])
907                        else:
908                            UseList[histoName]['Twins'].append([np.array([[1,0,0],[0,1,0],[0,0,1]]),[1.0,False,reflData['TwMax'][0]]])
909                else:   #no nonmerohedral twins
910                    UseList[histoName]['Twins'] = [[np.array([[1,0,0],[0,1,0],[0,0,1]]),[1.0,False,0]],]
911                for iref,ref in enumerate(reflData['RefList']):
912                    hkl = ref[:3]
913                    if Super:
914                        H = list(hkl+SuperVec*ref[3])
915                    else:
916                        H = hkl
917                    ref[4+Super] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
918                    iabsnt,mul,Uniq,phi = G2spc.GenHKLf(H,SGData)
919                    if iabsnt:  #flag space gp. absences
920                        if Super: 
921                            if not ref[2+Super]:
922                                ref[3+Super] = 0
923                            else:
924                                ref[3+Super] = 1    #twin id?
925                        else:
926                            ref[3] = 0
927        wx.EndBusyCursor()
928       
929        return # success
930
931    def _Add_ImportMenu_powder(self,parent):
932        '''configure the Powder Data menus accord to the readers found in _init_Imports
933        '''
934        submenu = wx.Menu()
935        item = parent.AppendMenu(wx.ID_ANY, 'Powder Data',
936            submenu, help='Import Powder data')
937        for reader in self.ImportPowderReaderlist:
938            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
939                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
940            self.ImportMenuId[item.GetId()] = reader
941            self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
942        item = submenu.Append(wx.ID_ANY,
943            help='Import powder data, use file to try to determine format',
944            kind=wx.ITEM_NORMAL,text='guess format from file')
945        self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
946        submenu.AppendSeparator()
947        item = submenu.Append(wx.ID_ANY,
948            help='Create a powder data set entry that will be simulated',
949            kind=wx.ITEM_NORMAL,text='Simulate a dataset')
950        self.Bind(wx.EVT_MENU, self.OnDummyPowder, id=item.GetId())
951       
952    def OpenPowderInstprm(self,instfile):
953        '''Read a GSAS-II (new) instrument parameter file
954
955        :param str instfile: name of instrument parameter file
956
957        '''
958        File = open(instfile,'r')
959        lines = File.readlines()
960        File.close()
961        return lines       
962           
963    def ReadPowderInstprm(self,instLines,bank,databanks,rd):
964        '''Read lines from a GSAS-II (new) instrument parameter file
965        similar to G2pwdGUI.OnLoad
966        If instprm file has multiple banks each with header #Bank n: ..., this
967        finds matching bank no. to load - problem with nonmatches?
968
969        :param list instLines: strings from GSAS-II parameter file; can be concatenated with ';'
970        :param int  bank: bank number to check when instprm file has '#BANK n:...' strings
971            when bank = n then use parameters; otherwise skip that set. Ignored if BANK n:
972            not present. NB: this kind of instprm file made by a Save all profile command in Instrument Parameters
973        :return dict: Inst  instrument parameter dict if OK, or
974                str: Error message if failed   
975        '''
976        if 'GSAS-II' not in instLines[0]: # not a valid file
977            return 'Not a valid GSAS-II instprm file'
978        newItems = []
979        newVals = []
980        Found = False
981        il = 0
982        while il < len(instLines):
983            S = instLines[il]
984            if S[0] == '#':
985                if Found:
986                    break
987                if 'Bank' in S:
988                    if bank == int(S.split(':')[0].split()[1]):
989                        il += 1
990                        S = instLines[il]
991                    else:
992                        il += 1
993                        S = instLines[il]
994                        while il < len(instLines) and '#Bank' not in S:
995                            il += 1
996                            if il == len(instLines):
997                                return 'Bank %d not found in instprm file'%(bank)
998                            S = instLines[il]
999                        continue
1000                else:   #a non #Bank file
1001                    il += 1
1002                    S = instLines[il]
1003            Found = True
1004            S = S.replace(' ','')
1005            SS = S[:-1].split(';')
1006            for s in SS:
1007                [item,val] = s.split(':')
1008                newItems.append(item)
1009                try:
1010                    newVals.append(float(val))
1011                except ValueError:
1012                    newVals.append(val)
1013            il += 1                       
1014        return [G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}]
1015       
1016    def ReadPowderIparm(self,instfile,bank,databanks,rd):
1017        '''Read a GSAS (old) instrument parameter file
1018
1019        :param str instfile: name of instrument parameter file
1020        :param int bank: the bank number read in the raw data file
1021        :param int databanks: the number of banks in the raw data file.
1022          If the number of banks in the data and instrument parameter files
1023          agree, then the sets of banks are assumed to match up and bank
1024          is used to select the instrument parameter file. If not, the user
1025          is asked to make a selection.
1026        :param obj rd: the raw data (histogram) data object. This
1027          sets rd.instbank.
1028
1029        '''
1030        if not os.path.exists(instfile): # no such file
1031            return {}
1032        fp = 0
1033        try:
1034            fp = open(instfile,'Ur')
1035            Iparm = {}
1036            for S in fp:
1037                if '#' in S[0]:
1038                    continue
1039                Iparm[S[:12]] = S[12:-1]
1040        except IOError:
1041            print('Error reading file:'+str(instfile))
1042        if fp:       
1043            fp.close()
1044
1045        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
1046        hType = Iparm['INS   HTYPE '].strip()
1047        if ibanks == 1: # there is only one bank here, return it
1048            rd.instbank = 1
1049            return Iparm
1050        if 'PNT' in hType:
1051            rd.instbank = bank
1052        elif ibanks != databanks:
1053            # number of banks in data and prm file not not agree, need a
1054            # choice from a human here
1055            choices = []
1056            for i in range(1,1+ibanks):
1057                choices.append('Bank '+str(i))
1058            bank = rd.BlockSelector(
1059                choices, self,
1060                title='Select an instrument parameter bank for '+
1061                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
1062                '\nOr use Cancel to select from the default parameter sets',
1063                header='Block Selector')
1064        if bank is None: return {}
1065        # pull out requested bank # bank from the data, and change the bank to 1
1066        IparmS = {}
1067        for key in Iparm:
1068            if 'INS' in key[:3]:    #skip around rubbish lines in some old iparm files
1069                if key[4:6] == "  ":
1070                    IparmS[key] = Iparm[key]
1071                elif int(key[4:6].strip()) == bank:
1072                    IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
1073        rd.instbank = bank
1074        return IparmS
1075                       
1076    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
1077        '''Open and read an instrument parameter file for a data file
1078        Returns the list of parameters used in the data tree
1079
1080        :param obj rd: the raw data (histogram) data object.
1081
1082        :param str prevIparm: not used
1083
1084        :param str lastIparmfile: Name of last instrument parameter
1085          file that was read, or a empty string.
1086
1087        :param str lastdatafile: Name of last data file that was read.
1088
1089        :returns: a list of two dicts, the first containing instrument parameters
1090          and the second used for TOf lookup tables for profile coeff.
1091
1092        '''
1093        def SetPowderInstParms(Iparm, rd):
1094            '''extracts values from instrument parameters in rd.instdict
1095            or in array Iparm.
1096            Create and return the contents of the instrument parameter tree entry.
1097            '''
1098            Irads = {0:' ',1:'CrKa',2:'FeKa',3:'CuKa',4:'MoKa',5:'AgKa',6:'TiKa',7:'CoKa'}
1099            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
1100            # override inst values with values read from data file
1101            Bank = rd.powderentry[2]    #should be used in multibank iparm files
1102            if rd.instdict.get('type'):
1103                DataType = rd.instdict.get('type')
1104            data = [DataType,]
1105            instname = Iparm.get('INS  1INAME ')
1106            irad = int(Iparm.get('INS  1 IRAD ','0'))
1107            if instname:
1108                rd.Sample['InstrName'] = instname.strip()
1109            if 'C' in DataType:
1110                wave1 = None
1111                wave2 = 0.0
1112                if rd.instdict.get('wave'):
1113                    wl = rd.instdict.get('wave')
1114                    wave1 = wl[0]
1115                    if len(wl) > 1: wave2 = wl[1]
1116                s = Iparm['INS  1 ICONS']
1117                if not wave1:
1118                    wave1 = G2IO.sfloat(s[:10])
1119                    wave2 = G2IO.sfloat(s[10:20])
1120                v = (wave1,wave2,
1121                     G2IO.sfloat(s[20:30]),G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
1122                if not v[1]:
1123                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
1124                    v = (v[0],v[2],v[4])
1125                    codes = [0,0,0,0]
1126                else:
1127                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
1128                    codes = [0,0,0,0,0,0]
1129                data.extend(v)
1130                if 'INS  1PRCF  ' in Iparm:
1131                    v1 = Iparm['INS  1PRCF  '].split()                                                 
1132                    v = Iparm['INS  1PRCF 1'].split()
1133                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1134                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1135                    v = Iparm['INS  1PRCF 2'].split()
1136                    if v1[0] == 3:
1137                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1138                    else:
1139                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file                   
1140                else:
1141                    v1 = Iparm['INS  1PRCF1 '].split()                                                 
1142                    v = Iparm['INS  1PRCF11'].split()
1143                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1144                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1145                    v = Iparm['INS  1PRCF12'].split()
1146                    if v1[0] == 3:
1147                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1148                    else:
1149                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
1150                codes.extend([0,0,0,0,0,0,0])
1151                Iparm1 = G2IO.makeInstDict(names,data,codes)
1152                Iparm1['Source'] = [Irads[irad],Irads[irad]]
1153                Iparm1['Bank'] = [Bank,Bank,0]
1154                return [Iparm1,{}]
1155            elif 'T' in DataType:
1156                names = ['Type','fltPath','2-theta','difC','difA', 'difB','Zero','alpha','beta-0','beta-1',
1157                    'beta-q','sig-0','sig-1','sig-2','sig-q', 'X','Y','Azimuth',]
1158                codes = [0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,]
1159                azm = 0.
1160                if 'INS  1DETAZM' in Iparm:
1161                    azm = float(Iparm['INS  1DETAZM'])
1162                s = Iparm['INS   FPATH1'].split()
1163                fltPath0 = G2IO.sfloat(s[0])
1164                if 'INS  1BNKPAR' not in Iparm:     #bank missing from Iparm file
1165                    return []
1166                s = Iparm['INS  1BNKPAR'].split()
1167                fltPath1 = G2IO.sfloat(s[0])
1168                data.extend([fltPath0+fltPath1,])               #Flight path source-sample-detector
1169                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
1170                s = Iparm['INS  1 ICONS'].split()
1171                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,G2IO.sfloat(s[2])])    #difC,difA,difB,Zero
1172                if 'INS  1PRCF  ' in Iparm:
1173                    s = Iparm['INS  1PRCF  '].split()
1174                    pfType = int(s[0])
1175                    s = Iparm['INS  1PRCF 1'].split()
1176                    if abs(pfType) == 1:
1177                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1178                        s = Iparm['INS  1PRCF 2'].split()
1179                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1180                    elif abs(pfType) in [3,4,5]:
1181                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1182                        if abs(pfType) == 4:
1183                            data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1184                        else:
1185                            s = Iparm['INS  1PRCF 2'].split()
1186                            data.extend([0.0,0.0,G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                       
1187                    elif abs(pfType) == 2:
1188                        data.extend([G2IO.sfloat(s[1]),0.0,1./G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1189                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                           
1190                else:
1191                    s = Iparm['INS  1PRCF1 '].split()
1192                    pfType = int(s[0])
1193                    s = Iparm['INS  1PRCF11'].split()
1194                    if abs(pfType) == 1:
1195                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1196                        s = Iparm['INS  1PRCF12'].split()
1197                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1198                    elif abs(pfType) in [3,4,5]:
1199                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1200                        if abs(pfType) == 4:
1201                            data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1202                        else:
1203                            s = Iparm['INS  1PRCF12'].split()
1204                            data.extend([0.0,0.0,G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                       
1205                Inst1 = G2IO.makeInstDict(names,data,codes)
1206                Inst1['Bank'] = [Bank,Bank,0]
1207                Inst2 = {}
1208                if pfType < 0:
1209                    Ipab = 'INS  1PAB'+str(-pfType)
1210                    Npab = int(Iparm[Ipab+'  '].strip())
1211                    Inst2['Pdabc'] = []
1212                    for i in range(Npab):
1213                        k = Ipab+str(i+1).rjust(2)
1214                        s = Iparm[k].split()
1215                        Inst2['Pdabc'].append([float(t) for t in s])
1216                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
1217                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
1218                if 'INS  1I ITYP' in Iparm:
1219                    s = Iparm['INS  1I ITYP'].split()
1220                    Ityp = int(s[0])
1221                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
1222                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
1223                    if Ityp in [1,2,4]:
1224                        Inst2['Itype'] = Itypes[Ityp-1]
1225                        Inst2['Tminmax'] = Tminmax
1226                        Icoeff = []
1227                        Iesd = []
1228                        Icovar = []                   
1229                        for i in range(3):
1230                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
1231                            Icoeff += [float(S) for S in s]
1232                            s = Iparm['INS  1IECOF'+str(i+1)].split()
1233                            Iesd += [float(S) for S in s]
1234                        NT = 10
1235                        for i in range(8):
1236                            s = Iparm['INS  1IECOR'+str(i+1)]
1237                            if i == 7:
1238                                NT = 8
1239                            Icovar += [float(s[6*j:6*j+6]) for j in range(NT)]
1240                        Inst2['Icoeff'] = Icoeff
1241                        Inst2['Iesd'] = Iesd
1242                        Inst2['Icovar'] = Icovar
1243                return [Inst1,Inst2]
1244               
1245        def GetDefaultParms(self,rd):
1246            '''Solicits from user a default set of parameters & returns Inst parm dict
1247            param: self: refers to the GSASII main class
1248            param: rd: importer data structure
1249            returns: dict: Instrument parameter dictionary
1250            '''       
1251            sind = lambda x: math.sin(x*math.pi/180.)
1252            tand = lambda x: math.tan(x*math.pi/180.)
1253            import defaultIparms as dI
1254            while True: # loop until we get a choice
1255                choices = []
1256                head = 'Select from default instrument parameters for '+rd.idstring
1257   
1258                for l in dI.defaultIparm_lbl:
1259                    choices.append('Defaults for '+l)
1260                res = rd.BlockSelector(choices,ParentFrame=self,title=head,
1261                    header='Select default inst parms',useCancel=True)
1262                if res is None: return None
1263                rd.instfile = ''
1264                if 'Generic' in choices[res]:
1265                    dlg = G2G.MultiFloatDialog(self,title='Generic TOF detector bank',
1266                        prompts=['Total FP','2-theta',],values=[25.0,150.,],
1267                            limits=[[6.,200.],[5.,175.],],formats=['%6.2f','%6.1f',])
1268                    if dlg.ShowModal() == wx.ID_OK: #strictly empirical approx.
1269                        FP,tth = dlg.GetValues()
1270                        difC = 505.632*FP*sind(tth/2.)
1271                        sig1 = 50.+2.5e-6*(difC/tand(tth/2.))**2
1272                        bet1 = .00226+7.76e+11/difC**4
1273                        rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1274                        Inst = self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)
1275                        Inst[0]['difC'] = [difC,difC,0]
1276                        Inst[0]['sig-1'] = [sig1,sig1,0]
1277                        Inst[0]['beta-1'] = [bet1,bet1,0]
1278                        return Inst    #this is [Inst1,Inst2] a pair of dicts
1279                    dlg.Destroy()
1280                else:
1281                    rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1282                    return self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1283
1284        # stuff we might need from the reader
1285        filename = rd.powderentry[0]
1286        bank = rd.powderentry[2]
1287        numbanks = rd.numbanks
1288        #1st priority: is there an instrument parameter file matching the current file
1289        # with extension .instprm, .prm, .inst, or .ins? If so read it
1290        basename = os.path.splitext(filename)[0]
1291        for ext in '.prm','.inst','.ins','.instprm':
1292            if self.zipfile:
1293                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1294                    selection=os.path.split(basename + ext)[1],parent=self)
1295                if instfile == None:
1296                    continue
1297            else:
1298                instfile = basename + ext
1299            if not os.path.exists(instfile):
1300                continue
1301            if 'instprm' in instfile:
1302                Lines = self.OpenPowderInstprm(instfile)
1303                instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1304                if 'list' in str(type(instParmList)):
1305                    rd.instfile = instfile
1306                    rd.instmsg = 'GSAS-II file '+instfile
1307                    return instParmList
1308                else:
1309                    #print 'debug: open/read failed',instfile
1310                    pass # fail silently
1311            else:
1312                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1313                if Iparm:
1314                    #print 'debug: success'
1315                    rd.instfile = instfile
1316                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1317                    return SetPowderInstParms(Iparm,rd)
1318                else:
1319                    #print 'debug: open/read failed',instfile
1320                    pass # fail silently
1321
1322        #2nd priority: is there an instrument parameter file defined for the current data set?
1323        # or if this is a read on a set of set of files, use the last one again
1324        #if rd.instparm as found in data file header or (lastdatafile == filename and lastIparmfile):
1325        if rd.instparm or lastIparmfile:
1326            if rd.instparm:
1327                instfile = os.path.join(os.path.split(filename)[0],rd.instparm)
1328            else:
1329                # for multiple reads of one data file, reuse the inst parm file
1330                instfile = lastIparmfile
1331#            if self.zipfile:
1332#                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1333#                    selection=os.path.split(instfile)[1],parent=self)
1334            if instfile != None and os.path.exists(instfile):
1335                #print 'debug: try read',instfile
1336                if 'instprm' in instfile:   #GSAS-II file must have .instprm as extension
1337                    Lines = self.OpenPowderInstprm(instfile)
1338                    if Lines is not None:
1339                        instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)   #this is [Inst1,Inst2] a pair of dicts
1340                else:   #old GSAS style iparm file - could be named anything!
1341                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1342                    if Iparm:
1343                        #print 'debug: success'
1344                        rd.instfile = instfile
1345                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1346                        instParmList = SetPowderInstParms(Iparm,rd)     #this is [Inst1,Inst2] a pair of dicts
1347                if 'list' in str(type(instParmList)):   #record stuff & return stuff
1348                    rd.instfile = instfile
1349                    rd.instmsg = 'GSAS-II file '+instfile
1350                    return instParmList
1351                else:   #bad iparms - try default
1352                    rd.instmsg = instParmList   #an error message
1353                    return GetDefaultParms(self,rd)
1354            else:
1355                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1356                    +str(instfile)+' requested by file '+ filename)
1357        #Finally - ask user for Instrument parametrs file - seems it can't be in a zip file
1358        while True: # loop until we get a file that works or we get a cancel
1359            instfile = ''
1360            pth = G2G.GetImportPath(self)
1361            if not pth: pth = '.'
1362            dlg = wx.FileDialog(self,
1363                'Choose inst. param file for "'+rd.idstring+'" (or Cancel for default)',
1364                pth, '',
1365                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1366                'GSAS-II iparm file (*.instprm)|*.instprm|'
1367                'All files (*.*)|*.*', wx.OPEN)
1368            if os.path.exists(lastIparmfile):
1369                dlg.SetFilename(lastIparmfile)
1370            if dlg.ShowModal() == wx.ID_OK:
1371                instfile = dlg.GetPath()
1372            dlg.Destroy()
1373            if not instfile: 
1374                return GetDefaultParms(self,rd) #on Cancel/break
1375            if 'instprm' in instfile:
1376                Lines = self.OpenPowderInstprm(instfile)
1377                if Lines is not None:
1378                    instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1379                if 'list' in str(type(instParmList)):
1380                    rd.instfile = instfile
1381                    rd.instmsg = 'GSAS-II file '+instfile
1382                    return instParmList
1383                else:
1384                    rd.instmsg = instParmList   #an error message
1385                    return GetDefaultParms(self,rd)
1386            else:
1387                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1388                if Iparm:
1389                    #print 'debug: success with',instfile
1390                    rd.instfile = instfile
1391                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1392                    return SetPowderInstParms(Iparm,rd)
1393                else:
1394                    self.ErrorDialog('Read Error',
1395                                     'Error opening/reading file '+str(instfile))
1396       
1397    def OnImportPowder(self,event):
1398        '''Called in response to an Import/Powder Data/... menu item
1399        to read a powder diffraction data set.
1400        dict self.ImportMenuId is used to look up the specific
1401        reader item associated with the menu item, which will be
1402        None for the last menu item, which is the "guess" option
1403        where all appropriate formats will be tried.
1404
1405        Also reads an instrument parameter file for each dataset.
1406        '''
1407        # get a list of existing histograms
1408        PWDRlist = []
1409        if self.PatternTree.GetCount():
1410            item, cookie = self.PatternTree.GetFirstChild(self.root)
1411            while item:
1412                name = self.PatternTree.GetItemText(item)
1413                if name.startswith('PWDR ') and name not in PWDRlist:
1414                    PWDRlist.append(name)
1415                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1416        # look up which format was requested
1417        reqrdr = self.ImportMenuId.get(event.GetId()) 
1418        rdlist = self.OnImportGeneric(
1419            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1420        if len(rdlist) == 0: return
1421        self.CheckNotebook()
1422        Iparm = None
1423        lastIparmfile = ''
1424        lastdatafile = ''
1425        newHistList = []
1426        self.EnablePlot = False
1427        for rd in rdlist:
1428            if 'Instrument Parameters' not in rd.pwdparms:
1429                # get instrument parameters for each dataset, unless already set
1430                Iparms = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1431                if not Iparms:  #may have bailed out
1432                    Id = 0
1433                    continue
1434                Iparm1,Iparm2 = Iparms
1435                if rd.repeat_instparm: 
1436                    lastIparmfile = rd.instfile
1437                # override any keys in read instrument parameters with ones set in import
1438                for key in Iparm1: 
1439                    if key in rd.instdict:
1440                        Iparm1[key] = rd.instdict[key]
1441            else:
1442                Iparm1,Iparm2 = rd.pwdparms['Instrument Parameters']
1443            lastdatafile = rd.powderentry[0]
1444            HistName = rd.idstring
1445            HistName = 'PWDR '+HistName
1446            # make new histogram names unique
1447            if HistName in PWDRlist:
1448                dlg = wx.MessageDialog(self,'Skip %s?'%(HistName),'Duplicate data name',wx.YES_NO)
1449                try:
1450                    if dlg.ShowModal() == wx.ID_YES:
1451                        Id = 0
1452                        continue
1453                finally:
1454                    dlg.Destroy()
1455            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1456            print 'Read powder data '+str(HistName)+ \
1457                ' from file '+str(rd.readfilename) + \
1458                ' with parameters from '+str(rd.instmsg)
1459            # data are read, now store them in the tree
1460            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1461            if 'T' in Iparm1['Type'][0]:
1462                if not rd.clockWd and rd.GSAS:
1463                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1464                cw = np.diff(rd.powderdata[0])
1465                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1466                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
1467                    npts = min(len(rd.powderdata[0]),len(rd.powderdata[1]),len(cw))
1468                    rd.powderdata[1] = rd.powderdata[1][:npts]/cw[:npts]
1469                    rd.powderdata[2] = rd.powderdata[2][:npts]*cw[:npts]**2  #1/var=w at this point
1470                else:       #NB: from topas/fullprof type files
1471                    rd.powderdata[1] = rd.powderdata[1][:-1]
1472                    rd.powderdata[2] = rd.powderdata[2][:-1]
1473                if 'Itype' in Iparm2:
1474                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1475                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1476                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1477                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1478                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1479                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1480                    var += WYI*rd.powderdata[1]**2
1481                    var /= YI**2
1482                    rd.powderdata[2] = 1./var
1483                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1484                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1485                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1486            valuesdict = {
1487                'wtFactor':1.0,
1488                'Dummy':False,
1489                'ranId':ran.randint(0,sys.maxint),
1490                'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1491                'qPlot':False,'dPlot':False,'sqrtPlot':False
1492                }
1493            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1494            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1495            self.PatternTree.SetItemPyData(
1496                self.PatternTree.AppendItem(Id,text='Comments'),
1497                rd.comments)
1498            Tmin = min(rd.powderdata[0])
1499            Tmax = max(rd.powderdata[0])
1500            self.PatternTree.SetItemPyData(
1501                self.PatternTree.AppendItem(Id,text='Limits'),
1502                rd.pwdparms.get('Limits',[(Tmin,Tmax),[Tmin,Tmax]])
1503                )
1504            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1505            self.PatternTree.SetItemPyData(
1506                self.PatternTree.AppendItem(Id,text='Background'),
1507                rd.pwdparms.get('Background',
1508                    [['chebyschev',True,3,1.0,0.0,0.0],{'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1509                    )
1510            self.PatternTree.SetItemPyData(
1511                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1512                [Iparm1,Iparm2])
1513            self.PatternTree.SetItemPyData(
1514                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1515                rd.Sample)
1516            self.PatternTree.SetItemPyData(
1517                self.PatternTree.AppendItem(Id,text='Peak List')
1518                ,{'peaks':[],'sigDict':{}})
1519            self.PatternTree.SetItemPyData(
1520                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1521                [[],[]])
1522            self.PatternTree.SetItemPyData(
1523                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1524                [])
1525            self.PatternTree.SetItemPyData(
1526                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1527                {})
1528            # if any Control values have been set, move them into tree
1529            Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
1530            Controls.update(rd.Controls)
1531            newHistList.append(HistName)
1532            rd.repeat_instparm = False  #clear the iparm reuse flag
1533        else:
1534            self.EnablePlot = True
1535            if Id:
1536                self.PatternTree.Expand(Id)
1537                self.PatternTree.SelectItem(Id)
1538
1539        if not newHistList: return # somehow, no new histograms
1540        # make a list of phase names
1541        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1542        phaseNameList = usedHistograms.keys() # phase names in use
1543        if not phaseNameList: return # no phases yet, nothing to do
1544        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1545        for Name in newHistList:
1546            header += '\n  '+str(Name)
1547
1548        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1549        if not result: return
1550        # connect new phases to histograms
1551        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1552        if not sub:
1553            raise Exception('ERROR -- why are there no phases here?')
1554        item, cookie = self.PatternTree.GetFirstChild(sub)
1555        iph = -1
1556        while item: # loop over (new) phases
1557            iph += 1
1558            phaseName = self.PatternTree.GetItemText(item)
1559            data = self.PatternTree.GetItemPyData(item)
1560            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1561            if iph not in result: continue
1562            generalData = data['General']
1563            SGData = generalData['SGData']
1564            UseList = data['Histograms']
1565            NShkl = len(G2spc.MustrainNames(SGData))
1566            NDij = len(G2spc.HStrainNames(SGData))
1567            for histoName in newHistList:
1568                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1569                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1570                refList = self.PatternTree.GetItemPyData(
1571                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1572                refList[generalData['Name']] = []
1573        return # success
1574
1575    def OnDummyPowder(self,event):
1576        '''Called in response to Import/Powder Data/Simulate menu item
1577        to create a Dummy powder diffraction data set.
1578
1579        Reads an instrument parameter file and then gets input from the user
1580        '''
1581        # get a list of existing histograms
1582        PWDRlist = []
1583        if self.PatternTree.GetCount():
1584            item, cookie = self.PatternTree.GetFirstChild(self.root)
1585            while item:
1586                name = self.PatternTree.GetItemText(item)
1587                if name.startswith('PWDR ') and name not in PWDRlist:
1588                    PWDRlist.append(name)
1589                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1590        # Initialize a base class reader
1591        rd = G2IO.ImportPowderData(
1592            extensionlist=tuple(),
1593            strictExtension=False,
1594            formatName = 'Simulate dataset',
1595            longFormatName = 'Compute a simulated pattern')
1596        rd.powderentry[0] = '' # no filename
1597        # #self.powderentry[1] = pos # bank offset (N/A here)
1598        rd.powderentry[2] = 1 # only one bank
1599        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1600        self.CheckNotebook()
1601        Iparm = None
1602        lastIparmfile = ''
1603        lastdatafile = ''
1604        self.zipfile = None
1605        # get instrument parameters for it
1606        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1607        if 'T' in Iparm1['Type'][0]:
1608            print('TOF simulation not supported yet')
1609            return False
1610        else:
1611            # need to get name, 2theta start, end, step
1612            rd.idstring = ' CW'
1613            if 'X' in Iparm1['Type'][0]:
1614                rd.idstring = 'CW x-ray simulation'
1615            else:
1616                rd.idstring = 'CW neutron simulation'
1617            # base initial range on wavelength
1618            wave = Iparm1.get('Lam')
1619            if wave:
1620                wave = wave[0]
1621            else:
1622                wave = Iparm1.get('Lam1')
1623                if wave:
1624                    wave = wave[0]
1625        N = 0
1626        while (N < 3): # insist on a dataset with a few points
1627            names = ('dataset name', 'start angle', 'end angle', 'step size')
1628            if not wave or wave < 1.0:
1629                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1630            else:
1631                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1632            dlg = G2G.ScrolledMultiEditor(
1633                self,[inp] * len(inp),range(len(inp)),names,
1634                header='Enter simulation name and range',
1635                minvals=(None,0.001,0.001,0.0001),
1636                maxvals=(None,180.,180.,.1),
1637                sizevals=((225,-1),)
1638                )
1639            dlg.CenterOnParent()
1640            if dlg.ShowModal() == wx.ID_OK:
1641                if inp[1] > inp[2]:
1642                    end,start,step = inp[1:]
1643                else:               
1644                    start,end,step = inp[1:]
1645                step = abs(step)
1646            else:
1647                return False
1648            N = int((end-start)/step)+1
1649            x = np.linspace(start,end,N,True)
1650            N = len(x)
1651        rd.powderdata = [
1652            np.array(x), # x-axis values
1653            np.zeros_like(x), # powder pattern intensities
1654            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1655            np.zeros_like(x), # calc. intensities (zero)
1656            np.zeros_like(x), # calc. background (zero)
1657            np.zeros_like(x), # obs-calc profiles
1658            ]
1659        Tmin = rd.powderdata[0][0]
1660        Tmax = rd.powderdata[0][-1]
1661        # data are read, now store them in the tree
1662        HistName = inp[0]
1663        HistName = 'PWDR '+HistName
1664        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1665        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1666        valuesdict = {
1667            'wtFactor':1.0,
1668            'Dummy':True,
1669            'ranId':ran.randint(0,sys.maxint),
1670            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1671            'qPlot':False,'dPlot':False,'sqrtPlot':False
1672            }
1673        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1674        self.PatternTree.SetItemPyData(
1675            self.PatternTree.AppendItem(Id,text='Comments'),
1676            rd.comments)
1677        self.PatternTree.SetItemPyData(
1678            self.PatternTree.AppendItem(Id,text='Limits'),
1679            [(Tmin,Tmax),[Tmin,Tmax]])
1680        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1681        self.PatternTree.SetItemPyData(
1682            self.PatternTree.AppendItem(Id,text='Background'),
1683            [['chebyschev',True,3,1.0,0.0,0.0],
1684             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1685        self.PatternTree.SetItemPyData(
1686            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1687            [Iparm1,Iparm2])
1688        self.PatternTree.SetItemPyData(
1689            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1690            rd.Sample)
1691        self.PatternTree.SetItemPyData(
1692            self.PatternTree.AppendItem(Id,text='Peak List')
1693            ,{'peaks':[],'sigDict':{}})
1694        self.PatternTree.SetItemPyData(
1695            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1696            [[],[]])
1697        self.PatternTree.SetItemPyData(
1698            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1699            [])
1700        self.PatternTree.SetItemPyData(
1701            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1702            {})
1703        self.PatternTree.Expand(Id)
1704        self.PatternTree.SelectItem(Id)
1705        print('Added simulation powder data '+str(HistName)+ 
1706              ' with parameters from '+str(rd.instmsg))
1707
1708        # make a list of phase names
1709        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1710        phaseNameList = usedHistograms.keys() # phase names in use
1711        if not phaseNameList: return # no phases yet, nothing to do
1712        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1713        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1714        if not result: return
1715        # connect new phases to histograms
1716        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1717        if not sub:
1718            raise Exception('ERROR -- why are there no phases here?')
1719        item, cookie = self.PatternTree.GetFirstChild(sub)
1720        iph = -1
1721        while item: # loop over (new) phases
1722            iph += 1
1723            phaseName = self.PatternTree.GetItemText(item)
1724            data = self.PatternTree.GetItemPyData(item)
1725            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1726            if iph not in result: continue
1727            generalData = data['General']
1728            SGData = generalData['SGData']
1729            UseList = data['Histograms']
1730            NShkl = len(G2spc.MustrainNames(SGData))
1731            NDij = len(G2spc.HStrainNames(SGData))
1732            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
1733            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1734            refList = self.PatternTree.GetItemPyData(
1735                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1736            refList[generalData['Name']] = []
1737        return # success
1738       
1739    def OnPreferences(self,event):
1740        'Edit the GSAS-II configuration variables'
1741        dlg = G2G.SelectConfigSetting(self)
1742        dlg.ShowModal() == wx.ID_OK
1743        dlg.Destroy()
1744
1745    def _Add_ImportMenu_smallangle(self,parent):
1746        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1747        '''
1748        submenu = wx.Menu()
1749        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1750            submenu, help='Import small angle data')
1751        for reader in self.ImportSmallAngleReaderlist:
1752            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1753                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1754            self.ImportMenuId[item.GetId()] = reader
1755            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1756        # item = submenu.Append(wx.ID_ANY,
1757        #     help='Import small angle data, use file to try to determine format',
1758        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1759        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1760
1761    def OnImportSmallAngle(self,event):
1762        '''Called in response to an Import/Small Angle Data/... menu item
1763        to read a small angle diffraction data set.
1764        dict self.ImportMenuId is used to look up the specific
1765        reader item associated with the menu item, which will be
1766        None for the last menu item, which is the "guess" option
1767        where all appropriate formats will be tried.
1768
1769        '''
1770       
1771        def GetSASDIparm(reader):
1772            parm = reader.instdict
1773            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1774                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1775            return Iparm,{}
1776           
1777        # get a list of existing histograms
1778        SASDlist = []
1779        if self.PatternTree.GetCount():
1780            item, cookie = self.PatternTree.GetFirstChild(self.root)
1781            while item:
1782                name = self.PatternTree.GetItemText(item)
1783                if name.startswith('SASD ') and name not in SASDlist:
1784                    SASDlist.append(name)
1785                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1786        # look up which format was requested
1787        reqrdr = self.ImportMenuId.get(event.GetId()) 
1788        rdlist = self.OnImportGeneric(
1789            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1790        if len(rdlist) == 0: return
1791        self.CheckNotebook()
1792        Iparm = None
1793        lastdatafile = ''
1794        newHistList = []
1795        self.EnablePlot = False
1796        for rd in rdlist:
1797            lastdatafile = rd.smallangleentry[0]
1798            HistName = rd.idstring
1799            HistName = 'SASD '+HistName
1800            # make new histogram names unique
1801            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1802            print 'Read small angle data '+str(HistName)+ \
1803                ' from file '+str(self.lastimport)
1804            # data are read, now store them in the tree
1805            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1806            Iparm1,Iparm2 = GetSASDIparm(rd)
1807#            if 'T' in Iparm1['Type'][0]:
1808#                if not rd.clockWd and rd.GSAS:
1809#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1810#                cw = np.diff(rd.powderdata[0])
1811#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1812#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1813#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1814#                if 'Itype' in Iparm2:
1815#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1816#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1817#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1818#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1819#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1820#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1821#                    var += WYI*rd.powderdata[1]**2
1822#                    var /= YI**2
1823#                    rd.powderdata[2] = 1./var
1824#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1825#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1826#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1827            Tmin = min(rd.smallangledata[0])
1828            Tmax = max(rd.smallangledata[0])
1829            valuesdict = {
1830                'wtFactor':1.0,
1831                'Dummy':False,
1832                'ranId':ran.randint(0,sys.maxint),
1833                }
1834            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1835            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1836            self.PatternTree.SetItemPyData(
1837                self.PatternTree.AppendItem(Id,text='Comments'),
1838                rd.comments)
1839            self.PatternTree.SetItemPyData(
1840                self.PatternTree.AppendItem(Id,text='Limits'),
1841                [(Tmin,Tmax),[Tmin,Tmax]])
1842            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1843            self.PatternTree.SetItemPyData(
1844                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1845                [Iparm1,Iparm2])
1846            self.PatternTree.SetItemPyData(
1847                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1848            self.PatternTree.SetItemPyData(
1849                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1850                rd.Sample)
1851            self.PatternTree.SetItemPyData(
1852                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1853            newHistList.append(HistName)
1854        else:
1855            self.EnablePlot = True
1856            self.PatternTree.Expand(Id)
1857            self.PatternTree.SelectItem(Id)
1858           
1859        if not newHistList: return # somehow, no new histograms
1860        return # success
1861
1862    def OnMacroRecordStatus(self,event,setvalue=None):
1863        '''Called when the record macro menu item is used which toggles the
1864        value. Alternately a value to be set can be provided. Note that this
1865        routine is made more complex because on the Mac there are lots of menu
1866        items (listed in self.MacroStatusList) and this loops over all of them.
1867        '''
1868        nextvalue = log.ShowLogStatus() != True
1869        if setvalue is not None:
1870            nextvalue = setvalue
1871        if nextvalue:
1872            log.LogOn()
1873            set2 = True
1874        else:
1875            log.LogOff()
1876            set2 = False
1877        for menuitem in self.MacroStatusList:
1878            menuitem.Check(set2)
1879
1880    def _init_Macro(self):
1881        '''Define the items in the macro menu.
1882        '''
1883        menu = self.MacroMenu
1884        item = menu.Append(
1885                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
1886                kind=wx.ITEM_CHECK,text='Record actions')
1887        self.MacroStatusList.append(item)
1888        item.Check(log.ShowLogStatus())
1889        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
1890
1891        # this may only be of value for development work
1892        item = menu.Append(
1893            help='Show logged commands', id=wx.ID_ANY,
1894            kind=wx.ITEM_NORMAL,text='Show log')
1895        def OnShowLog(event):
1896            print 70*'='
1897            print 'List of logged actions'
1898            for i,line in enumerate(log.G2logList):
1899                if line: print i,line
1900            print 70*'='
1901        self.Bind(wx.EVT_MENU, OnShowLog, item)
1902
1903        item = menu.Append(
1904            help='Clear logged commands', id=wx.ID_ANY,
1905            kind=wx.ITEM_NORMAL,text='Clear log')
1906        def OnClearLog(event): log.G2logList=[None]
1907        self.Bind(wx.EVT_MENU, OnClearLog, item)
1908       
1909        item = menu.Append(
1910            help='Save logged commands to file', id=wx.ID_ANY,
1911            kind=wx.ITEM_NORMAL,text='Save log')
1912        def OnSaveLog(event):
1913            import cPickle
1914            defnam = os.path.splitext(
1915                os.path.split(self.GSASprojectfile)[1]
1916                )[0]+'.gcmd'
1917            dlg = wx.FileDialog(self,
1918                'Choose an file to save past actions', '.', defnam, 
1919                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1920                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1921            dlg.CenterOnParent()
1922            try:
1923                if dlg.ShowModal() == wx.ID_OK:
1924                    filename = dlg.GetPath()
1925                    # make sure extension is correct
1926                    filename = os.path.splitext(filename)[0]+'.gcmd'
1927                else:
1928                    filename = None
1929            finally:
1930                dlg.Destroy()
1931            if filename:
1932                fp = open(filename,'wb')
1933                fp.write(str(len(log.G2logList)-1)+'\n')
1934                for item in log.G2logList:
1935                    if item: cPickle.dump(item,fp)
1936                fp.close()
1937        self.Bind(wx.EVT_MENU, OnSaveLog, item)
1938
1939        item = menu.Append(
1940            help='Load logged commands from file', id=wx.ID_ANY,
1941            kind=wx.ITEM_NORMAL,text='Load log')
1942        def OnLoadLog(event):
1943            # this appends. Perhaps we should ask to clear?
1944            import cPickle
1945            defnam = os.path.splitext(
1946                os.path.split(self.GSASprojectfile)[1]
1947                )[0]+'.gcmd'
1948            dlg = wx.FileDialog(self,
1949                'Choose an file to read saved actions', '.', defnam, 
1950                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1951                wx.OPEN)
1952            dlg.CenterOnParent()
1953            try:
1954                if dlg.ShowModal() == wx.ID_OK:
1955                    filename = dlg.GetPath()
1956                    # make sure extension is correct
1957                    filename = os.path.splitext(filename)[0]+'.gcmd'
1958                else:
1959                    filename = None
1960            finally:
1961                dlg.Destroy()
1962            if filename and os.path.exists(filename):
1963                fp = open(filename,'rb')
1964                lines = fp.readline()
1965                for i in range(int(lines)):
1966                    log.G2logList.append(cPickle.load(fp))
1967                fp.close()
1968        self.Bind(wx.EVT_MENU, OnLoadLog, item)
1969
1970        item = menu.Append(
1971            help='Replay saved commands', id=wx.ID_ANY,
1972            kind=wx.ITEM_NORMAL,text='Replay log')
1973        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
1974
1975    def _init_Exports(self,menu):
1976        '''Find exporter routines and add them into menus
1977        '''
1978        # set up the top-level menus
1979        projectmenu = wx.Menu()
1980        item = menu.AppendMenu(
1981            wx.ID_ANY, 'Entire project as',
1982            projectmenu, help='Export entire project')
1983
1984        phasemenu = wx.Menu()
1985        item = menu.AppendMenu(
1986            wx.ID_ANY, 'Phase as',
1987            phasemenu, help='Export phase or sometimes phases')
1988
1989        powdermenu = wx.Menu()
1990        item = menu.AppendMenu(
1991            wx.ID_ANY, 'Powder data as',
1992            powdermenu, help='Export powder diffraction histogram(s)')
1993
1994        singlemenu = wx.Menu()
1995        item = menu.AppendMenu(
1996            wx.ID_ANY, 'Single crystal data as',
1997            singlemenu, help='Export single crystal histogram(s)')
1998
1999        imagemenu = wx.Menu()
2000        item = menu.AppendMenu(
2001            wx.ID_ANY, 'Image data as',
2002            imagemenu, help='Export powder image(s) data')
2003
2004        mapmenu = wx.Menu()
2005        item = menu.AppendMenu(
2006            wx.ID_ANY, 'Maps as',
2007            mapmenu, help='Export density map(s)')
2008
2009        # pdfmenu = wx.Menu()
2010        # item = menu.AppendMenu(
2011        #     wx.ID_ANY, 'PDFs as',
2012        #     pdfmenu, help='Export pair distribution function(s)')
2013
2014        # find all the exporter files
2015        pathlist = sys.path
2016        filelist = []
2017        for path in pathlist:
2018            for filename in glob.iglob(os.path.join(path,"G2export*.py")):
2019                filelist.append(filename)   
2020        filelist = sorted(list(set(filelist))) # remove duplicates
2021        self.exporterlist = []
2022        # go through the routines and import them, saving objects that
2023        # have export routines (method Exporter)
2024        for filename in filelist:
2025            path,rootname = os.path.split(filename)
2026            pkg = os.path.splitext(rootname)[0]
2027            try:
2028                fp = None
2029                fp, fppath,desc = imp.find_module(pkg,[path,])
2030                pkg = imp.load_module(pkg,fp,fppath,desc)
2031                for clss in inspect.getmembers(pkg): # find classes defined in package
2032                    if clss[0].startswith('_'): continue
2033                    if inspect.isclass(clss[1]):
2034                        # check if we have the required methods
2035                        for m in 'Exporter','loadParmDict':
2036                            if not hasattr(clss[1],m): break
2037                            if not callable(getattr(clss[1],m)): break
2038                        else:
2039                            exporter = clss[1](self) # create an export instance
2040                            self.exporterlist.append(exporter)
2041            except AttributeError:
2042                print 'Import_'+errprefix+': Attribute Error'+str(filename)
2043                pass
2044            except ImportError:
2045                print 'Import_'+errprefix+': Error importing file'+str(filename)
2046                pass
2047            if fp: fp.close()
2048        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
2049        for obj in self.exporterlist:
2050            #print 'exporter',obj
2051            for typ in obj.exporttype:
2052                if typ == "project":
2053                    submenu = projectmenu
2054                elif typ == "phase":
2055                    submenu = phasemenu
2056                elif typ == "powder":
2057                    submenu = powdermenu
2058                elif typ == "single":
2059                    submenu = singlemenu
2060                elif typ == "image":
2061                    submenu = imagemenu
2062                elif typ == "map":
2063                    submenu = mapmenu
2064                # elif typ == "pdf":
2065                #     submenu = pdfmenu
2066                else:
2067                    print("Error, unknown type in "+str(obj))
2068                    break
2069                item = submenu.Append(
2070                    wx.ID_ANY,
2071                    help=obj.longFormatName,
2072                    kind=wx.ITEM_NORMAL,
2073                    text=obj.formatName)
2074                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
2075                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
2076        item = imagemenu.Append(wx.ID_ANY,
2077                        help='Export image controls and masks for multiple images',
2078                        kind=wx.ITEM_NORMAL,
2079                        text='Multiple image controls and masks')
2080        self.Bind(wx.EVT_MENU, self.OnSaveMultipleImg, id=item.GetId())
2081        #code to debug an Exporter. hard-code the routine below, to allow a reload before use
2082        # def DebugExport(event):
2083        #      print 'start reload'
2084        #      reload(G2IO)
2085        #      import G2export_pwdr as dev
2086        #      reload(dev)
2087        #      dev.ExportPowderFXYE(self).Exporter(event)
2088        # item = menu.Append(
2089        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2090        #     help="debug exporter",text="test Export FXYE")
2091        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
2092        # # #self.ExportLookup[item.GetId()] = 'image'
2093        # self.ExportLookup[item.GetId()] = 'powder'
2094
2095    def _Add_ExportMenuItems(self,parent):
2096        # item = parent.Append(
2097        #     help='Select PWDR item to enable',id=wx.ID_ANY,
2098        #     kind=wx.ITEM_NORMAL,
2099        #     text='Export Powder Patterns...')
2100        # self.ExportPattern.append(item)
2101        # item.Enable(False)
2102        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
2103
2104        item = parent.Append(
2105            help='',id=wx.ID_ANY,
2106            kind=wx.ITEM_NORMAL,
2107            text='Export All Peak Lists...')
2108        self.ExportPeakList.append(item)
2109        item.Enable(True)
2110        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
2111
2112        item = parent.Append(
2113            help='',id=wx.ID_ANY,
2114            kind=wx.ITEM_NORMAL,
2115            text='Export HKLs...')
2116        self.ExportHKL.append(item)
2117        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
2118
2119        item = parent.Append(
2120            help='Select PDF item to enable',
2121            id=wx.ID_ANY,
2122            kind=wx.ITEM_NORMAL,
2123            text='Export PDF...')
2124        self.ExportPDF.append(item)
2125        item.Enable(False)
2126        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
2127
2128    def FillMainMenu(self,menubar):
2129        '''Define contents of the main GSAS-II menu for the (main) data tree window.
2130        For the mac, this is also called for the data item windows as well so that
2131        the main menu items are data menu as well.
2132        '''
2133        File = wx.Menu(title='')
2134        menubar.Append(menu=File, title='&File')
2135        self._Add_FileMenuItems(File)
2136        Data = wx.Menu(title='')
2137        menubar.Append(menu=Data, title='Data')
2138        self._Add_DataMenuItems(Data)
2139        Calculate = wx.Menu(title='')       
2140        menubar.Append(menu=Calculate, title='&Calculate')
2141        self._Add_CalculateMenuItems(Calculate)
2142        Import = wx.Menu(title='')       
2143        menubar.Append(menu=Import, title='Import')
2144        self._Add_ImportMenu_Image(Import)
2145        self._Add_ImportMenu_Phase(Import)
2146        self._Add_ImportMenu_powder(Import)
2147        self._Add_ImportMenu_Sfact(Import)
2148        self._Add_ImportMenu_smallangle(Import)
2149
2150        #======================================================================
2151        # Code to help develop/debug an importer, much is hard-coded below
2152        # but module is reloaded before each use, allowing faster testing
2153        # def DebugImport(event):
2154        #     print 'start reload'
2155        #     import G2phase_ISO as dev
2156        #     reload(dev)
2157        #     rd = dev.ISODISTORTPhaseReader()
2158        #     self.ImportMenuId[event.GetId()] = rd
2159        #     self.OnImportPhase(event)
2160            # or ----------------------------------------------------------------------
2161            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
2162            # special debug code
2163            # or ----------------------------------------------------------------------
2164            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
2165            # fp = open(filename,'Ur')
2166            # if not rd.ContentsValidator(fp):
2167            #     print 'not validated'
2168            #     # make a list of used phase ranId's
2169            # phaseRIdList = []
2170            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2171            # if sub:
2172            #     item, cookie = self.PatternTree.GetFirstChild(sub)
2173            #     while item:
2174            #         phaseName = self.PatternTree.GetItemText(item)
2175            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2176            #         if ranId: phaseRIdList.append(ranId)
2177            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2178            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2179            #     print 'read OK'
2180        # item = Import.Append(
2181        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2182        #     help="debug importer",text="test importer")
2183        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2184        #======================================================================
2185        self.ExportMenu = wx.Menu(title='')
2186        menubar.Append(menu=self.ExportMenu, title='Export')
2187        self._init_Exports(self.ExportMenu)
2188        self._Add_ExportMenuItems(self.ExportMenu)
2189        if GSASIIpath.GetConfigValue('Enable_logging'):
2190            self.MacroMenu = wx.Menu(title='')
2191            menubar.Append(menu=self.MacroMenu, title='Macro')
2192            self._init_Macro()
2193        HelpMenu=G2G.MyHelp(self,helpType='Data tree',
2194            morehelpitems=[
2195                           ('&Tutorials','Tutorials'), 
2196                           ])
2197        menubar.Append(menu=HelpMenu,title='&Help')
2198           
2199    def _init_ctrls(self, parent):
2200        wx.Frame.__init__(self, name='GSASII', parent=parent,
2201            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2202        clientSize = wx.ClientDisplayRect()
2203        Size = self.GetSize()
2204        xPos = clientSize[2]-Size[0]
2205        self.SetPosition(wx.Point(xPos,clientSize[1]))
2206        self._init_Imports()
2207        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2208        self.MakePDF = []
2209        self.Refine = []
2210        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2211        #self.ExportPattern = []
2212        self.ExportPeakList = []
2213        self.ExportHKL = []
2214        self.ExportPDF = []
2215        self.ExportPhase = []
2216        self.ExportCIF = []
2217        #
2218        self.GSASIIMenu = wx.MenuBar()
2219        # create a list of all dataframe menus (appended in PrefillDataMenu)
2220        self.dataMenuBars = [self.GSASIIMenu]
2221        self.MacroStatusList = []
2222        self.FillMainMenu(self.GSASIIMenu)
2223        self.SetMenuBar(self.GSASIIMenu)
2224        self.Bind(wx.EVT_SIZE, self.OnSize)
2225        self.Status = self.CreateStatusBar()
2226        self.mainPanel = wx.Panel(self,-1)
2227       
2228        wxID_PATTERNTREE = wx.NewId()
2229        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2230        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2231            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2232        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnPatternTreeSelChanged)
2233        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2234            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2235        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2236            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2237        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2238            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2239        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2240            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2241        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2242            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2243        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2244            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2245        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2246        self.root = self.PatternTree.root
2247        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2248            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2249        #self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
2250        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame,G2frame=self)
2251        plotFrame.Show()
2252       
2253        self.dataDisplay = None
2254       
2255    def __init__(self, parent):
2256        self.ExportLookup = {}
2257        self._init_ctrls(parent)
2258        self.Image = wx.Image(
2259            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2260            wx.BITMAP_TYPE_ICO)
2261        if "wxMSW" in wx.PlatformInfo:
2262            img = self.Image.Scale(16, 16).ConvertToBitmap()
2263        elif "wxGTK" in wx.PlatformInfo:
2264            img = self.Image.Scale(22, 22).ConvertToBitmap()
2265        else:
2266            img = self.Image.ConvertToBitmap()
2267        self.SetIcon(wx.IconFromBitmap(img))
2268        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2269        # various defaults
2270        self.oldFocus = None
2271        self.GSASprojectfile = ''
2272        self.undofile = ''
2273        self.TreeItemDelete = False
2274        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2275        self.Weight = False
2276        self.IfPlot = False
2277        self.DDShowAll = False
2278        self.atmSel = ''
2279        self.PatternId = 0
2280        self.PickId = 0
2281        self.PickIdText = None
2282        self.PeakTable = []
2283        self.LimitsTable = []
2284        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2285        self.HKL = []
2286        self.Lines = []
2287        self.itemPicked = None
2288        self.dataFrame = None
2289        self.Interpolate = 'nearest'
2290        self.ContourColor = 'Paired'
2291        self.VcovColor = 'RdYlGn'
2292        self.RamaColor = 'Blues'
2293        self.Projection = 'equal area'
2294        self.logPlot = False
2295        self.plusPlot = True
2296        self.sqPlot = False
2297        self.ErrorBars = False
2298        self.Contour = False
2299        self.Legend = False
2300        self.SinglePlot = True
2301        self.SubBack = False
2302        self.seqReverse = False
2303        self.seqLines = True #draw lines between points
2304        self.plotView = 0
2305        self.Image = 0
2306        self.oldImagefile = '' # the name of the last image file read
2307        self.oldImageTag = None # the name of the tag for multi-image files
2308        self.ImageZ = []
2309        self.Integrate = 0
2310        self.imageDefault = {}
2311        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2312        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2313        self.autoIntFrame = None
2314        self.IntegratedList = [] # list of already integrated IMG tree items
2315        self.Sngl = False
2316        self.ifGetRing = False
2317        self.MaskKey = ''           #trigger for making image masks
2318        self.StrainKey = ''         #ditto for new strain d-zeros
2319        self.EnablePlot = True
2320        self.hist = ''              # selected histogram in Phase/Data tab
2321        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
2322        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2323        self.LastImportDir = None # last-used directory where an import was done
2324        self.LastGPXdir = None    # directory where a GPX file was last read
2325        self.LastExportDir = None  # the last directory used for exports, if any.
2326        self.dataDisplayPhaseText = ''
2327       
2328        arg = sys.argv
2329        if len(arg) > 1 and arg[1]:
2330            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2331            self.dirname = os.path.dirname(arg[1])
2332            if self.dirname: os.chdir(self.dirname)
2333            try:
2334                self.StartProject()         #open the file if possible
2335                return
2336            except Exception:
2337                print 'Error opening or reading file',arg[1]
2338                import traceback
2339                print traceback.format_exc()
2340               
2341        if GSASIIpath.GetConfigValue('Starting_directory'):
2342            try:
2343                pth = GSASIIpath.GetConfigValue('Starting_directory')
2344                pth = os.path.expanduser(pth) 
2345                os.chdir(pth)
2346                self.LastGPXdir = pth
2347            except:
2348                print('Ignoring Config Starting_directory value: '+
2349                      GSASIIpath.GetConfigValue('Starting_directory'))
2350
2351    def GetTreeItemsList(self,item):
2352        return self.PatternTree._getTreeItemsList(item)
2353
2354    def OnSize(self,event):
2355        'Called to make PatternTree fill mainPanel'
2356        w,h = self.GetClientSizeTuple()
2357        self.mainPanel.SetSize(wx.Size(w,h))
2358        self.PatternTree.SetSize(wx.Size(w,h))
2359                       
2360    def OnPatternTreeSelChanged(self, event):
2361        '''Called when a data tree item is selected'''
2362        if self.TreeItemDelete:
2363            self.TreeItemDelete = False
2364        else:
2365            pltNum = self.G2plotNB.nb.GetSelection()
2366            if pltNum >= 0:                         #to avoid the startup with no plot!
2367                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2368                pltPlot = pltPage.figure
2369            item = event.GetItem()
2370            G2gd.MovePatternTreeToGrid(self,item)
2371            if self.oldFocus:
2372                self.oldFocus.SetFocus()
2373       
2374    def OnPatternTreeItemCollapsed(self, event):
2375        'Called when a tree item is collapsed - all children will be collapsed'
2376        self.PatternTree.CollapseAllChildren(event.GetItem())
2377
2378    def OnPatternTreeItemExpanded(self, event):
2379        'Called when a tree item is expanded'
2380        self.OnPatternTreeSelChanged(event)
2381        event.Skip()
2382       
2383    def OnPatternTreeItemDelete(self, event):
2384        'Called when a tree item is deleted -- not sure what this does'
2385        self.TreeItemDelete = True
2386
2387    def OnPatternTreeItemActivated(self, event):
2388        'Called when a tree item is activated'
2389        event.Skip()
2390       
2391    def OnPatternTreeBeginRDrag(self,event):
2392        event.Allow()
2393        self.BeginDragId = event.GetItem()
2394        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2395        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2396        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2397        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2398        while item:     #G2 data tree has no sub children under a child of a tree item
2399            name = self.PatternTree.GetItemText(item)
2400            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2401            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2402       
2403    def OnPatternTreeEndDrag(self,event):
2404        event.Allow()
2405        self.EndDragId = event.GetItem()
2406        try:
2407            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2408        except:
2409            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2410            NewParent = self.root
2411        if self.ParentId != NewParent:
2412            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2413        else:
2414            Name,Item = self.DragData[0]
2415            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2416            self.PatternTree.SetItemPyData(NewId,Item)
2417            for name,item in self.DragData[1:]:     #loop over children
2418                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2419                self.PatternTree.SetItemPyData(Id,item)
2420            self.PatternTree.Delete(self.BeginDragId)
2421            G2gd.MovePatternTreeToGrid(self,NewId)
2422       
2423    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2424        'Allows stepping through the tree with the up/down arrow keys'
2425        self.oldFocus = wx.Window.FindFocus()
2426        keyevt = event.GetKeyEvent()
2427        key = event.GetKeyCode()
2428        item = self.PatternTree.GetSelection()
2429        if type(item) is int: return # is this the toplevel in tree?
2430        name = self.PatternTree.GetItemText(item)
2431        parent = self.PatternTree.GetItemParent(item)
2432        if key == wx.WXK_UP:
2433            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2434                if type(parent) is int: return # is this the toplevel in tree?
2435                prev = self.PatternTree.GetPrevSibling(parent)
2436                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2437                if NewId:
2438                    self.PatternTree.Collapse(parent)
2439                    self.PatternTree.Expand(prev)
2440                    self.oldFocus = wx.Window.FindFocus()
2441                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2442                else:
2443                    wx.CallAfter(self.PatternTree.SelectItem,item)
2444            else:   
2445                self.PatternTree.GetPrevSibling(item)
2446                self.PatternTree.SelectItem(item)
2447        elif key == wx.WXK_DOWN:
2448            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2449                next = self.PatternTree.GetNextSibling(parent)
2450                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2451                if NewId:
2452                    self.PatternTree.Collapse(parent)
2453                    self.PatternTree.Expand(next)
2454                    self.oldFocus = wx.Window.FindFocus()
2455                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2456                else:
2457                    wx.CallAfter(self.PatternTree.SelectItem,item)
2458            else:   
2459                self.PatternTree.GetNextSibling(item)
2460                self.PatternTree.SelectItem(item)
2461               
2462    def OnReadPowderPeaks(self,event):
2463        'Bound to menu Data/Read Powder Peaks'
2464        Cuka = 1.54052
2465        self.CheckNotebook()
2466        pth = G2G.GetImportPath(self)
2467        if not pth: pth = '.'
2468        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2469            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2470        try:
2471            if dlg.ShowModal() == wx.ID_OK:
2472                self.HKL = []
2473                self.powderfile = dlg.GetPath()
2474                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2475                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2476                data = ['PKS',wave,0.0]
2477                names = ['Type','Lam','Zero'] 
2478                codes = [0,0,0]
2479                inst = [G2IO.makeInstDict(names,data,codes),{}]
2480                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2481                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2482                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2483                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2484                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2485                self.PatternTree.Expand(Id)
2486                self.PatternTree.SelectItem(Id)
2487                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2488        finally:
2489            dlg.Destroy()
2490                       
2491    def OnImageRead(self,event):
2492        '''Called to read in an image in any known format. *** Depreciated. ***
2493        '''
2494        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2495
2496    def CheckNotebook(self):
2497        '''Make sure the data tree has the minimally expected controls.
2498        '''
2499        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2500            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2501            self.PatternTree.SetItemPyData(sub,[''])
2502        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2503            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2504            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2505        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2506            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2507            self.PatternTree.SetItemPyData(sub,{})
2508        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2509            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2510            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2511        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2512            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2513            self.PatternTree.SetItemPyData(sub,{})
2514        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2515            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2516            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2517                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2518               
2519    class CopyDialog(wx.Dialog):
2520        '''Creates a dialog for copying control settings between
2521        data tree items'''
2522        def __init__(self,parent,title,text,data):
2523            wx.Dialog.__init__(self,parent,-1,title, 
2524                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2525            self.data = data
2526            panel = wx.Panel(self)
2527            mainSizer = wx.BoxSizer(wx.VERTICAL)
2528            topLabl = wx.StaticText(panel,-1,text)
2529            mainSizer.Add((10,10),1)
2530            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2531            mainSizer.Add((10,10),1)
2532            ncols = len(data)/40+1
2533            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2534            for id,item in enumerate(self.data):
2535                ckbox = wx.CheckBox(panel,id,item[1])
2536                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2537                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2538            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2539            OkBtn = wx.Button(panel,-1,"Ok")
2540            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2541            cancelBtn = wx.Button(panel,-1,"Cancel")
2542            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2543            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2544            btnSizer.Add((20,20),1)
2545            btnSizer.Add(OkBtn)
2546            btnSizer.Add((20,20),1)
2547            btnSizer.Add(cancelBtn)
2548            btnSizer.Add((20,20),1)
2549           
2550            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2551            panel.SetSizer(mainSizer)
2552            panel.Fit()
2553            self.Fit()
2554       
2555        def OnCopyChange(self,event):
2556            id = event.GetId()
2557            self.data[id][0] = self.FindWindowById(id).GetValue()       
2558           
2559        def OnOk(self,event):
2560            parent = self.GetParent()
2561            parent.Raise()
2562            self.EndModal(wx.ID_OK)             
2563           
2564        def OnCancel(self,event):
2565            parent = self.GetParent()
2566            parent.Raise()
2567            self.EndModal(wx.ID_CANCEL)             
2568           
2569        def GetData(self):
2570            return self.data
2571       
2572    class SumDialog(wx.Dialog):
2573        'Allows user to supply scale factor(s) when summing data'
2574        def __init__(self,parent,title,text,dataType,data):
2575            wx.Dialog.__init__(self,parent,-1,title, 
2576                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2577            self.data = data
2578            panel = wx.Panel(self)
2579            mainSizer = wx.BoxSizer(wx.VERTICAL)
2580            topLabl = wx.StaticText(panel,-1,text)
2581            mainSizer.Add((10,10),1)
2582            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2583            mainSizer.Add((10,10),1)
2584            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2585            for id,item in enumerate(self.data[:-1]):
2586                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2587                name.SetEditable(False)
2588                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2589                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2590                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2591                dataGridSizer.Add(scale,0,wx.LEFT,10)
2592                dataGridSizer.Add(name,0,wx.RIGHT,10)
2593            if dataType:
2594                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2595                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2596                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2597                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2598                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2599                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2600            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2601            OkBtn = wx.Button(panel,-1,"Ok")
2602            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2603            cancelBtn = wx.Button(panel,-1,"Cancel")
2604            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2605            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2606            btnSizer.Add((20,20),1)
2607            btnSizer.Add(OkBtn)
2608            btnSizer.Add((20,20),1)
2609            btnSizer.Add(cancelBtn)
2610            btnSizer.Add((20,20),1)
2611           
2612            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2613            panel.SetSizer(mainSizer)
2614            panel.Fit()
2615            self.Fit()
2616
2617        def OnScaleChange(self,event):
2618            id = event.GetId()
2619            value = self.FindWindowById(id).GetValue()
2620            try:
2621                self.data[id][0] = float(value)
2622                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2623            except ValueError:
2624                if value and '-' not in value[0]:
2625                    print 'bad input - numbers only'
2626                    self.FindWindowById(id).SetValue('0.000')
2627           
2628        def OnNameChange(self,event):
2629            self.data[-1] = self.name.GetValue() 
2630           
2631        def OnOk(self,event):
2632            parent = self.GetParent()
2633            parent.Raise()
2634            self.EndModal(wx.ID_OK)             
2635           
2636        def OnCancel(self,event):
2637            parent = self.GetParent()
2638            parent.Raise()
2639            self.EndModal(wx.ID_CANCEL)             
2640           
2641        def GetData(self):
2642            return self.data
2643                       
2644    def OnPwdrSum(self,event):
2645        'Sum together powder data(?)'
2646        TextList = []
2647        DataList = []
2648        SumList = []
2649        Names = []
2650        Inst = None
2651        SumItemList = []
2652        Comments = ['Sum equals: \n']
2653        if self.PatternTree.GetCount():
2654            item, cookie = self.PatternTree.GetFirstChild(self.root)
2655            while item:
2656                name = self.PatternTree.GetItemText(item)
2657                Names.append(name)
2658                if 'PWDR' in name:
2659                    TextList.append([0.0,name])
2660                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2661                    if not Inst:
2662                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2663                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2664            if len(TextList) < 2:
2665                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2666                return
2667            TextList.append('default_sum_name')               
2668            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2669            try:
2670                if dlg.ShowModal() == wx.ID_OK:
2671                    lenX = 0
2672                    Xminmax = [0,0]
2673                    Xsum = []
2674                    Ysum = []
2675                    Vsum = []
2676                    result = dlg.GetData()
2677                    for i,item in enumerate(result[:-1]):
2678                        scale,name = item
2679                        data = DataList[i]
2680                        if scale:
2681                            Comments.append("%10.3f %s" % (scale,' * '+name))
2682                            x,y,w,yc,yb,yd = data   #numpy arrays!
2683                            v = 1./w
2684                            if lenX:
2685                                if lenX != len(x):
2686                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2687                                        '\nExpected:'+str(lenX)+ \
2688                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2689                                    return
2690                            else:
2691                                lenX = len(x)
2692                            if Xminmax[1]:
2693                                if Xminmax != [x[0],x[-1]]:
2694                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2695                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2696                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2697                                    return
2698                                else:
2699                                    for j,yi in enumerate(y):
2700                                         Ysum[j] += scale*yi
2701                                         Vsum[j] += abs(scale)*v[j]
2702                            else:
2703                                Xminmax = [x[0],x[-1]]
2704                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2705                                for j,yi in enumerate(y):
2706                                    Xsum.append(x[j])
2707                                    Ysum.append(scale*yi)
2708                                    Vsum.append(abs(scale*v[j]))
2709                    Wsum = 1./np.array(Vsum)
2710                    outname = 'PWDR '+result[-1]
2711                    Id = 0
2712                    if outname in Names:
2713                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2714                        try:
2715                            if dlg2.ShowModal() == wx.ID_OK:
2716                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2717                                self.PatternTree.Delete(Id)
2718                        finally:
2719                            dlg2.Destroy()
2720                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2721                    if Id:
2722                        Sample = G2pdG.SetDefaultSample()
2723                        valuesdict = {
2724                            'wtFactor':1.0,
2725                            'Dummy':False,
2726                            'ranId':ran.randint(0,sys.maxint),
2727                            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
2728                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2729                            }
2730                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2731                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2732                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2733                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2734                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2735                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2736                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2737                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2738                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2739                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2740                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2741                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2742                        self.PatternTree.SelectItem(Id)
2743                        self.PatternTree.Expand(Id)
2744            finally:
2745                dlg.Destroy()
2746
2747    def OnImageSum(self,event):
2748        'Sum together image data(?)'
2749        TextList = []
2750        DataList = []
2751        SumList = []
2752        Names = []
2753        Inst = []
2754        SumItemList = []
2755        Comments = ['Sum equals: \n']
2756        if self.PatternTree.GetCount():
2757            item, cookie = self.PatternTree.GetFirstChild(self.root)
2758            while item:
2759                name = self.PatternTree.GetItemText(item)
2760                Names.append(name)
2761                if 'IMG' in name:
2762                    TextList.append([0.0,name])
2763                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2764                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2765                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2766            if len(TextList) < 2:
2767                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2768                return
2769            TextList.append('default_sum_name')               
2770            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2771            try:
2772                if dlg.ShowModal() == wx.ID_OK:
2773                    imSize = 0
2774                    result = dlg.GetData()
2775                    First = True
2776                    Found = False
2777                    for i,item in enumerate(result[:-1]):
2778                        scale,name = item
2779                        if scale:
2780                            Found = True                               
2781                            Comments.append("%10.3f %s" % (scale,' * '+name))
2782                            Npix,imagefile,imagetag = DataList[i]
2783                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2784                            if First:
2785                                newImage = np.zeros_like(image)
2786                                First = False
2787                            if imSize:
2788                                if imSize != Npix:
2789                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2790                                        '\nExpected:'+str(imSize)+ \
2791                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2792                                    return
2793                                newImage = newImage+scale*image
2794                            else:
2795                                imSize = Npix
2796                                newImage = newImage+scale*image
2797                            del(image)
2798                    if not Found:
2799                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2800                        return
2801                       
2802                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2803                    outname = 'IMG '+result[-1]
2804                    Id = 0
2805                    if outname in Names:
2806                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2807                        try:
2808                            if dlg2.ShowModal() == wx.ID_OK:
2809                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2810                        finally:
2811                            dlg2.Destroy()
2812                    else:
2813                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2814                    if Id:
2815                        pth = G2G.GetExportPath(self)
2816                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth, '', 
2817                            'G2img files (*.G2img)|*.G2img', 
2818                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
2819                        if dlg.ShowModal() == wx.ID_OK:
2820                            newimagefile = dlg.GetPath()
2821                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2822                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2823                            Imax = np.amax(newImage)
2824                            Imin = np.amin(newImage)
2825                            newImage = []
2826                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2827                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2828                        del(newImage)
2829                        if self.imageDefault:
2830                            Data = copy.copy(self.imageDefault)
2831                        Data['showLines'] = True
2832                        Data['ring'] = []
2833                        Data['rings'] = []
2834                        Data['cutoff'] = 10
2835                        Data['pixLimit'] = 20
2836                        Data['ellipses'] = []
2837                        Data['calibrant'] = ''
2838                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2839                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2840                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2841                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2842                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2843                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2844                        self.PatternTree.SelectItem(Id)
2845                        self.PatternTree.Expand(Id)
2846                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2847                        self.Image = self.PickId
2848            finally:
2849                dlg.Destroy()
2850                     
2851    def OnAddPhase(self,event):
2852        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2853        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2854            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2855        else:
2856            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2857        PhaseName = ''
2858        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2859            style=wx.OK)
2860        if dlg.ShowModal() == wx.ID_OK:
2861            PhaseName = dlg.GetValue()
2862        dlg.Destroy()
2863        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2864        E,SGData = G2spc.SpcGroup('P 1')
2865        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2866        G2gd.MovePatternTreeToGrid(self,sub) #bring up new phase General tab
2867       
2868    def OnDeletePhase(self,event):
2869        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2870        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2871        if self.dataFrame:
2872            self.dataFrame.Clear() 
2873        TextList = []
2874        DelList = []
2875        DelItemList = []
2876        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2877            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2878        else:
2879            return
2880        if sub:
2881            item, cookie = self.PatternTree.GetFirstChild(sub)
2882            while item:
2883                TextList.append(self.PatternTree.GetItemText(item))
2884                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2885            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2886            try:
2887                if dlg.ShowModal() == wx.ID_OK:
2888                    result = dlg.GetSelections()
2889                    for i in result: DelList.append([i,TextList[i]])
2890                    item, cookie = self.PatternTree.GetFirstChild(sub)
2891                    i = 0
2892                    while item:
2893                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2894                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2895                        i += 1
2896                    for item in DelItemList:
2897                        name = self.PatternTree.GetItemText(item)
2898                        self.PatternTree.Delete(item)
2899                        self.G2plotNB.Delete(name)
2900                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2901                    while item:
2902                        name = self.PatternTree.GetItemText(item)
2903                        if 'PWDR' in name:
2904                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2905                            refList = self.PatternTree.GetItemPyData(Id)
2906                            if len(refList):
2907                                for i,item in DelList:
2908                                    if item in refList:
2909                                        del(refList[item])
2910                            self.PatternTree.SetItemPyData(Id,refList)
2911                        elif 'HKLF' in name:
2912                            data = self.PatternTree.GetItemPyData(item)
2913                            data[0] = {}
2914                            self.PatternTree.SetItemPyData(item,data)
2915                           
2916                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2917            finally:
2918                dlg.Destroy()
2919               
2920    def OnRenameData(self,event):
2921        'Renames an existing phase. Called by Data/Rename Phase menu'
2922        name = self.PatternTree.GetItemText(self.PickId)     
2923        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2924            if 'Bank' in name:
2925                names = name.split('Bank')
2926                names[1] = ' Bank'+names[1]
2927            elif 'Azm' in name:
2928                names = name.split('Azm')
2929                names[1] = ' Azm'+names[1]
2930            else:
2931                names = [name,'']
2932            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
2933            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
2934                defaultValue=names[0][names[0].index(' ')+1:])
2935            try:
2936                if dlg.ShowModal() == wx.ID_OK:
2937                    name = dataType+dlg.GetValue()+names[1]
2938                    self.PatternTree.SetItemText(self.PickId,name)
2939            finally:
2940                dlg.Destroy()
2941       
2942    def GetFileList(self,fileType,skip=None):        #potentially useful?
2943        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2944        fileList = []
2945        Source = ''
2946        id, cookie = self.PatternTree.GetFirstChild(self.root)
2947        while id:
2948            name = self.PatternTree.GetItemText(id)
2949            if fileType in name:
2950                if id == skip:
2951                    Source = name
2952                else:
2953                    fileList.append([False,name,id])
2954            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2955        if skip:
2956            return fileList,Source
2957        else:
2958            return fileList
2959           
2960    def OnDataDelete(self, event):
2961        '''Delete one or more histograms from data tree. Called by the
2962        Data/DeleteData menu
2963        '''
2964        TextList = []
2965        DelList = []
2966        DelItemList = []
2967        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
2968        ifPWDR = False
2969        ifSASD = False
2970        ifIMG = False
2971        ifHKLF = False
2972        ifPDF = False
2973        if self.PatternTree.GetCount():
2974            item, cookie = self.PatternTree.GetFirstChild(self.root)
2975            while item:
2976                name = self.PatternTree.GetItemText(item)
2977                if name not in ['Notebook','Controls','Covariance','Constraints',
2978                    'Restraints','Phases','Rigid bodies','Sequential results']:
2979                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
2980                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
2981                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
2982                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
2983                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
2984                    TextList.append(name)
2985                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2986            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2987            try:
2988                if dlg.ShowModal() == wx.ID_OK:
2989                    result = dlg.GetSelections()
2990                    for i in result: DelList.append(TextList[i])
2991                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2992                    while item:
2993                        itemName = self.PatternTree.GetItemText(item)
2994                        if itemName in DelList:
2995                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
2996                            elif 'SASD' in itemName: nItems['SASD'] -= 1
2997                            elif 'IMG' in itemName: nItems['IMG'] -= 1
2998                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
2999                            elif 'PDF' in itemName: nItems['PDF'] -= 1
3000                            DelItemList.append(item)
3001                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3002                    for item in DelItemList:
3003                        self.PatternTree.Delete(item)
3004                    self.PickId = 0
3005                    self.PickIdText = None
3006                    self.PatternId = 0
3007                    if nItems['PWDR']:
3008                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3009                    else:
3010                        self.G2plotNB.Delete('Powder Patterns')
3011                    if not nItems['IMG']:
3012                        self.G2plotNB.Delete('2D Powder Image')
3013                    if not nItems['HKLF']:
3014                        self.G2plotNB.Delete('Structure Factors')
3015                        if '3D Structure Factors' in self.G2plotNB.plotList:
3016                            self.G2plotNB.Delete('3D Structure Factors')
3017            finally:
3018                dlg.Destroy()
3019
3020    def OnFileOpen(self, event, filename=None):
3021        '''Gets a GSAS-II .gpx project file in response to the
3022        File/Open Project menu button
3023        '''
3024        result = wx.ID_OK
3025        self.EnablePlot = False
3026        if self.PatternTree.GetChildrenCount(self.root,False):
3027            if self.dataFrame:
3028                self.dataFrame.Clear() 
3029            dlg = wx.MessageDialog(
3030                self,
3031                'Do you want to overwrite the current project? '+
3032                'Any unsaved changes in current project will be lost. Press OK to continue.',
3033                'Overwrite?',  wx.OK | wx.CANCEL)
3034            try:
3035                result = dlg.ShowModal()
3036                if result == wx.ID_OK:
3037                    self.PatternTree.DeleteChildren(self.root)
3038                    self.GSASprojectfile = ''
3039                    self.HKL = []
3040                    if self.G2plotNB.plotList:
3041                        self.G2plotNB.clear()
3042            finally:
3043                dlg.Destroy()
3044        if result != wx.ID_OK: return
3045
3046        if not filename:
3047            if self.dataDisplay: self.dataDisplay.Destroy()
3048            if self.LastGPXdir:
3049                pth = self.LastGPXdir
3050            else:
3051                pth = '.'
3052            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3053                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3054            try:
3055                if dlg.ShowModal() != wx.ID_OK: return
3056                self.GSASprojectfile = dlg.GetPath()
3057                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3058                self.dirname = dlg.GetDirectory()
3059            finally:
3060                dlg.Destroy()
3061        else:
3062            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3063            self.dirname = os.path.split(filename)[0]
3064
3065        try:
3066            self.StartProject()         #open the file if possible
3067        except:
3068            print '\nError opening file ',filename
3069            import traceback
3070            print traceback.format_exc()
3071       
3072    def StartProject(self):
3073        '''Opens a GSAS-II project file & selects the 1st available data set to
3074        display (PWDR, HKLF or SASD)
3075        '''
3076       
3077        Id = 0
3078        G2IO.ProjFileOpen(self)
3079        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3080        self.PatternTree.Expand(self.root)
3081        self.HKL = []
3082        item, cookie = self.PatternTree.GetFirstChild(self.root)
3083        while item and not Id:
3084            name = self.PatternTree.GetItemText(item)
3085            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3086                Id = item
3087            elif name == 'Controls':
3088                data = self.PatternTree.GetItemPyData(item)
3089                if data:
3090                    for item in self.Refine: item.Enable(True)
3091                    self.EnableSeqRefineMenu()
3092            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3093        if Id:
3094            self.EnablePlot = True
3095            self.PatternTree.SelectItem(Id)
3096        self.CheckNotebook()
3097        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3098        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3099        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3100        self.LastGPXdir = pth
3101
3102    def OnFileClose(self, event):
3103        '''Clears the data tree in response to the
3104        File/New Project menu button. User is given option to save
3105        the project.
3106        '''
3107        if self.dataFrame:
3108            self.dataFrame.Clear()
3109            self.dataFrame.SetLabel('GSAS-II data display') 
3110        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3111        try:
3112            result = dlg.ShowModal()
3113            if result == wx.ID_OK:
3114                self.OnFileSaveMenu(event)
3115            if result != wx.ID_CANCEL:
3116                self.GSASprojectfile = ''
3117                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3118                self.PatternTree.DeleteChildren(self.root)
3119                if self.HKL: self.HKL = []
3120                if self.G2plotNB.plotList:
3121                    self.G2plotNB.clear()
3122        finally:
3123            dlg.Destroy()
3124
3125    def OnFileSave(self, event):
3126        '''Save the current project in response to the
3127        File/Save Project menu button
3128        '''
3129       
3130        if self.GSASprojectfile: 
3131            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3132            self.CheckNotebook()
3133            G2IO.ProjFileSave(self)
3134        else:
3135            self.OnFileSaveas(event)
3136
3137    def OnFileSaveas(self, event):
3138        '''Save the current project in response to the
3139        File/Save as menu button
3140        '''
3141        if GSASIIpath.GetConfigValue('Starting_directory'):
3142            pth = GSASIIpath.GetConfigValue('Starting_directory')
3143            pth = os.path.expanduser(pth) 
3144        elif self.LastGPXdir:
3145            pth = self.LastGPXdir
3146        else:
3147            pth = '.'
3148        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3149            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3150        try:
3151            if dlg.ShowModal() == wx.ID_OK:
3152                self.GSASprojectfile = dlg.GetPath()
3153                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3154                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3155                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3156                self.CheckNotebook()
3157                G2IO.ProjFileSave(self)
3158                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3159        finally:
3160            dlg.Destroy()
3161
3162    def ExitMain(self, event):
3163        '''Called if the main window is closed'''
3164        if self.G2plotNB:
3165            self.G2plotNB.Destroy()
3166        if self.dataFrame:
3167            self.dataFrame.Clear() 
3168            self.dataFrame.Destroy()
3169        if self.undofile:
3170            os.remove(self.undofile)
3171        sys.exit()
3172       
3173    def OnFileExit(self, event):
3174        '''Called in response to the File/Quit menu button'''
3175        if self.G2plotNB:
3176            self.G2plotNB.Destroy()
3177        if self.dataFrame:
3178            self.dataFrame.Clear() 
3179            self.dataFrame.Destroy()
3180        self.Close()
3181       
3182    def OnExportPeakList(self,event):
3183        nptand = lambda x: np.tan(x*math.pi/180.)
3184        pth = G2G.GetExportPath(self)
3185        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3186            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3187        try:
3188            if dlg.ShowModal() == wx.ID_OK:
3189                self.peaklistfile = dlg.GetPath()
3190                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3191                file = open(self.peaklistfile,'w')               
3192                item, cookie = self.PatternTree.GetFirstChild(self.root)
3193                while item:
3194                    name = self.PatternTree.GetItemText(item)
3195                    if 'PWDR' in name:
3196                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3197                        wave = 0.0
3198                        while item2:
3199                            name2 = self.PatternTree.GetItemText(item2)
3200                            if name2 == 'Instrument Parameters':
3201                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3202                                Type = Inst['Type'][0]
3203                                if 'T' not in Type:
3204                                    wave = G2mth.getWave(Inst)
3205                            elif name2 == 'Peak List':
3206                                pkdata = self.PatternTree.GetItemPyData(item2)
3207                                peaks = pkdata['peaks']
3208                                sigDict = pkdata['sigDict']
3209                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3210                        file.write("#%s \n" % (name+' Peak List'))
3211                        if wave:
3212                            file.write('#wavelength = %10.6f\n'%(wave))
3213                        if 'T' in Type:
3214                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3215                        else:
3216                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3217                        for ip,peak in enumerate(peaks):
3218                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3219                            if 'T' in Type:  #TOF - more cols
3220                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3221                                for name in esds.keys():
3222                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3223                                sig = np.sqrt(peak[8])
3224                                gam = peak[10]
3225                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3226                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3227                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3228                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3229                            else:               #CW
3230                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3231                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3232                                for name in esds.keys():
3233                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3234                                sig = np.sqrt(peak[4]) #var -> sig
3235                                gam = peak[6]
3236                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3237                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3238                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3239                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3240                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3241                file.close()
3242        finally:
3243            dlg.Destroy()
3244       
3245    def OnExportHKL(self,event):
3246        pth = G2G.GetExportPath(self)
3247        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3248            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3249        try:
3250            if dlg.ShowModal() == wx.ID_OK:
3251                self.peaklistfile = dlg.GetPath()
3252                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3253                file = open(self.peaklistfile,'w')               
3254                item, cookie = self.PatternTree.GetFirstChild(self.root)
3255                while item:
3256                    name = self.PatternTree.GetItemText(item)
3257                    if 'PWDR' in name:
3258                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3259                        while item2:
3260                            name2 = self.PatternTree.GetItemText(item2)
3261                            if name2 == 'Reflection Lists':
3262                                data = self.PatternTree.GetItemPyData(item2)
3263                                phases = data.keys()
3264                                for phase in phases:
3265                                    peaks = data[phase]
3266                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3267                                    if 'T' in peaks.get('Type','PXC'):
3268                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3269                                    else:               
3270                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3271                                    for peak in peaks['RefList']:
3272                                        if 'T' in peaks.get('Type','PXC'):
3273                                            sig = np.sqrt(peak[6])
3274                                            gam = peak[7]
3275                                            FWHM = G2pwd.getgamFW(gam,sig)
3276                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3277                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3278                                        else:
3279                                            sig = np.sqrt(peak[6])
3280                                            gam = peak[7]
3281                                            FWHM = G2pwd.getgamFW(gam,sig)
3282                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3283                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3284                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3285                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3286                file.close()
3287        finally:
3288            dlg.Destroy()
3289       
3290    def OnExportPDF(self,event):
3291        #need S(Q) and G(R) to be saved here - probably best from selection?
3292        names = ['All']
3293        exports = []
3294        item, cookie = self.PatternTree.GetFirstChild(self.root)
3295        while item:
3296            name = self.PatternTree.GetItemText(item)
3297            if 'PDF' in name:
3298                names.append(name)
3299            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3300        if names:
3301            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3302            if dlg.ShowModal() == wx.ID_OK:
3303                sel = dlg.GetSelections()
3304                if sel[0] == 0:
3305                    exports = names[1:]
3306                else:
3307                    for x in sel:
3308                        exports.append(names[x])
3309            dlg.Destroy()
3310        if exports:
3311            G2IO.PDFSave(self,exports)
3312       
3313    def OnMakePDFs(self,event):
3314        '''Calculates PDFs
3315        '''
3316        sind = lambda x: math.sin(x*math.pi/180.)
3317        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3318        TextList = ['All PWDR']
3319        PDFlist = []
3320        Names = []
3321        if self.PatternTree.GetCount():
3322            id, cookie = self.PatternTree.GetFirstChild(self.root)
3323            while id:
3324                name = self.PatternTree.GetItemText(id)
3325                Names.append(name)
3326                if 'PWDR' in name:
3327                    TextList.append(name)
3328                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3329            if len(TextList) == 1:
3330                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3331                return
3332            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3333            try:
3334                if dlg.ShowModal() == wx.ID_OK:
3335                    result = dlg.GetSelections()
3336                    for i in result: PDFlist.append(TextList[i])
3337                    if 0 in result:
3338                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3339                    for item in PDFlist:
3340                        PWDRname = item[4:]
3341                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3342                        Data = {
3343                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3344                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3345                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3346                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3347                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3348                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3349                            'Lorch':True,}
3350                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3351                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3352                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3353                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3354                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3355                for item in self.ExportPDF: item.Enable(True)
3356            finally:
3357                dlg.Destroy()
3358               
3359    def GetPWDRdatafromTree(self,PWDRname):
3360        ''' Returns powder data from GSASII tree
3361
3362        :param str PWDRname: a powder histogram name as obtained from
3363          :meth:`GSASIIstruct.GetHistogramNames`
3364
3365        :returns: PWDRdata = powder data dictionary with
3366          Powder data arrays, Limits, Instrument Parameters,
3367          Sample Parameters           
3368        '''
3369        PWDRdata = {}
3370        try:
3371            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3372        except ValueError:
3373            PWDRdata['wtFactor'] = 1.0
3374        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3375        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3376        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3377        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3378        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3379        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3380        if 'ranId' not in PWDRdata:  # patch, add a random Id
3381            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3382        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3383            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3384        return PWDRdata
3385
3386    def GetHKLFdatafromTree(self,HKLFname):
3387        ''' Returns single crystal data from GSASII tree
3388
3389        :param str HKLFname: a single crystal histogram name as obtained
3390          from
3391          :meth:`GSASIIstruct.GetHistogramNames`
3392
3393        :returns: HKLFdata = single crystal data list of reflections
3394
3395        '''
3396        HKLFdata = {}
3397        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3398#        try:
3399#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3400#        except ValueError:
3401#            HKLFdata['wtFactor'] = 1.0
3402        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3403        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3404        return HKLFdata
3405       
3406    def GetPhaseData(self):
3407        '''Returns a dict with defined phases.
3408        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3409        get same info from GPX file.
3410        '''
3411        phaseData = {}
3412        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3413            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3414        else:
3415            print 'no phases found in GetPhaseData'
3416            sub = None
3417        if sub:
3418            item, cookie = self.PatternTree.GetFirstChild(sub)
3419            while item:
3420                phaseName = self.PatternTree.GetItemText(item)
3421                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3422                if 'ranId' not in phaseData[phaseName]:
3423                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3424                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3425        return phaseData
3426
3427    def GetPhaseInfofromTree(self):
3428        '''Get the phase names and their rId values,
3429        also the histograms used in each phase.
3430
3431        :returns: (phaseRIdList, usedHistograms) where
3432
3433          * phaseRIdList is a list of random Id values for each phase
3434          * usedHistograms is a dict where the keys are the phase names
3435            and the values for each key are a list of the histogram names
3436            used in each phase.
3437        '''
3438        phaseRIdList = []
3439        usedHistograms = {}
3440        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3441        if sub:
3442            item, cookie = self.PatternTree.GetFirstChild(sub)
3443            while item:
3444                phaseName = self.PatternTree.GetItemText(item)
3445                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3446                if ranId: phaseRIdList.append(ranId)
3447                data = self.PatternTree.GetItemPyData(item)
3448                UseList = data['Histograms']
3449                usedHistograms[phaseName] = UseList.keys()
3450                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3451        return phaseRIdList,usedHistograms
3452
3453    def GetPhaseNames(self):
3454        '''Returns a list of defined phases.
3455        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3456        get same info from GPX file.
3457        '''
3458        phaseNames = []
3459        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3460            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3461        else:
3462            print 'no phases found in GetPhaseNames'
3463            sub = None
3464        if sub:
3465            item, cookie = self.PatternTree.GetFirstChild(sub)
3466            while item:
3467                phase = self.PatternTree.GetItemText(item)
3468                phaseNames.append(phase)
3469                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3470        return phaseNames
3471   
3472    def GetHistogramNames(self,hType):
3473        """ Returns a list of histogram names found in the GSASII data tree
3474        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3475        get same info from GPX file.
3476       
3477        :param str hType: list of histogram types
3478        :return: list of histogram names
3479       
3480        """
3481        HistogramNames = []
3482        if self.PatternTree.GetCount():
3483            item, cookie = self.PatternTree.GetFirstChild(self.root)
3484            while item:
3485                name = self.PatternTree.GetItemText(item)
3486                if name[:4] in hType:
3487                    HistogramNames.append(name)       
3488                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3489
3490        return HistogramNames
3491                   
3492    def GetUsedHistogramsAndPhasesfromTree(self):
3493        ''' Returns all histograms that are found in any phase
3494        and any phase that uses a histogram.
3495        This also assigns numbers to used phases and histograms by the
3496        order they appear in the file.
3497        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3498        get same info from GPX file.
3499
3500        :returns: (Histograms,Phases)
3501
3502            * Histograms = dictionary of histograms as {name:data,...}
3503            * Phases = dictionary of phases that use histograms
3504        '''
3505        Histograms = {}
3506        Phases = {}
3507        phaseNames = self.GetPhaseNames()
3508        phaseData = self.GetPhaseData()
3509        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3510
3511        for phase in phaseData:
3512            Phase = phaseData[phase]
3513            pId = phaseNames.index(phase)
3514            Phase['pId'] = pId
3515            if Phase['Histograms']:
3516                if phase not in Phases:
3517                    Phases[phase] = Phase
3518                for hist in Phase['Histograms']:
3519                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3520                        Phase['Histograms'][hist]['Use'] = True         
3521                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3522                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3523                        if item:
3524                            if 'PWDR' in hist[:4]: 
3525                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3526                            elif 'HKLF' in hist[:4]:
3527                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3528                            hId = histoList.index(hist)
3529                            Histograms[hist]['hId'] = hId
3530                        else: # would happen if a referenced histogram were renamed or deleted
3531                            print('For phase "'+str(phase)+
3532                                  '" unresolved reference to histogram "'+str(hist)+'"')
3533        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3534        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3535        return Histograms,Phases
3536       
3537    def MakeLSParmDict(self):
3538        '''Load all parameters used for computation from the tree into a
3539        dict of paired values [value, refine flag]. Note that this is
3540        different than the parmDict used in the refinement, which only has
3541        values.
3542
3543        Note that similar things are done in
3544        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3545        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3546        a GPX file).
3547
3548        :returns: (parmDict,varyList) where:
3549
3550         * parmDict is a dict with values and refinement flags
3551           for each parameter and
3552         * varyList is a list of variables (refined parameters).
3553        '''
3554        parmDict = {}
3555        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3556        for phase in Phases:
3557            if 'pId' not in Phases[phase]:
3558                self.ErrorDialog('View parameter error','You must run least squares at least once')
3559                raise Exception,'No pId for phase '+str(phase)
3560        rigidbodyDict = self.PatternTree.GetItemPyData(   
3561            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3562        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3563        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3564        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3565        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3566        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3567        varyList = rbVary+phaseVary+hapVary+histVary
3568        parmDict.update(rbDict)
3569        parmDict.update(phaseDict)
3570        parmDict.update(hapDict)
3571        parmDict.update(histDict)
3572        for parm in parmDict:
3573            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3574                'Omega','Chi','Phi','nDebye','nPeaks']:
3575                parmDict[parm] = [parmDict[parm],'-']
3576            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3577                parmDict[parm] = [parmDict[parm],'-']
3578            elif parm in varyList:
3579                parmDict[parm] = [parmDict[parm],'T']
3580            else:
3581                parmDict[parm] = [parmDict[parm],'F']
3582        # for i in parmDict: print i,'\t',parmDict[i]
3583        # fl = open('parmDict.dat','wb')
3584        # import cPickle
3585        # cPickle.dump(parmDict,fl,1)
3586        # fl.close()
3587        return parmDict,varyList
3588
3589    def ShowLSParms(self,event):
3590        '''Displays a window showing all parameters in the refinement.
3591        Called from the Calculate/View LS Parms menu.
3592        '''
3593        parmDict,varyList = self.MakeLSParmDict()
3594        parmValDict = {}
3595        for i in parmDict:
3596            parmValDict[i] = parmDict[i][0]
3597           
3598        reqVaryList = tuple(varyList) # save requested variables
3599        try:
3600            # process constraints
3601            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3602            Constraints = self.PatternTree.GetItemPyData(sub)
3603            constList = []
3604            for item in Constraints:
3605                if item.startswith('_'): continue
3606                constList += Constraints[item]
3607            G2mv.InitVars()
3608            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3609            groups,parmlist = G2mv.GroupConstraints(constrDict)
3610            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3611            G2mv.Map2Dict(parmValDict,varyList)
3612        except:
3613            pass
3614        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3615        dlg.ShowModal()
3616        dlg.Destroy()
3617       
3618    def OnRefine(self,event):
3619        '''Perform a refinement.
3620        Called from the Calculate/Refine menu.
3621        '''       
3622        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3623        if Id:
3624            dlg = wx.MessageDialog(
3625                self,
3626                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3627                'Remove sequential results?',wx.OK|wx.CANCEL)
3628            if dlg.ShowModal() == wx.ID_OK:
3629                self.PatternTree.Delete(Id)
3630                dlg.Destroy()
3631            else:
3632                dlg.Destroy()
3633                return
3634        self.OnFileSave(event)
3635        # check that constraints are OK here
3636        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3637        if errmsg:
3638            self.ErrorDialog('Refinement error',errmsg)
3639            return
3640        if warnmsg:
3641            print('Conflict between refinment flag settings and constraints:\n'+
3642                warnmsg+'\nRefinement not possible')
3643            self.ErrorDialog('Refinement Flag Error',
3644                'Conflict between refinement flag settings and constraints:\n'+
3645                warnmsg+'\nRefinement not possible')
3646            return
3647        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3648            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3649            parent=self)
3650        Size = dlg.GetSize()
3651        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3652            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3653        dlg.CenterOnParent()
3654        Rw = 100.00
3655        oldId =  self.PatternTree.GetSelection()        #retain current selection
3656        oldPath = self.GetTreeItemsList(oldId)
3657        parentName = ''
3658        oldName = self.PatternTree.GetItemText(oldId)
3659        parentId = self.PatternTree.GetItemParent(oldId)
3660        if parentId:
3661            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3662            if 'Phases' in parentName:
3663                tabId = self.dataDisplay.GetSelection()
3664        try:
3665            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)    #Msg is Rvals dict if Ok=True
3666        finally:
3667            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3668            dlg.Destroy()
3669            wx.Yield()
3670        if OK:
3671            Rw = Msg['Rwp']
3672            lamMax = Msg.get('lamMax',0.001)
3673            text = 'Load new result?'
3674            if lamMax >= 10.:
3675                text += '\nWARNING: Steepest descents dominates;'+   \
3676                ' minimum may not have been reached\nor result may be false minimum.'+  \
3677                ' You should reconsider your parameter suite'
3678            dlg2 = wx.MessageDialog(self,text,'Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3679            try:
3680                if dlg2.ShowModal() == wx.ID_OK:
3681                    Id = 0
3682                    self.PatternTree.DeleteChildren(self.root)
3683                    self.HKL = []
3684                    G2IO.ProjFileOpen(self,False)
3685                    Id =  self.root
3686                    txt = None
3687                    for txt in oldPath:
3688                        Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3689                    self.PickIdText = None  #force reload of page
3690                    if Id:
3691                        self.PickId = Id
3692                        self.PatternTree.SelectItem(Id)
3693                        G2gd.MovePatternTreeToGrid(self,Id) # reload current tree item, should update current plot
3694            finally:
3695                dlg2.Destroy()
3696        else:
3697            self.ErrorDialog('Refinement error',Msg)
3698
3699    def OnSeqRefine(self,event):
3700        '''Perform a sequential refinement.
3701        Called from the Calculate/Sequential refine menu.
3702        '''       
3703        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3704        if not Id:
3705            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3706            self.PatternTree.SetItemPyData(Id,{})           
3707        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3708        Controls['ShowCell'] = True
3709        self.OnFileSave(event)
3710        # check that constraints are OK here
3711        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3712        if errmsg:
3713            self.ErrorDialog('Refinement error',errmsg)
3714            return
3715        if warnmsg:
3716            print('Conflict between refinment flag settings and constraints:\n'+
3717                  warnmsg+'\nRefinement not possible')
3718            self.ErrorDialog('Refinement Flag Error',
3719                             'Conflict between refinment flag settings and constraints:\n'+
3720                             warnmsg+'\nRefinement not possible')
3721            return
3722        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3723            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3724            parent=self)           
3725        Size = dlg.GetSize()
3726        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3727            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3728        dlg.CenterOnParent()
3729        try:
3730            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)     #Msg is Rvals dict if Ok=True
3731        finally:
3732            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3733            dlg.Destroy()
3734            wx.Yield()
3735        if OK:
3736            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3737            try:
3738                if dlg.ShowModal() == wx.ID_OK:
3739                    Id = 0
3740#                    self.G2plotNB.setReplotFlags() # mark all plots as old - doesn't exist!
3741                    self.PickIdText = None  #force reload of PickId contents
3742                    self.PatternTree.DeleteChildren(self.root)
3743                    if len(self.HKL): self.HKL = []
3744                    if self.G2plotNB.plotList:
3745                        self.G2plotNB.clear()
3746                    G2IO.ProjFileOpen(self,False)
3747                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3748                    self.PatternTree.SelectItem(Id)
3749#                    self.G2plotNB.replotAll() # refresh any plots not yet updated - doesn't exist!
3750            finally:
3751                dlg.Destroy()
3752        else:
3753            self.ErrorDialog('Sequential refinement error',Msg)
3754       
3755    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3756        'Display an error message'
3757        result = None
3758        if parent is None:
3759            dlg = wx.MessageDialog(self, message, title,  wtype)
3760        else:
3761            dlg = wx.MessageDialog(parent, message, title,  wtype)
3762            dlg.CenterOnParent() # not working on Mac
3763        try:
3764            result = dlg.ShowModal()
3765        finally:
3766            dlg.Destroy()
3767        return result
3768   
3769    def OnSaveMultipleImg(self,event):
3770        '''Select and save multiple image parameter and mask files
3771        '''
3772        G2IO.SaveMultipleImg(self)
3773       
3774class GSASIImain(wx.App):
3775    '''Defines a wxApp for GSAS-II
3776
3777    Creates a wx frame (self.main) which contains the display of the
3778    data tree.
3779    '''
3780    def OnInit(self):
3781        '''Called automatically when the app is created.'''
3782        import platform
3783        if '2.7' not in sys.version[:5]:
3784            dlg = wx.MessageDialog(None, 
3785                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
3786                'Python version error',  wx.OK)
3787            try:
3788                result = dlg.ShowModal()
3789            finally:
3790                dlg.Destroy()
3791            sys.exit()
3792        self.main = GSASII(None)
3793        self.main.Show()
3794        self.SetTopWindow(self.main)
3795        # save the current package versions
3796        self.main.PackageVersions = []
3797        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
3798        for p in (wx,mpl,np,sp,ogl):
3799            self.main.PackageVersions.append([p.__name__,p.__version__])
3800        try:
3801            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
3802        except:
3803            try:
3804                from PIL import PILLOW_VERSION
3805                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
3806            except:
3807                pass
3808        self.main.PackageVersions.append([' Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
3809       
3810        return True
3811    # def MacOpenFile(self, filename):
3812    #     '''Called on Mac every time a file is dropped on the app when it is running,
3813    #     treat this like a File/Open project menu action.
3814    #     Should be ignored on other platforms
3815    #     '''
3816    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3817    #     print 'MacOpen',filename
3818    #     if os.path.splitext(filename)[1] == '.py': return
3819    #     # end PATCH
3820    #     self.main.OnFileOpen(None,filename)
3821    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3822       
3823def main():
3824    '''Start up the GSAS-II application'''
3825    #application = GSASIImain() # don't redirect output, someday we
3826    # may want to do this if we can
3827    application = GSASIImain(0)
3828    if GSASIIpath.GetConfigValue('wxInspector'):
3829        import wx.lib.inspection as wxeye
3830        wxeye.InspectionTool().Show()
3831
3832    #application.main.OnRefine(None)
3833    application.MainLoop()
3834   
3835if __name__ == '__main__':
3836    # print versions
3837    print "Python module versions loaded:"
3838    print "python:     ",sys.version.split()[0]
3839    print "wxpython:   ",wx.__version__
3840    print "matplotlib: ",mpl.__version__
3841    print "numpy:      ",np.__version__
3842    print "scipy:      ",sp.__version__
3843    print "OpenGL:     ",ogl.__version__
3844    try:
3845        from PIL import Image
3846        try:
3847            from PIL import PILLOW_VERSION
3848            version = PILLOW_VERSION
3849        except:
3850            version = Image.VERSION
3851        print "pillow:     ",version
3852    except ImportError:
3853        try:
3854            import Image
3855            print "Image (PIL):",Image.VERSION
3856        except ImportError:
3857            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3858    try:
3859        import mkl
3860        print "Max threads ",mkl.get_max_threads()
3861    except:
3862        pass
3863    import platform
3864    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3865    #print "wxPython description",wx.PlatformInfo
3866    print "This is GSAS-II version:     ",__version__,' revision #'+str(GSASIIpath.GetVersionNumber())
3867    GSASIIpath.InvokeDebugOpts()
3868    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.