source: trunk/GSASII.py @ 2710

Last change on this file since 2710 was 2710, checked in by vondreele, 7 years ago

improve ExportPDF with G2file selector & allow selection of I(Q), S(Q), F(Q) & G(R)

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