source: trunk/GSASII.py @ 2667

Last change on this file since 2667 was 2667, checked in by toby, 8 years ago

prevent Unicode in data tree names; move PDF setup to standalone routine

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