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

Last change on this file since 2888 was 2888, checked in by vondreele, 5 years ago
  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Revision URL Id
File size: 211.0 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2017-07-01 11:22:10 +0000 (Sat, 01 Jul 2017) $
6# $Author: vondreele $
7# $Revision: 2888 $
8# $URL: branch/2frame/GSASII.py $
9# $Id: GSASII.py 2888 2017-07-01 11:22:10Z vondreele $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import glob
24import imp
25import inspect
26import numpy as np
27import scipy as sp
28import wx
29import wx.lib.scrolledpanel as wxscroll
30try:  # patch for LANG environment var problem on occasional OSX machines
31    import locale
32    locale.getdefaultlocale()
33except ValueError:
34    print('Fixing location (see https://github.com/matplotlib/matplotlib/issues/5420.)')
35    os.environ['LC_ALL'] = 'en_US.UTF-8'
36    locale.getdefaultlocale()
37import matplotlib as mpl
38try:
39    import OpenGL as ogl
40except ImportError:
41    print('*******************************************************')
42    print('PyOpenGL is missing from your python installation')
43    print('     - we will try to install it')
44    print('*******************************************************')
45    def install_with_easyinstall(package):
46        try: 
47            print "trying a system-wide PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],package])
49            return
50        except:
51            pass
52        try: 
53            print "trying a user level PyOpenGl install"
54            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
55            return
56        except:
57            print "Install of '+package+' failed. Please report this information:"
58            import traceback
59            print traceback.format_exc()
60            sys.exit()
61    from setuptools.command import easy_install
62    install_with_easyinstall('PyOpenGl')
63    print('*******************************************************')         
64    print('OpenGL has been installed. Restarting GSAS-II')
65    print('*******************************************************')         
66    loc = os.path.dirname(__file__)
67    import subprocess
68    subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
69    sys.exit()
70   
71# load the GSAS routines
72import GSASIIpath
73GSASIIpath.SetVersionNumber("$Revision: 2888 $")
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
124class 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        clientSize = wx.ClientDisplayRect()
2539        Size = self.GetSize()
2540        xPos = clientSize[2]-Size[0]
2541        self.SetPosition(wx.Point(xPos,clientSize[1]))
2542        self._init_Imports()
2543        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2544        self.MakePDF = []
2545        self.Refine = []
2546        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2547        #self.ExportPattern = []
2548        self.ExportPeakList = []
2549        self.ExportHKL = []
2550        self.ExportPDF = []
2551        self.ExportPhase = []
2552        self.ExportCIF = []
2553        #
2554        self.GSASIIMenu = wx.MenuBar()
2555        # create a list of all dataframe menus (appended in PrefillDataMenu)
2556        self.dataMenuBars = [self.GSASIIMenu]
2557        self.MacroStatusList = []
2558        self.FillMainMenu(self.GSASIIMenu)
2559        self.SetMenuBar(self.GSASIIMenu)
2560        self.Bind(wx.EVT_SIZE, self.OnSize)
2561        self.Status = self.CreateStatusBar()
2562#TODO - split window tree on left, data on right
2563        self.mainPanel = GSASIIsplit(self,-1)
2564        self.treePanel = wx.Panel(self.mainPanel, wx.ID_ANY,
2565                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
2566        self.treePanel.SetAutoLayout(True)
2567        self.dataPanel = wx.Panel(self.mainPanel, wx.ID_ANY,
2568                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
2569        self.dataPanel.SetAutoLayout(True)
2570        self.dataWindow = G2gd.DataWindow(self.dataPanel)
2571        self.dataWindow.SetupScrolling()
2572        self.mainPanel.SetMinimumPaneSize(100)
2573        self.mainPanel.SplitVertically(self.treePanel, self.dataPanel, 200)
2574        self.dataFrame = self.dataWindow        #kluge!!
2575       
2576        wxID_PATTERNTREE = wx.NewId()
2577        treeSizer = wx.BoxSizer()
2578        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2579            parent=self.treePanel, size=self.treePanel.GetSize(),style=wx.TR_DEFAULT_STYLE )
2580        treeSizer.Add(self.PatternTree,1,wx.EXPAND)
2581        self.treePanel.SetSizer(treeSizer)
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        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2599            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2600        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame,G2frame=self)
2601        plotFrame.Show()
2602        self.SetDataSize()
2603       
2604    def __init__(self, parent):
2605        self.ExportLookup = {}
2606        self.exporterlist = []
2607        self._init_ctrls(parent)
2608        self.Image = wx.Image(
2609            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2610            wx.BITMAP_TYPE_ICO)
2611        if "wxMSW" in wx.PlatformInfo:
2612            img = self.Image.Scale(16, 16).ConvertToBitmap()
2613        elif "wxGTK" in wx.PlatformInfo:
2614            img = self.Image.Scale(22, 22).ConvertToBitmap()
2615        else:
2616            img = self.Image.ConvertToBitmap()
2617        self.SetIcon(wx.IconFromBitmap(img))
2618        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2619        # various defaults
2620        self.oldFocus = None
2621        self.GSASprojectfile = ''
2622        self.undofile = ''
2623        self.TreeItemDelete = False
2624        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False,'sqPlot':False}
2625        self.Weight = False
2626        self.IfPlot = False
2627        self.DDShowAll = False
2628        self.atmSel = ''
2629        self.PatternId = 0
2630        self.PickId = 0
2631        self.PickIdText = None
2632        self.PeakTable = []
2633        self.LimitsTable = []
2634        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2635        self.HKL = []
2636        self.Lines = []
2637        self.itemPicked = None
2638        self.Interpolate = 'nearest'
2639        self.ContourColor = GSASIIpath.GetConfigValue('Contour_color','Paired')
2640        self.VcovColor = 'RdYlGn'
2641        self.RamaColor = 'Blues'
2642        self.Projection = 'equal area'
2643        self.logPlot = False
2644        self.plusPlot = True
2645        self.ErrorBars = False
2646        self.Contour = False
2647        self.Legend = False
2648        self.SinglePlot = True
2649        self.Waterfall = False
2650        self.selections= None
2651        self.PDFselections = None
2652        self.SubBack = False
2653        self.seqReverse = False
2654        self.seqLines = True #draw lines between points
2655        self.plotView = 0
2656        self.Image = 0
2657        self.oldImagefile = '' # the name of the last image file read
2658        self.oldImageTag = None # the name of the tag for multi-image files
2659        self.PauseIntegration = False
2660        self.ImageZ = []
2661        self.Integrate = 0
2662        self.imageDefault = {}
2663        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2664        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2665        self.autoIntFrame = None
2666        self.IntegratedList = [] # list of already integrated IMG tree items
2667        self.Sngl = False
2668        self.ifGetRing = False
2669        self.MaskKey = ''           #trigger for making image masks
2670        self.MskDelete = False      #trigger for mask delete
2671        self.StrainKey = ''         #ditto for new strain d-zeros
2672        self.EnablePlot = True
2673        self.hist = ''              # selected histogram in Phase/Data tab
2674        self.dirname = os.path.abspath(os.path.expanduser('~'))       #start in the users home directory by default; may be meaningless
2675        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2676        self.LastImportDir = None # last-used directory where an import was done
2677        self.LastGPXdir = None    # directory where a GPX file was last read
2678        self.LastExportDir = None  # the last directory used for exports, if any.
2679        self.dataDisplayPhaseText = ''
2680        self.lastTreeSetting = []
2681        self.ExpandingAll = False
2682        self.SeqTblHideList = []
2683               
2684        arg = sys.argv
2685        if len(arg) > 1 and arg[1]:
2686            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2687            self.dirname = os.path.abspath(os.path.dirname(arg[1]))
2688            if self.dirname: os.chdir(self.dirname)
2689            try:
2690                self.StartProject()         #open the file if possible
2691                return
2692            except Exception:
2693                print 'Error opening or reading file',arg[1]
2694                import traceback
2695                print traceback.format_exc()
2696               
2697        if GSASIIpath.GetConfigValue('Starting_directory'):
2698            try:
2699                pth = GSASIIpath.GetConfigValue('Starting_directory')
2700                pth = os.path.expanduser(pth) 
2701                os.chdir(pth)
2702                self.LastGPXdir = pth
2703            except:
2704                print('Ignoring Config Starting_directory value: '+
2705                      GSASIIpath.GetConfigValue('Starting_directory'))
2706
2707    def GetTreeItemsList(self,item):
2708        return self.PatternTree._getTreeItemsList(item)
2709
2710    def OnSize(self,event):
2711        'Called to make PatternTree fill mainPanel'
2712        w,h = self.GetClientSizeTuple()
2713        self.mainPanel.SetSize(wx.Size(w,h))
2714        self.PatternTree.SetSize(wx.Size(w,h))
2715       
2716    def SetDataSize(self):
2717        Size = self.GetSize()
2718        self.SetSize(Size)
2719        Size[1] += 1        #kluge to ensure scrollbar settings & window properly displayed
2720        self.SetSize(Size)
2721                               
2722    def OnDataTreeSelChanged(self, event):
2723        '''Called when a data tree item is selected'''
2724        if self.TreeItemDelete:
2725            self.TreeItemDelete = False
2726        else:
2727            if self.ExpandingAll:
2728                if GSASIIpath.GetConfigValue('debug'): print('Skipping Tree selection due to ExpandAll')
2729                return
2730            pltNum = self.G2plotNB.nb.GetSelection()
2731            if pltNum >= 0:                         #to avoid the startup with no plot!
2732                self.G2plotNB.nb.GetPage(pltNum)
2733            item = event.GetItem()
2734            wx.CallAfter(G2gd.SelectDataTreeItem,self,item,self.oldFocus)
2735            #if self.oldFocus: # now done via last parameter on SelectDataTreeItem
2736            #    wx.CallAfter(self.oldFocus.SetFocus)
2737       
2738    def OnPatternTreeItemCollapsed(self, event):
2739        'Called when a tree item is collapsed - all children will be collapsed'
2740        self.PatternTree.CollapseAllChildren(event.GetItem())
2741
2742    def OnPatternTreeItemExpanded(self, event):
2743        'Called when a tree item is expanded'
2744        self.OnDataTreeSelChanged(event)
2745        event.Skip()
2746       
2747    def OnPatternTreeItemDelete(self, event):
2748        'Called when a tree item is deleted -- not sure what this does'
2749        self.TreeItemDelete = True
2750
2751    def OnPatternTreeItemActivated(self, event):
2752        'Called when a tree item is activated'
2753        event.Skip()
2754       
2755    def OnPatternTreeBeginRDrag(self,event):
2756        event.Allow()
2757        self.BeginDragId = event.GetItem()
2758        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2759        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2760        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2761        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2762        while item:     #G2 data tree has no sub children under a child of a tree item
2763            name = self.PatternTree.GetItemText(item)
2764            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2765            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2766       
2767    def OnPatternTreeEndDrag(self,event):
2768        event.Allow()
2769        self.EndDragId = event.GetItem()
2770        try:
2771            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2772        except:
2773            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2774            NewParent = self.root
2775        if self.ParentId != NewParent:
2776            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2777        else:
2778            Name,Item = self.DragData[0]
2779            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2780            self.PatternTree.SetItemPyData(NewId,Item)
2781            for name,item in self.DragData[1:]:     #loop over children
2782                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2783                self.PatternTree.SetItemPyData(Id,item)
2784            self.PatternTree.Delete(self.BeginDragId)
2785            G2gd.SelectDataTreeItem(self,NewId)
2786       
2787    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2788        'Allows stepping through the tree with the up/down arrow keys'
2789        self.oldFocus = wx.Window.FindFocus()
2790        keyevt = event.GetKeyEvent()
2791        key = event.GetKeyCode()
2792        item = self.PatternTree.GetSelection()
2793        if type(item) is int: return # is this the toplevel in tree?
2794        name = self.PatternTree.GetItemText(item)
2795        parent = self.PatternTree.GetItemParent(item)
2796        if key == wx.WXK_UP:
2797            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2798                if type(parent) is int: return # is this the toplevel in tree?
2799                prev = self.PatternTree.GetPrevSibling(parent)
2800                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2801                if NewId:
2802                    self.PatternTree.Collapse(parent)
2803                    self.PatternTree.Expand(prev)
2804                    self.oldFocus = wx.Window.FindFocus()
2805                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2806                else:
2807                    wx.CallAfter(self.PatternTree.SelectItem,item)
2808            elif sys.platform == "win32":   
2809                self.PatternTree.GetPrevSibling(item)
2810                self.PatternTree.SelectItem(item)
2811            else:
2812                item = self.PatternTree.GetPrevSibling(item)
2813                if item.IsOk(): self.PatternTree.SelectItem(item)
2814        elif key == wx.WXK_DOWN:
2815            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2816                next = self.PatternTree.GetNextSibling(parent)
2817                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2818                if NewId:
2819                    self.PatternTree.Collapse(parent)
2820                    self.PatternTree.Expand(next)
2821                    self.oldFocus = wx.Window.FindFocus()
2822                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2823                else:
2824                    wx.CallAfter(self.PatternTree.SelectItem,item)
2825            elif sys.platform == "win32":   
2826                self.PatternTree.GetNextSibling(item)
2827                self.PatternTree.SelectItem(item)
2828            else:   
2829                item = self.PatternTree.GetNextSibling(item)
2830                if item.IsOk(): self.PatternTree.SelectItem(item)
2831               
2832    def OnReadPowderPeaks(self,event):
2833        'Bound to menu Data/Read Powder Peaks'
2834        self.CheckNotebook()
2835        pth = G2G.GetImportPath(self)
2836        if not pth: pth = '.'
2837        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2838            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2839        try:
2840            if dlg.ShowModal() == wx.ID_OK:
2841                self.HKL = []
2842                self.powderfile = dlg.GetPath()
2843                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2844                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2845                data = ['PKS',wave,0.0]
2846                names = ['Type','Lam','Zero'] 
2847                codes = [0,0,0]
2848                inst = [G2IO.makeInstDict(names,data,codes),{}]
2849                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2850                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2851                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2852                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2853                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2854                self.PatternTree.Expand(Id)
2855                self.PatternTree.SelectItem(Id)
2856                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2857        finally:
2858            dlg.Destroy()
2859                       
2860    def OnImageRead(self,event):
2861        '''Called to read in an image in any known format. *** Depreciated. ***
2862        '''
2863        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2864
2865    def CheckNotebook(self):
2866        '''Make sure the data tree has the minimally expected controls.
2867        '''
2868        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2869            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2870            self.PatternTree.SetItemPyData(sub,[''])
2871        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2872            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2873            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2874        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2875            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2876            self.PatternTree.SetItemPyData(sub,{})
2877        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2878            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2879            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2880        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2881            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2882            self.PatternTree.SetItemPyData(sub,{})
2883        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2884            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2885            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2886                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2887               
2888    class CopyDialog(wx.Dialog):
2889        '''Creates a dialog for copying control settings between
2890        data tree items'''
2891        def __init__(self,parent,title,text,data):
2892            wx.Dialog.__init__(self,parent,-1,title, 
2893                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2894            self.data = data
2895            panel = wx.Panel(self)
2896            mainSizer = wx.BoxSizer(wx.VERTICAL)
2897            topLabl = wx.StaticText(panel,-1,text)
2898            mainSizer.Add((10,10),1)
2899            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2900            mainSizer.Add((10,10),1)
2901            ncols = len(data)/40+1
2902            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2903            for id,item in enumerate(self.data):
2904                ckbox = wx.CheckBox(panel,id,item[1])
2905                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2906                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2907            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2908            OkBtn = wx.Button(panel,-1,"Ok")
2909            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2910            cancelBtn = wx.Button(panel,-1,"Cancel")
2911            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2912            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2913            btnSizer.Add((20,20),1)
2914            btnSizer.Add(OkBtn)
2915            btnSizer.Add((20,20),1)
2916            btnSizer.Add(cancelBtn)
2917            btnSizer.Add((20,20),1)
2918           
2919            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2920            panel.SetSizer(mainSizer)
2921            panel.Fit()
2922            self.Fit()
2923       
2924        def OnCopyChange(self,event):
2925            id = event.GetId()
2926            self.data[id][0] = self.FindWindowById(id).GetValue()       
2927           
2928        def OnOk(self,event):
2929            parent = self.GetParent()
2930            parent.Raise()
2931            self.EndModal(wx.ID_OK)             
2932           
2933        def OnCancel(self,event):
2934            parent = self.GetParent()
2935            parent.Raise()
2936            self.EndModal(wx.ID_CANCEL)             
2937           
2938        def GetData(self):
2939            return self.data
2940       
2941    class SumDialog(wx.Dialog):
2942        '''Allows user to supply scale factor(s) when summing data -
2943        TODO: CAN WE PREVIEW RESULT HERE?'''
2944        def __init__(self,parent,title,text,dataType,data,dataList):
2945            wx.Dialog.__init__(self,parent,-1,title,size=(400,250),
2946                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
2947            self.plotFrame = wx.Frame(self,-1,'Sum Plots',size=wx.Size(700,600), \
2948                style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2949            self.G2plotNB = G2plt.G2PlotNoteBook(self.plotFrame,G2frame=self)
2950            self.data = data
2951            self.dataList = dataList
2952            self.dataType = dataType
2953            size = (450,350)
2954            panel = wxscroll.ScrolledPanel(self, wx.ID_ANY,size=size,
2955                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
2956            mainSizer = wx.BoxSizer(wx.VERTICAL)
2957            topLabl = wx.StaticText(panel,-1,text)
2958            mainSizer.Add((10,10),1)
2959            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2960            mainSizer.Add((10,10),1)
2961            self.dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2962            for id,item in enumerate(self.data[:-1]):
2963                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(300,20))
2964                name.SetEditable(False)
2965#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2966                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2967                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2968                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2969                self.dataGridSizer.Add(scale,0,wx.LEFT,10)
2970                self.dataGridSizer.Add(name,0,wx.RIGHT,10)
2971            if self.dataType:
2972                self.dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+self.dataType),0, \
2973                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2974                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(300,20),style=wx.TE_PROCESS_ENTER)
2975                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2976                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2977                self.dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2978                self.dataGridSizer.Add(wx.StaticText(panel,label='All scales value: '),0,  \
2979                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2980#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2981                allScale = wx.TextCtrl(panel,value='',style=wx.TE_PROCESS_ENTER)
2982                allScale.Bind(wx.EVT_TEXT_ENTER,self.OnAllScale)
2983                allScale.Bind(wx.EVT_KILL_FOCUS,self.OnAllScale)
2984                self.dataGridSizer.Add(allScale,0,WACV)
2985            mainSizer.Add(self.dataGridSizer,0,wx.EXPAND)
2986            OkBtn = wx.Button(panel,-1,"Ok")
2987            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2988            cancelBtn = wx.Button(panel,-1,"Cancel")
2989            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2990            btnSizer = wx.FlexGridSizer(0,3,10,20)
2991            if self.dataType =='PWDR':
2992                TestBtn = wx.Button(panel,-1,"Test")
2993                TestBtn.Bind(wx.EVT_BUTTON, self.OnTest)
2994                btnSizer.Add(TestBtn)
2995            btnSizer.Add(OkBtn)
2996            btnSizer.Add(cancelBtn)
2997           
2998            panel.SetSizer(mainSizer)
2999            panel.SetAutoLayout(1)
3000            panel.SetupScrolling()
3001            mainSizer.Add((10,10),1)
3002            mainSizer.Add(btnSizer,0,wx.CENTER)
3003            panel.SetSizer(mainSizer)
3004            panel.Fit()
3005            self.Fit()
3006
3007        def OnScaleChange(self,event):
3008            event.Skip()
3009            id = event.GetId()
3010            value = self.FindWindowById(id).GetValue()
3011            try:
3012                self.data[id][0] = float(value)
3013                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
3014            except ValueError:
3015                if value and '-' not in value[0]:
3016                    print 'bad input - numbers only'
3017                    self.FindWindowById(id).SetValue('0.000')
3018                   
3019        def OnAllScale(self,event):
3020            event.Skip()
3021            id = event.GetId()
3022            try:
3023                scale = float(self.FindWindowById(id).GetValue())
3024                self.FindWindowById(id).SetValue('%.3f'%(scale))
3025                entries = self.dataGridSizer.GetChildren()
3026                for i,item in enumerate(self.data[:-1]):
3027                    item[0] = scale
3028                    entries[2*i].GetWindow().SetValue('%.3f'%(scale))
3029                 
3030            except ValueError:
3031                print 'bad input - numbers only'
3032                self.FindWindowById(id).SetValue('')
3033                   
3034           
3035        def OnNameChange(self,event):
3036            event.Skip()
3037            self.data[-1] = self.name.GetValue()
3038           
3039        def OnTest(self,event):
3040            lenX = 0
3041            Xminmax = [0,0]
3042            XY = []
3043            Xsum = []
3044            Ysum = []
3045            Vsum = []
3046            result = self.data
3047            for i,item in enumerate(result[:-1]):
3048                scale,name = item
3049                data = self.dataList[i]
3050                if scale:
3051                    x,y,w,yc,yb,yd = data   #numpy arrays!
3052                    XY.append([x,scale*y])
3053                    v = 1./w
3054                    if lenX:
3055                        if lenX != len(x):
3056                            self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
3057                                '\nExpected:'+str(lenX)+ \
3058                                '\nFound:   '+str(len(x))+'\nfor '+name)
3059                            self.OnCancel(event)
3060                    else:
3061                        lenX = len(x)
3062                    if Xminmax[1]:
3063                        if Xminmax != [x[0],x[-1]]:
3064                            self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
3065                                '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
3066                                '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
3067                            self.OnCancel(event)
3068                        else:
3069                            for j,yi in enumerate(y):
3070                                 Ysum[j] += scale*yi
3071                                 Vsum[j] += abs(scale)*v[j]
3072                    else:
3073                        Xminmax = [x[0],x[-1]]
3074                        Xsum = x
3075                        Ysum = scale*y
3076                        Vsum = abs(scale*v)
3077            Wsum = 1./np.array(Vsum)
3078            YCsum = np.zeros(lenX)
3079            YBsum = np.zeros(lenX)
3080            YDsum = np.zeros(lenX)
3081            XY.append([Xsum,Ysum])
3082            self.result = [Xsum,Ysum,Wsum,YCsum,YBsum,YDsum]
3083            # N.B. PlotXY expects the first arg to point to G2frame. In this case, we
3084            # create a duplicate (temporary) Plot notebook window that is a child of the
3085            # modal SumDialog dialog (self). This nicely gets deleted when the dialog is destroyed,
3086            # but the plot window is not fully functional, at least on the Mac.
3087            G2plt.PlotXY(self,XY,lines=True,Title='Sum:'+self.data[-1],labelY='Intensity',)
3088            self.plotFrame.Show()
3089                       
3090        def OnOk(self,event):
3091            if self.dataType == 'PWDR': self.OnTest(event)
3092            parent = self.GetParent()
3093            parent.Raise()
3094            self.EndModal(wx.ID_OK)             
3095           
3096        def OnCancel(self,event):
3097            parent = self.GetParent()
3098            parent.Raise()
3099            self.EndModal(wx.ID_CANCEL)             
3100           
3101        def GetData(self):
3102            if self.dataType == 'PWDR':
3103                return self.data,self.result
3104            else:
3105                return self.data
3106                       
3107    def OnPwdrSum(self,event):
3108        'Sum together powder data(?)'
3109        TextList = []
3110        DataList = []
3111        Names = []
3112        Inst = None
3113        Comments = ['Sum equals: \n']
3114        if self.PatternTree.GetCount():
3115            item, cookie = self.PatternTree.GetFirstChild(self.root)
3116            while item:
3117                name = self.PatternTree.GetItemText(item)
3118                Names.append(name)
3119                if 'PWDR' in name:
3120                    TextList.append([0.0,name])
3121                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
3122                    if not Inst:
3123                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
3124                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3125            if len(TextList) < 2:
3126                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
3127                return
3128            TextList.append('default_sum_name')               
3129            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList,DataList)
3130            try:
3131                if dlg.ShowModal() == wx.ID_OK:
3132                    result,sumData = dlg.GetData()
3133                    Xsum,Ysum,Wsum,YCsum,YBsum,YDsum = sumData
3134                    Xminmax = [Xsum[0],Xsum[-1]]
3135                    outname = 'PWDR '+result[-1]
3136                    Id = 0
3137                    if outname in Names:
3138                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
3139                        try:
3140                            if dlg2.ShowModal() == wx.ID_OK:
3141                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
3142                                self.PatternTree.Delete(Id)
3143                        finally:
3144                            dlg2.Destroy()
3145                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
3146                    if Id:
3147                        Sample = G2obj.SetDefaultSample()
3148                        Ymin = np.min(Ysum)
3149                        Ymax = np.max(Ysum)
3150                        valuesdict = {
3151                            'wtFactor':1.0,
3152                            'Dummy':False,
3153                            'ranId':ran.randint(0,sys.maxint),
3154                            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
3155                            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
3156                            }
3157                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
3158                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
3159                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
3160                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
3161                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
3162                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
3163                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
3164                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
3165                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
3166                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
3167                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
3168                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
3169                        self.PatternTree.SelectItem(Id)
3170                        self.PatternTree.Expand(Id)
3171            finally:
3172                dlg.Destroy()
3173
3174    def OnImageSum(self,event):
3175        'Sum together image data'
3176        TextList = []
3177        DataList = []
3178        IdList = []
3179        Names = []
3180        Comments = ['Sum equals: \n']
3181        if self.PatternTree.GetCount():
3182            item, cookie = self.PatternTree.GetFirstChild(self.root)
3183            while item:
3184                name = self.PatternTree.GetItemText(item)
3185                Names.append(name)
3186                if 'IMG' in name:
3187                    TextList.append([0.0,name])
3188                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
3189                    IdList.append(item)
3190                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
3191                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3192            if len(TextList) < 2:
3193                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
3194                return
3195            TextList.append('default_sum_name')               
3196            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList,DataList)
3197            try:
3198                if dlg.ShowModal() == wx.ID_OK:
3199                    imSize = 0
3200                    result = dlg.GetData()
3201                    First = True
3202                    Found = False
3203                    for i,item in enumerate(result[:-1]):
3204                        scale,name = item
3205                        if scale:
3206                            Found = True                               
3207                            Comments.append("%10.3f %s" % (scale,' * '+name))
3208                            Npix,imagefile,imagetag = DataList[i]
3209                            imagefile = G2IO.GetCheckImageFile(self,IdList[i])[1]
3210                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
3211                            if First:
3212                                newImage = np.zeros_like(image)
3213                                First = False
3214                            if imSize:
3215                                if imSize != Npix:
3216                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
3217                                        '\nExpected:'+str(imSize)+ \
3218                                        '\nFound:   '+str(Npix)+'\nfor '+name)
3219                                    return
3220                                newImage = newImage+scale*image
3221                            else:
3222                                imSize = Npix
3223                                newImage = newImage+scale*image
3224                            del(image)
3225                    if not Found:
3226                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
3227                        return
3228                       
3229                       
3230                    newImage = np.array(newImage,dtype=np.int32)                       
3231                    outname = 'IMG '+result[-1]
3232                    Id = 0
3233                    if outname in Names:
3234                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
3235                        try:
3236                            if dlg2.ShowModal() == wx.ID_OK:
3237                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
3238                        finally:
3239                            dlg2.Destroy()
3240                    else:
3241                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
3242                    if Id:
3243                        pth = G2G.GetExportPath(self)
3244                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth,outname.split('IMG ')[1], 
3245                            'G2img files (*.G2img)|*.G2img', 
3246                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
3247                        if dlg.ShowModal() == wx.ID_OK:
3248                            newimagefile = dlg.GetPath()
3249                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
3250                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
3251                            Imax = np.amax(newImage)
3252                            Imin = np.amin(newImage)
3253                            newImage = []
3254                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
3255                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
3256                        del(newImage)
3257                        if self.imageDefault:
3258                            Data = copy.copy(self.imageDefault)
3259                        Data['formatName'] = 'GSAS-II image'
3260                        Data['showLines'] = True
3261                        Data['ring'] = []
3262                        Data['rings'] = []
3263                        Data['cutoff'] = 10
3264                        Data['pixLimit'] = 20
3265                        Data['ellipses'] = []
3266                        Data['calibrant'] = ''
3267                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
3268                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
3269                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
3270                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
3271                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
3272                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
3273                        self.PatternTree.SelectItem(Id)
3274                        self.PatternTree.Expand(Id)
3275                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
3276                        self.Image = self.PickId
3277            finally:
3278                dlg.Destroy()
3279                     
3280    def OnAddPhase(self,event):
3281        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
3282        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3283            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
3284        else:
3285            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3286        PhaseName = ''
3287        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
3288            style=wx.OK)
3289        if dlg.ShowModal() == wx.ID_OK:
3290            PhaseName = dlg.GetValue()
3291        dlg.Destroy()
3292        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
3293        E,SGData = G2spc.SpcGroup('P 1')
3294        self.PatternTree.SetItemPyData(sub,G2obj.SetNewPhase(Name=PhaseName,SGData=SGData))
3295        G2gd.SelectDataTreeItem(self,sub) #bring up new phase General tab
3296       
3297    def OnDeletePhase(self,event):
3298        'Delete a phase from the tree. Called by Data/Delete Phase menu'
3299        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
3300        if self.dataFrame:
3301            self.dataFrame.Clear() 
3302        TextList = []
3303        DelList = []
3304        DelItemList = []
3305        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3306            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3307        else:
3308            return
3309        if sub:
3310            item, cookie = self.PatternTree.GetFirstChild(sub)
3311            while item:
3312                TextList.append(self.PatternTree.GetItemText(item))
3313                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
3314            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
3315            try:
3316                if dlg.ShowModal() == wx.ID_OK:
3317                    result = dlg.GetSelections()
3318                    for i in result: DelList.append([i,TextList[i]])
3319                    item, cookie = self.PatternTree.GetFirstChild(sub)
3320                    i = 0
3321                    while item:
3322                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
3323                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3324                        i += 1
3325                    for item in DelItemList:
3326                        name = self.PatternTree.GetItemText(item)
3327                        self.PatternTree.Delete(item)
3328                        self.G2plotNB.Delete(name)
3329                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3330                    while item:
3331                        name = self.PatternTree.GetItemText(item)
3332                        if 'PWDR' in name:
3333                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
3334                            refList = self.PatternTree.GetItemPyData(Id)
3335                            if len(refList):
3336                                for i,item in DelList:
3337                                    if item in refList:
3338                                        del(refList[item])
3339#                            self.PatternTree.SetItemPyData(Id,refList)
3340                        elif 'HKLF' in name:
3341                            data = self.PatternTree.GetItemPyData(item)
3342                            data[0] = {}
3343#                            self.PatternTree.SetItemPyData(item,data)
3344                           
3345                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3346            finally:
3347                dlg.Destroy()
3348               
3349    def OnRenameData(self,event):
3350        'Renames an existing phase. Called by Data/Rename Phase menu'
3351        name = self.PatternTree.GetItemText(self.PickId)     
3352        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
3353            if 'Bank' in name:
3354                names = name.split('Bank')
3355                names[1] = ' Bank'+names[1]
3356            elif 'Azm' in name:
3357                names = name.split('Azm')
3358                names[1] = ' Azm'+names[1]
3359            else:
3360                names = [name,'']
3361            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
3362            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
3363                defaultValue=names[0][names[0].index(' ')+1:])
3364            try:
3365                if dlg.ShowModal() == wx.ID_OK:
3366                    name = dataType+dlg.GetValue()+names[1]
3367                    self.PatternTree.SetItemText(self.PickId,name)
3368            finally:
3369                dlg.Destroy()
3370       
3371    def GetFileList(self,fileType,skip=None):        #potentially useful?
3372        'Appears unused. Note routine of same name in GSASIIpwdGUI'
3373        fileList = []
3374        Source = ''
3375        id, cookie = self.PatternTree.GetFirstChild(self.root)
3376        while id:
3377            name = self.PatternTree.GetItemText(id)
3378            if fileType in name:
3379                if id == skip:
3380                    Source = name
3381                else:
3382                    fileList.append([False,name,id])
3383            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3384        if skip:
3385            return fileList,Source
3386        else:
3387            return fileList
3388           
3389    def OnDataDelete(self, event):
3390        '''Delete one or more histograms from data tree. Called by the
3391        Data/DeleteData menu
3392        '''
3393        TextList = []
3394        DelList = []
3395        DelItemList = []
3396        nItems = {'PWDR':0,'SASD':0,'REFD':0,'IMG':0,'HKLF':0,'PDF':0}
3397        PDFnames = []
3398        if self.PatternTree.GetCount():
3399            item, cookie = self.PatternTree.GetFirstChild(self.root)
3400            while item:
3401                name = self.PatternTree.GetItemText(item)
3402                if name not in ['Notebook','Controls','Covariance','Constraints',
3403                    'Restraints','Phases','Rigid bodies'] and 'Sequential' not in name:
3404                    if 'PWDR' in name[:4]: nItems['PWDR'] += 1
3405                    if 'SASD' in name[:4]: nItems['SASD'] += 1
3406                    if 'REFD' in name[:4]: nItems['REFD'] += 1
3407                    if 'IMG' in name[:3]:  nItems['IMG'] += 1
3408                    if 'HKLF' in name[:4]: nItems['HKLF'] += 1
3409                    if 'PDF' in name[:3]:
3410                        PDFnames.append(name)
3411                        nItems['PDF'] += 1
3412                    TextList.append(name)
3413                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3414            for pdfName in PDFnames:
3415                try:
3416                    TextList.remove('PWDR'+pdfName[4:])
3417                except ValueError:
3418                    print 'PWDR'+pdfName[4:]+' for '+pdfName+' not found'
3419            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
3420            try:
3421                if dlg.ShowModal() == wx.ID_OK:
3422                    result = dlg.GetSelections()
3423                    for i in result: DelList.append(TextList[i])
3424                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3425                    while item:
3426                        itemName = self.PatternTree.GetItemText(item)
3427                        if itemName in DelList:
3428                            if 'PWDR' in itemName[:4]: nItems['PWDR'] -= 1
3429                            elif 'SASD' in itemName[:4]: nItems['SASD'] -= 1
3430                            elif 'REFD' in itemName[:4]: nItems['REFD'] -= 1
3431                            elif 'IMG' in itemName[:3]: nItems['IMG'] -= 1
3432                            elif 'HKLF' in itemName[:4]: nItems['HKLF'] -= 1
3433                            elif 'PDF' in itemName[:3]: nItems['PDF'] -= 1
3434                            DelItemList.append(item)
3435                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3436                    for item in DelItemList:
3437                        self.PatternTree.Delete(item)
3438                    self.PickId = 0
3439                    self.PickIdText = None
3440                    self.PatternId = 0
3441                    if nItems['PWDR']:
3442                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3443                    else:
3444                        self.G2plotNB.Delete('Powder Patterns')
3445                    if not nItems['IMG']:
3446                        self.G2plotNB.Delete('2D Powder Image')
3447                    if not nItems['HKLF']:
3448                        self.G2plotNB.Delete('Structure Factors')
3449                        if '3D Structure Factors' in self.G2plotNB.plotList:
3450                            self.G2plotNB.Delete('3D Structure Factors')
3451            finally:
3452                dlg.Destroy()
3453
3454    def OnFileOpen(self, event, filename=None):
3455        '''Gets a GSAS-II .gpx project file in response to the
3456        File/Open Project menu button
3457        '''
3458        result = wx.ID_OK
3459        self.EnablePlot = False
3460        if self.PatternTree.GetChildrenCount(self.root,False):
3461            if self.dataFrame:
3462                self.dataFrame.ClearData() 
3463            dlg = wx.MessageDialog(
3464                self,
3465                'Do you want to overwrite the current project? '+
3466                'Any unsaved changes in current project will be lost. Press OK to continue.',
3467                'Overwrite?',  wx.OK | wx.CANCEL)
3468            try:
3469                result = dlg.ShowModal()
3470                if result == wx.ID_OK:
3471                    self.PatternTree.DeleteChildren(self.root)
3472                    self.GSASprojectfile = ''
3473                    self.HKL = []
3474                    if self.G2plotNB.plotList:
3475                        self.G2plotNB.clear()
3476            finally:
3477                dlg.Destroy()
3478        if result != wx.ID_OK: return
3479
3480        if not filename:
3481            if self.LastGPXdir:
3482                pth = self.LastGPXdir
3483            else:
3484                pth = '.'
3485            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3486                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3487            try:
3488                if dlg.ShowModal() != wx.ID_OK: return
3489                self.GSASprojectfile = dlg.GetPath()
3490                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3491                self.dirname = dlg.GetDirectory()
3492            finally:
3493                dlg.Destroy()
3494        else:
3495            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3496            self.dirname = os.path.split(filename)[0]
3497
3498        try:
3499            self.StartProject()         #open the file if possible
3500        except:
3501            print '\nError opening file ',filename
3502            import traceback
3503            print traceback.format_exc()
3504       
3505    def StartProject(self):
3506        '''Opens a GSAS-II project file & selects the 1st available data set to
3507        display (PWDR, HKLF, REFD or SASD)
3508        '''
3509       
3510        Id = 0
3511        phaseId = None
3512        G2IO.ProjFileOpen(self)
3513        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3514        self.PatternTree.Expand(self.root)
3515        self.HKL = []
3516        item, cookie = self.PatternTree.GetFirstChild(self.root)
3517        while item and not Id:
3518            name = self.PatternTree.GetItemText(item)
3519            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD','REFD']:
3520                Id = item
3521            elif name == "Phases":
3522                phaseId = item
3523            elif name == 'Controls':
3524                data = self.PatternTree.GetItemPyData(item)
3525                if data:
3526                    for item in self.Refine: item.Enable(True)
3527                    self.EnableSeqRefineMenu()
3528            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3529        if phaseId: # show all phases
3530            self.PatternTree.Expand(phaseId)
3531        if Id:
3532            self.EnablePlot = True
3533            self.PatternTree.SelectItem(Id)
3534            self.PatternTree.Expand(Id)
3535        elif phaseId:
3536            self.PatternTree.SelectItem(phaseId)
3537        self.CheckNotebook()
3538        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3539        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3540        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3541        self.LastGPXdir = pth
3542
3543    def OnFileClose(self, event):
3544        '''Clears the data tree in response to the
3545        File/New Project menu button. User is given option to save
3546        the project.
3547        '''
3548#        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3549        try:
3550            result = dlg.ShowModal()
3551            if result == wx.ID_OK:
3552                self.OnFileSaveMenu(event)
3553            if result != wx.ID_CANCEL:
3554                self.GSASprojectfile = ''
3555                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3556                self.PatternTree.DeleteChildren(self.root)
3557                if self.HKL: self.HKL = []
3558                if self.G2plotNB.plotList:
3559                    self.G2plotNB.clear()
3560        finally:
3561            dlg.Destroy()
3562
3563    def OnFileSave(self, event):
3564        '''Save the current project in response to the
3565        File/Save Project menu button
3566        '''
3567       
3568        if self.GSASprojectfile: 
3569            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3570            self.CheckNotebook()
3571            G2IO.ProjFileSave(self)
3572        else:
3573            self.OnFileSaveas(event)
3574
3575    def OnFileSaveas(self, event):
3576        '''Save the current project in response to the
3577        File/Save as menu button
3578        '''
3579        if GSASIIpath.GetConfigValue('Starting_directory'):
3580            pth = GSASIIpath.GetConfigValue('Starting_directory')
3581            pth = os.path.expanduser(pth) 
3582        elif self.LastGPXdir:
3583            pth = self.LastGPXdir
3584        else:
3585            pth = '.'
3586        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3587            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3588        try:
3589            if dlg.ShowModal() == wx.ID_OK:
3590                self.GSASprojectfile = dlg.GetPath()
3591                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3592                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3593                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3594                self.CheckNotebook()
3595                G2IO.ProjFileSave(self)
3596                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3597        finally:
3598            dlg.Destroy()
3599           
3600    def ExpandAll(self,event):
3601        '''Expand all tree items or those of a single type
3602        '''
3603        txt = self.GetMenuBar().GetLabel(event.Id)
3604        if txt == 'all':
3605            self.ExpandingAll = True
3606            try:
3607                self.PatternTree.ExpandAll()
3608            finally:
3609                self.ExpandingAll = False
3610        else:
3611            self.ExpandingAll = True
3612            try:
3613                item, cookie = self.PatternTree.GetFirstChild(self.root)
3614                while item:
3615                    name = self.PatternTree.GetItemText(item)
3616                    if name.startswith(txt+' '): self.PatternTree.Expand(item)
3617                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3618            finally:
3619                self.ExpandingAll = False
3620
3621    def MoveTreeItems(self,event):
3622        '''Move tree items of a single type to the end of the tree
3623        '''
3624        txt = self.GetMenuBar().GetLabel(event.Id)
3625        # make a list of items to copy
3626        copyList = []
3627        item, cookie = self.PatternTree.GetFirstChild(self.root)
3628        while item:
3629            if self.PatternTree.GetItemText(item).startswith(txt+' '):
3630                copyList.append(item)
3631            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3632       
3633        self.ExpandingAll = True
3634        try:
3635            for item in copyList:
3636                name = self.PatternTree.GetItemText(item)
3637                newId = self.PatternTree.AppendItem(self.root,name)
3638                self.PatternTree.SetItemPyData(newId,self.PatternTree.GetItemPyData(item))
3639                chld, chldcookie = self.PatternTree.GetFirstChild(item)
3640                while chld:
3641                    chname = self.PatternTree.GetItemText(chld)
3642                    newCh = self.PatternTree.AppendItem(newId,chname)
3643                    self.PatternTree.SetItemPyData(newCh,self.PatternTree.GetItemPyData(chld))
3644                    chld, chldcookie = self.PatternTree.GetNextChild(item, chldcookie)
3645                self.PatternTree.Delete(item)
3646        finally:
3647            self.ExpandingAll = False
3648        G2gd.SelectDataTreeItem(self,self.root)
3649           
3650    def ExitMain(self, event):
3651        '''Called if the main window is closed'''
3652        if self.G2plotNB:
3653            self.G2plotNB.Destroy()
3654        if self.dataFrame:
3655            self.dataFrame.Destroy()
3656        if self.undofile:
3657            os.remove(self.undofile)
3658        sys.exit()
3659       
3660    def OnFileExit(self, event):
3661        '''Called in response to the File/Quit menu button'''
3662        if self.G2plotNB:
3663            self.G2plotNB.Destroy()
3664        if self.dataFrame:
3665            self.dataFrame.Destroy()
3666        self.Close()
3667       
3668    def OnExportPeakList(self,event):
3669        nptand = lambda x: np.tan(x*math.pi/180.)
3670        pth = G2G.GetExportPath(self)
3671        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3672            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3673        try:
3674            if dlg.ShowModal() == wx.ID_OK:
3675                self.peaklistfile = dlg.GetPath()
3676                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3677                file = open(self.peaklistfile,'w')               
3678                item, cookie = self.PatternTree.GetFirstChild(self.root)
3679                while item:
3680                    name = self.PatternTree.GetItemText(item)
3681                    if 'PWDR' in name:
3682                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3683                        wave = 0.0
3684                        while item2:
3685                            name2 = self.PatternTree.GetItemText(item2)
3686                            if name2 == 'Instrument Parameters':
3687                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3688                                Type = Inst['Type'][0]
3689                                if 'T' not in Type:
3690                                    wave = G2mth.getWave(Inst)
3691                            elif name2 == 'Peak List':
3692                                pkdata = self.PatternTree.GetItemPyData(item2)
3693                                peaks = pkdata['peaks']
3694                                sigDict = pkdata['sigDict']
3695                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3696                        file.write("#%s \n" % (name+' Peak List'))
3697                        if wave:
3698                            file.write('#wavelength = %10.6f\n'%(wave))
3699                        if 'T' in Type:
3700                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3701                        else:
3702                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3703                        for ip,peak in enumerate(peaks):
3704                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3705                            if 'T' in Type:  #TOF - more cols
3706                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3707                                for name in esds.keys():
3708                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3709                                sig = np.sqrt(peak[8])
3710                                gam = peak[10]
3711                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3712                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3713                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3714                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3715                            else:               #CW
3716                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3717                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3718                                for name in esds.keys():
3719                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3720                                sig = np.sqrt(peak[4]) #var -> sig
3721                                gam = peak[6]
3722                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3723                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3724                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3725                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3726                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3727                file.close()
3728        finally:
3729            dlg.Destroy()
3730       
3731    def OnExportHKL(self,event):
3732        pth = G2G.GetExportPath(self)
3733        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3734            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3735        try:
3736            if dlg.ShowModal() == wx.ID_OK:
3737                self.peaklistfile = dlg.GetPath()
3738                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3739                file = open(self.peaklistfile,'w')               
3740                item, cookie = self.PatternTree.GetFirstChild(self.root)
3741                while item:
3742                    name = self.PatternTree.GetItemText(item)
3743                    if 'PWDR' in name:
3744                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3745                        while item2:
3746                            name2 = self.PatternTree.GetItemText(item2)
3747                            if name2 == 'Reflection Lists':
3748                                data = self.PatternTree.GetItemPyData(item2)
3749                                phases = data.keys()
3750                                for phase in phases:
3751                                    peaks = data[phase]
3752                                    I100 = peaks['RefList'].T[8]*np.array([refl[11] for refl in peaks['RefList']])
3753                                    Imax = np.max(I100)
3754                                    if Imax:
3755                                        I100 *= 100.0/Imax
3756                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3757                                    if 'T' in peaks.get('Type','PXC'):
3758                                        file.write('%s \n'%('   h   k   l   m   d-space       TOF       wid     Fo**2     Fc**2     Icorr      Prfo     Trans      ExtP      I100'))
3759                                    else:               
3760                                        file.write('%s \n'%('   h   k   l   m   d-space   2-theta       wid     Fo**2     Fc**2     Icorr      Prfo     Trans      ExtP      I100'))
3761                                    for ipk,peak in enumerate(peaks['RefList']):
3762                                        if 'T' in peaks.get('Type','PXC'):
3763                                            sig = np.sqrt(peak[6])
3764                                            gam = peak[7]
3765                                            FWHM = G2pwd.getgamFW(gam,sig)
3766                                            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" % \
3767                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8],
3768                                                peak[9],peak[11],peak[12],peak[13],peak[14],I100[ipk]))
3769                                        else:
3770                                            sig = np.sqrt(peak[6])
3771                                            gam = peak[7]
3772                                            FWHM = G2pwd.getgamFW(gam,sig)
3773                                            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" % \
3774                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,
3775                                                peak[8],peak[9],peak[11],peak[12],peak[13],peak[14],I100[ipk]))
3776                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3777                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3778                file.close()
3779        finally:
3780            dlg.Destroy()
3781       
3782    def OnExportPDF(self,event):
3783        #need S(Q) and G(R) to be saved here - probably best from selection?
3784        names = G2pdG.GetFileList(self,'PDF')
3785        exports = []
3786        if names:
3787            od = {'label_1':'Export I(Q)','value_1':False,'label_2':'Export S(Q)','value_2':False,
3788                  'label_3':'Export F(Q)','value_3':False,'label_4':'Export G(R)','value_4':True,
3789                  'label_5':'Make G(R) for pdfGUI','value_5':False}
3790            dlg = G2G.G2MultiChoiceDialog(self,'Select','PDF patterns to export',names,extraOpts=od)
3791            if dlg.ShowModal() == wx.ID_OK:
3792                sel = dlg.GetSelections()
3793                for x in sel:
3794                    exports.append(names[x])
3795            dlg.Destroy()
3796        if exports:
3797            PDFsaves = [od['value_1'],od['value_2'],od['value_3'],od['value_4'],od['value_5']]
3798            G2IO.PDFSave(self,exports,PDFsaves)
3799       
3800    def OnMakePDFs(self,event):
3801        '''Sets up PDF data structure filled with defaults; if found chemical formula is inserted
3802        so a default PDF can be made.
3803        '''
3804        sind = lambda x: math.sin(x*math.pi/180.)
3805        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3806        tof2q = lambda t,C:2.0*math.pi*C/t
3807        TextList = []
3808        ElLists = []
3809        Qlimits = []
3810        Names = []
3811        if self.PatternTree.GetCount():
3812            id, cookie = self.PatternTree.GetFirstChild(self.root)
3813            while id:
3814                name = self.PatternTree.GetItemText(id)
3815                Names.append(name)
3816                if 'PWDR' in name:
3817                    TextList.append(name)
3818                    Data = self.PatternTree.GetItemPyData(id)[1]
3819                    pwdrMin = np.min(Data[1])
3820                    Comments = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Comments'))
3821                    Parms = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Instrument Parameters'))[0]
3822                    fullLimits = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Limits'))[0]
3823                    if 'C' in Parms['Type'][0]:
3824                        wave = G2mth.getWave(Parms)
3825                        qMax = tth2q(fullLimits[1],wave)
3826                    else:   #'T'of
3827                        qMax = tof2q(fullLimits[0],Parms['difC'][1])
3828                    Qlimits.append([0.9*qMax,qMax])
3829                    ElList = {}
3830                    sumnum = 1.
3831                    for item in Comments:           #grab chemical formula from Comments
3832                        if 'formula' in item[:15].lower():
3833                            formula = item.split('=')[1].split()
3834                            try:
3835                                elems = formula[::2]
3836                                nums = formula[1::2]
3837                                Formula = zip(elems,nums)
3838                                sumnum = 0.
3839                                for [elem,num] in Formula:
3840                                    ElData = G2elem.GetElInfo(elem,Parms)
3841                                    ElData['FormulaNo'] = float(num)
3842                                    sumnum += float(num)
3843                                    ElList[elem] = ElData
3844                               
3845                            except ValueError:
3846                                ElData = G2elem.GetElInfo(formula[0],Parms)
3847                                ElData['FormulaNo'] = 1.0
3848                                ElList[elem] = ElData
3849                    ElLists.append(ElList)
3850                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3851            if len(TextList) < 1:
3852                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3853                return
3854            dlg = G2G.G2MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3855            try:
3856                if dlg.ShowModal() == wx.ID_OK:
3857                    for i in dlg.GetSelections():
3858                        PDFnames = G2gd.GetPatternTreeDataNames(self,['PDF ',])
3859                        G2obj.CreatePDFitems(self,TextList[i],ElLists[i],Qlimits[i],sumnum,pwdrMin,PDFnames)
3860                for item in self.ExportPDF: item.Enable(True)
3861            finally:
3862                dlg.Destroy()
3863               
3864    def GetPWDRdatafromTree(self,PWDRname):
3865        ''' Returns powder data from GSASII tree
3866
3867        :param str PWDRname: a powder histogram name as obtained from
3868          :meth:`GSASIIstruct.GetHistogramNames`
3869
3870        :returns: PWDRdata = powder data dictionary with
3871          Powder data arrays, Limits, Instrument Parameters,
3872          Sample Parameters           
3873        '''
3874        PWDRdata = {}
3875        try:
3876            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3877        except ValueError:
3878            PWDRdata['wtFactor'] = 1.0
3879        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3880        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3881        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3882        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3883        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3884        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3885        if 'ranId' not in PWDRdata:  # patch, add a random Id
3886            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3887        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3888            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3889        return PWDRdata
3890
3891    def GetHKLFdatafromTree(self,HKLFname):
3892        ''' Returns single crystal data from GSASII tree
3893
3894        :param str HKLFname: a single crystal histogram name as obtained
3895          from
3896          :meth:`GSASIIstruct.GetHistogramNames`
3897
3898        :returns: HKLFdata = single crystal data list of reflections
3899
3900        '''
3901        HKLFdata = {}
3902        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3903#        try:
3904#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3905#        except ValueError:
3906#            HKLFdata['wtFactor'] = 1.0
3907        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3908        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3909        return HKLFdata
3910       
3911    def GetPhaseData(self):
3912        '''Returns a dict with defined phases.
3913        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3914        get same info from GPX file.
3915        '''
3916        phaseData = {}
3917        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3918            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3919        else:
3920            print 'no phases found in GetPhaseData'
3921            sub = None
3922        if sub:
3923            item, cookie = self.PatternTree.GetFirstChild(sub)
3924            while item:
3925                phaseName = self.PatternTree.GetItemText(item)
3926                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3927                if 'ranId' not in phaseData[phaseName]:
3928                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3929                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3930        return phaseData
3931
3932    def GetPhaseInfofromTree(self):
3933        '''Get the phase names and their rId values,
3934        also the histograms used in each phase.
3935
3936        :returns: (phaseRIdList, usedHistograms) where
3937
3938          * phaseRIdList is a list of random Id values for each phase
3939          * usedHistograms is a dict where the keys are the phase names
3940            and the values for each key are a list of the histogram names
3941            used in each phase.
3942        '''
3943        phaseRIdList = []
3944        usedHistograms = {}
3945        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3946        if sub:
3947            item, cookie = self.PatternTree.GetFirstChild(sub)
3948            while item:
3949                phaseName = self.PatternTree.GetItemText(item)
3950                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3951                if ranId: phaseRIdList.append(ranId)
3952                data = self.PatternTree.GetItemPyData(item)
3953                UseList = data['Histograms']
3954                usedHistograms[phaseName] = UseList.keys()
3955                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3956        return phaseRIdList,usedHistograms
3957
3958    def GetPhaseNames(self):
3959        '''Returns a list of defined phases.
3960        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3961        get same info from GPX file.
3962        '''
3963        phaseNames = []
3964        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3965            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3966        else:
3967            print 'no phases found in GetPhaseNames'
3968            sub = None
3969        if sub:
3970            item, cookie = self.PatternTree.GetFirstChild(sub)
3971            while item:
3972                phase = self.PatternTree.GetItemText(item)
3973                phaseNames.append(phase)
3974                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3975        return phaseNames
3976   
3977    def GetHistogramNames(self,hType):
3978        """ Returns a list of histogram names found in the GSASII data tree
3979        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3980        get same info from GPX file.
3981       
3982        :param str hType: list of histogram types
3983        :return: list of histogram names
3984       
3985        """
3986        HistogramNames = []
3987        if self.PatternTree.GetCount():
3988            item, cookie = self.PatternTree.GetFirstChild(self.root)
3989            while item:
3990                name = self.PatternTree.GetItemText(item)
3991                if name[:4] in hType:
3992                    HistogramNames.append(name)       
3993                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3994
3995        return HistogramNames
3996                   
3997    def GetUsedHistogramsAndPhasesfromTree(self):
3998        ''' Returns all histograms that are found in any phase
3999        and any phase that uses a histogram.
4000        This also assigns numbers to used phases and histograms by the
4001        order they appear in the file.
4002        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
4003        get same info from GPX file.
4004
4005        :returns: (Histograms,Phases)
4006
4007            * Histograms = dictionary of histograms as {name:data,...}
4008            * Phases = dictionary of phases that use histograms
4009        '''
4010        Histograms = {}
4011        Phases = {}
4012        phaseNames = self.GetPhaseNames()
4013        phaseData = self.GetPhaseData()
4014        histoList = self.GetHistogramNames(['PWDR','HKLF'])
4015
4016        for phase in phaseData:
4017            Phase = phaseData[phase]
4018            pId = phaseNames.index(phase)
4019            Phase['pId'] = pId
4020            if Phase['Histograms']:
4021                if phase not in Phases:
4022                    Phases[phase] = Phase
4023                for hist in Phase['Histograms']:
4024                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
4025                        Phase['Histograms'][hist]['Use'] = True         
4026                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
4027                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
4028                        if item:
4029                            if 'PWDR' in hist[:4]: 
4030                                Histograms[hist] = self.GetPWDRdatafromTree(item)
4031                            elif 'HKLF' in hist[:4]:
4032                                Histograms[hist] = self.GetHKLFdatafromTree(item)
4033                            hId = histoList.index(hist)
4034                            Histograms[hist]['hId'] = hId
4035                        else: # would happen if a referenced histogram were renamed or deleted
4036                            print('For phase "'+phase+
4037                                  '" unresolved reference to histogram "'+hist+'"')
4038        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
4039        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
4040        return Histograms,Phases
4041       
4042    def MakeLSParmDict(self):
4043        '''Load all parameters used for computation from the tree into a
4044        dict of paired values [value, refine flag]. Note that this is
4045        different than the parmDict used in the refinement, which only has
4046        values.
4047
4048        Note that similar things are done in
4049        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
4050        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
4051        a GPX file).
4052
4053        :returns: (parmDict,varyList) where:
4054
4055         * parmDict is a dict with values and refinement flags
4056           for each parameter and
4057         * varyList is a list of variables (refined parameters).
4058        '''
4059        parmDict = {}
4060        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
4061        for phase in Phases:
4062            if 'pId' not in Phases[phase]:
4063                self.ErrorDialog('View parameter error','You must run least squares at least once')
4064                raise Exception,'No pId for phase '+phase
4065        rigidbodyDict = self.PatternTree.GetItemPyData(   
4066            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
4067        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
4068        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
4069        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
4070        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
4071        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
4072        varyList = rbVary+phaseVary+hapVary+histVary
4073        parmDict.update(rbDict)
4074        parmDict.update(phaseDict)
4075        parmDict.update(hapDict)
4076        parmDict.update(histDict)
4077        for parm in parmDict:
4078            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
4079                'Omega','Chi','Phi','nDebye','nPeaks']:
4080                parmDict[parm] = [parmDict[parm],'-']
4081            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
4082                parmDict[parm] = [parmDict[parm],'-']
4083            elif parm in varyList:
4084                parmDict[parm] = [parmDict[parm],'T']
4085            else:
4086                parmDict[parm] = [parmDict[parm],'F']
4087        # for i in parmDict: print i,'\t',parmDict[i]
4088        # fl = open('parmDict.dat','wb')
4089        # import cPickle
4090        # cPickle.dump(parmDict,fl,1)
4091        # fl.close()
4092        return parmDict,varyList
4093
4094    def OnShowLSParms(self,event):
4095        '''Displays a window showing all parameters in the refinement.
4096        Called from the Calculate/View LS Parms menu.
4097        '''
4098        try:
4099            parmDict,varyList = self.MakeLSParmDict()
4100        except:
4101            print('Error retrieving parameters')
4102            return
4103        parmValDict = {}
4104        for i in parmDict:
4105            parmValDict[i] = parmDict[i][0]
4106           
4107        reqVaryList = tuple(varyList) # save requested variables
4108        try:
4109            # process constraints
4110            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
4111            Constraints = self.PatternTree.GetItemPyData(sub)
4112            constList = []
4113            for item in Constraints:
4114                if item.startswith('_'): continue
4115                constList += Constraints[item]
4116            G2mv.InitVars()
4117            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
4118            groups,parmlist = G2mv.GroupConstraints(constrDict)
4119            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
4120            G2mv.Map2Dict(parmValDict,varyList)
4121        except:
4122            pass
4123        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
4124        dlg.ShowModal()
4125        dlg.Destroy()
4126
4127    def OnRefine(self,event):
4128        '''Perform a refinement.
4129        Called from the Calculate/Refine menu.
4130        '''
4131        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4132        if Id:
4133            dlg = wx.MessageDialog(
4134                self,
4135                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
4136                'Remove sequential results?',wx.OK|wx.CANCEL)
4137            if dlg.ShowModal() == wx.ID_OK:
4138                self.PatternTree.Delete(Id)
4139                dlg.Destroy()
4140            else:
4141                dlg.Destroy()
4142                return
4143        self.OnFileSave(event)
4144        # check that constraints are OK here
4145        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
4146        if errmsg:
4147            self.ErrorDialog('Refinement error',errmsg)
4148            return
4149        if warnmsg:
4150            print('Conflict between refinment flag settings and constraints:\n'+
4151                warnmsg+'\nRefinement not possible')
4152            self.ErrorDialog('Refinement Flag Error',
4153                'Conflict between refinement flag settings and constraints:\n'+
4154                warnmsg+'\nRefinement not possible')
4155            return
4156        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
4157            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
4158            parent=self)
4159        Size = dlg.GetSize()
4160        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
4161            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
4162        dlg.CenterOnParent()
4163        Rw = 100.00
4164        self.SaveTreeSetting()
4165        self.PatternTree.SaveExposedItems()       
4166        try:
4167            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)    #Msg is Rvals dict if Ok=True
4168        finally:
4169            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
4170            dlg.Destroy()
4171            wx.Yield()
4172        if OK:
4173            Rw = Msg['Rwp']
4174            lamMax = Msg.get('lamMax',0.001)
4175            text = 'Load new result?'
4176            if lamMax >= 10.:
4177                text += '\nWARNING: Steepest descents dominates;'+   \
4178                ' minimum may not have been reached\nor result may be false minimum.'+  \
4179                ' You should reconsider your parameter suite'
4180            dlg2 = wx.MessageDialog(self,text,'Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
4181            try:
4182                if dlg2.ShowModal() == wx.ID_OK:
4183                    self.PatternTree.DeleteChildren(self.root)
4184                    self.HKL = []
4185                    G2IO.ProjFileOpen(self,False)
4186                    self.PatternTree.RestoreExposedItems()       
4187                    self.ResetPlots()
4188            finally:
4189                dlg2.Destroy()
4190        else:
4191            self.ErrorDialog('Refinement error',Msg)
4192       
4193    def SaveTreeSetting(self):
4194        'Save the last tree setting'
4195        oldId =  self.PatternTree.GetSelection()        #retain current selection
4196        oldPath = self.GetTreeItemsList(oldId)
4197        self.lastTreeSetting = oldPath
4198        # note that for reasons unclear, it does not seem necessary to reload the Atoms tab
4199        #parentName = ''
4200        #tabId = None
4201        # parentId = self.PatternTree.GetItemParent(oldId)
4202        # if parentId:
4203        #     parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
4204        #     if 'Phases' in parentName:
4205        #         tabId = self.dataDisplay.GetSelection()
4206        #self.lastTreeSetting = oldPath,tabId
4207        #GSASIIpath.IPyBreak()
4208       
4209    def TestResetPlot(self,event):
4210        '''Debug code to test cleaning up plots after a refinement'''
4211        #for i in range(self.G2plotNB.nb.GetPageCount()):
4212        #    [self.G2plotNB.nb.GetPageText(i)
4213        # save current tree item and (if needed) atoms tab
4214        self.SaveTreeSetting()
4215        self.ResetPlots()
4216       
4217    def ResetPlots(self):
4218        '''This reloads the current tree item, often drawing a plot. It refreshes any plots
4219        that have registered a refresh routine (see G2plotNB.RegisterRedrawRoutine)
4220        and deletes all plots that have not been refreshed and
4221        require one (see G2plotNB.SetNoDelete).
4222        '''
4223        lastRaisedPlotTab = self.G2plotNB.lastRaisedPlotTab # save the last page saved
4224        #print 'lastRaisedPlotTab=',lastRaisedPlotTab
4225        self.G2plotNB.lastRaisedPlotTab = None
4226        # mark displayed plots as invalid
4227        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
4228            frame.plotInvalid = True
4229        # reload current tree item, triggering the routine to redraw the data window and possibly a plot
4230        #oldPath,tabId = self.lastTreeSetting
4231        oldPath = self.lastTreeSetting
4232        Id = self.root
4233        for txt in oldPath:
4234            Id = G2gd.GetPatternTreeItemId(self, Id, txt)
4235        self.PickIdText = None  #force reload of page
4236        if Id:
4237            self.PickId = Id
4238            self.PatternTree.SelectItem(Id)
4239        # update other self-updating plots
4240#        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
4241#            if frame.plotInvalid and frame.replotFunction:
4242#                frame.replotFunction(*frame.replotArgs,**frame.replotKWargs)
4243        # delete any remaining plots that are still invalid and need a refresh
4244        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
4245            if frame.plotInvalid and frame.plotRequiresRedraw:
4246                self.G2plotNB.Delete(lbl)
4247        # put the previously last-raised tab on top, if present. If not, use the one corresponding to
4248        # the last tree item to be selected
4249        wx.CallAfter(self.G2plotNB.RaiseLastPage,lastRaisedPlotTab,self.G2plotNB.lastRaisedPlotTab)
4250       
4251    def OnSeqRefine(self,event):
4252        '''Perform a sequential refinement.
4253        Called from the Calculate/Sequential refine menu.
4254        '''
4255        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4256        if not Id:
4257            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
4258            self.PatternTree.SetItemPyData(Id,{})           
4259        self.G2plotNB.Delete('Sequential refinement')    #clear away probably invalid plot
4260        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
4261        if not Controls.get('Seq Data'):
4262            print('Error: a sequential refinement has not been set up')
4263            return
4264        Controls['ShowCell'] = True
4265        self.OnFileSave(event)
4266        # check that constraints are OK here
4267        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
4268        if errmsg:
4269            self.ErrorDialog('Refinement error',errmsg)
4270            return
4271        if warnmsg:
4272            print('Conflict between refinment flag settings and constraints:\n'+
4273                  warnmsg+'\nRefinement not possible')
4274            self.ErrorDialog('Refinement Flag Error',
4275                             'Conflict between refinment flag settings and constraints:\n'+
4276                             warnmsg+'\nRefinement not possible')
4277            return
4278        self.PatternTree.SaveExposedItems()       
4279        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
4280            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
4281            parent=self)           
4282        Size = dlg.GetSize()
4283        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
4284            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
4285        dlg.CenterOnParent()
4286        try:
4287            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)     #Msg is Rvals dict if Ok=True
4288        finally:
4289            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
4290            dlg.Destroy()
4291            wx.Yield()
4292        if OK:
4293            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
4294            try:
4295                if dlg.ShowModal() == wx.ID_OK:
4296                    self.PickIdText = None  #force reload of PickId contents
4297                    self.PatternTree.DeleteChildren(self.root)
4298                    if len(self.HKL): self.HKL = []
4299                    if self.G2plotNB.plotList:
4300                        self.G2plotNB.clear()
4301                    G2IO.ProjFileOpen(self,False)
4302                    self.PatternTree.RestoreExposedItems()
4303                    self.ResetPlots()
4304                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4305                    self.PatternTree.SelectItem(Id)
4306            finally:
4307                dlg.Destroy()
4308            self.SeqTblHideList = []
4309        else:
4310            self.ErrorDialog('Sequential refinement error',Msg)
4311       
4312    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
4313        'Display an error message'
4314        result = None
4315        if parent is None:
4316            dlg = wx.MessageDialog(self, message, title,  wtype)
4317        else:
4318            dlg = wx.MessageDialog(parent, message, title,  wtype)
4319            dlg.CenterOnParent() # not working on Mac
4320        try:
4321            result = dlg.ShowModal()
4322        finally:
4323            dlg.Destroy()
4324        return result
4325   
4326    def OnSaveMultipleImg(self,event):
4327        '''Select and save multiple image parameter and mask files
4328        '''
4329        G2IO.SaveMultipleImg(self)
4330       
4331class GSASIImain(wx.App):
4332    '''Defines a wxApp for GSAS-II
4333
4334    Creates a wx frame (self.main) which contains the display of the
4335    data tree.
4336    '''
4337    def OnInit(self):
4338        '''Called automatically when the app is created.'''
4339        import platform
4340        if '2.7' not in sys.version[:5]:
4341            dlg = wx.MessageDialog(None, 
4342                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
4343                'Python version error',  wx.OK)
4344            try:
4345                dlg.ShowModal()
4346            finally:
4347                dlg.Destroy()
4348            sys.exit()
4349        self.main = GSASII(None)
4350        self.main.Show()
4351        self.SetTopWindow(self.main)
4352        # save the current package versions
4353        self.main.PackageVersions = []
4354        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
4355        for p in (wx,mpl,np,sp,ogl):
4356            self.main.PackageVersions.append([p.__name__,p.__version__])
4357        try:
4358            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
4359        except:
4360            try:
4361                from PIL import PILLOW_VERSION
4362                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
4363            except:
4364                pass
4365        self.main.PackageVersions.append(['Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
4366       
4367        return True
4368    # def MacOpenFile(self, filename):
4369    #     '''Called on Mac every time a file is dropped on the app when it is running,
4370    #     treat this like a File/Open project menu action.
4371    #     Should be ignored on other platforms
4372    #     '''
4373    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
4374    #     print 'MacOpen',filename
4375    #     if os.path.splitext(filename)[1] == '.py': return
4376    #     # end PATCH
4377    #     self.main.OnFileOpen(None,filename)
4378    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
4379       
4380def main():
4381    '''Start up the GSAS-II application'''
4382    #application = GSASIImain() # don't redirect output, someday we
4383    # may want to do this if we can
4384    application = GSASIImain(0)
4385    if GSASIIpath.GetConfigValue('wxInspector'):
4386        import wx.lib.inspection as wxeye
4387        wxeye.InspectionTool().Show()
4388
4389    #application.main.OnRefine(None)
4390    application.MainLoop()
4391   
4392if __name__ == '__main__':
4393    # print versions
4394    print "Python module versions loaded:"
4395    print "  Python:     ",sys.version.split()[0]
4396    print "  wx:         ",wx.__version__
4397    print "  matplotlib: ",mpl.__version__
4398    print "  numpy:      ",np.__version__
4399    print "  scipy:      ",sp.__version__
4400    print "  OpenGL:     ",ogl.__version__
4401    try:
4402        from PIL import Image
4403        try:
4404            from PIL import PILLOW_VERSION
4405            version = PILLOW_VERSION
4406        except:
4407            version = Image.VERSION
4408        print "  PIL.Image:  ",version
4409    except ImportError:
4410        try:
4411            import Image
4412            print "Image (PIL):",Image.VERSION
4413        except ImportError:
4414            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
4415    import platform
4416    print "  Platform:   ",sys.platform,platform.architecture()[0],platform.machine()
4417    try:
4418        import mkl
4419        print "  Max threads:",mkl.get_max_threads()
4420    except:
4421        pass
4422    #print "wxPython description",wx.PlatformInfo
4423    print "This is GSAS-II revision "+str(GSASIIpath.GetVersionNumber())+'\n'
4424    GSASIIpath.InvokeDebugOpts()
4425    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.