source: trunk/GSASII.py @ 2851

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

more for arrow keys; do not understand how this windows code works, but apparently it does

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