source: trunk/GSASII.py @ 2637

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

fix delete data item selection to include only data items
expand the choices for stride in data selection dialog
cosmetic stuff in plots

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