source: trunk/GSASII.py @ 2821

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

revisions to GSASIIscriptable - now works for reading PWDR data

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