source: trunk/GSASII.py @ 2843

Last change on this file since 2843 was 2843, checked in by vondreele, 4 years ago

fix focus issues from lists of histograms in tree

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