source: trunk/GSASII.py @ 2727

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

fix issue with formula volume in PDF creation
block Lebail selection if DoPawley? is selected
force Flat Bkg >= 0
avoid crash if Refine done with no atoms

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