source: trunk/GSASII.py @ 2817

Last change on this file since 2817 was 2817, checked in by vondreele, 4 years ago

major revision - move all importers to GSASIIobj & make them independent of wx so they can be used in a scripting environment.
Still to move are PhaseSelector?, and 3 BlockSelector? dialogs

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