source: branch/2frame/GSASII.py @ 2898

Last change on this file since 2898 was 2898, checked in by toby, 4 years ago

fix scroll bars on tree panel, start debugging on data panel

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