source: trunk/GSASII.py @ 2818

Last change on this file since 2818 was 2818, checked in by vondreele, 6 years ago

add GetPhaseNames?(fl) to G2obj - uses opened gpx file
fix PhaseSelector? & remove ShowBusy? & DoneBusy? from G2IO - not used ever

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