source: trunk/GSASII.py @ 2629

Last change on this file since 2629 was 2629, checked in by toby, 6 years ago

chmod u+x on GSASII.py via subversion

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