source: trunk/GSASII.py @ 2877

Last change on this file since 2877 was 2877, checked in by toby, 6 years ago

Plot each histogram after fitting in a sequential refinement

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