source: trunk/GSASII.py @ 2755

Last change on this file since 2755 was 2755, checked in by vondreele, 5 years ago

extend Export HKLs for powder data to include most all columns in GSASII.py
narrow HorizontalLine? display
add a ReloadSubstances? option in case user changed wavelength in SASD & REFD substances
Add a default unit scatter to substances - only in new projects
add modeling of REFD patterns for x-rays (not yet tested - no data) & CW neutrons (tested)
modify XScattDen to give clearly fo, f' & f" components
add NCScattDen for CW neutrons - uses wave to generate real/imaginary components
fix printing of scattering density units - now correct symbols
remove unused imports from GSASIIsasd.py

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