source: trunk/GSASII.py @ 2998

Last change on this file since 2998 was 2925, checked in by toby, 8 years ago

add switch to 2frame; allow for latest G2path

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