source: trunk/GSASII.py @ 2739

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

comment out "other self-updating plots" - caused failures in GSASII.py
newLeBail interfered with ordinary Rietveld refinement - set False by default (was true)
add generation of delt-G(R) calc - not complete

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