source: trunk/GSASII.py @ 2701

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

Rename ShowLSParms to OnShowLSParms
Add [Ymin,Ymax] to PWDR data[0] dict as 'Yminmax'
shift Phases = up a few lines to fix plot open bugs
Also PDFdata = up a few lines
Provide a ResetFlatBkg? to provide a start for Flat Bkg based on sample, Background, Container mns & mults for PDF.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Revision URL Id
File size: 197.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2017-02-13 20:58:18 +0000 (Mon, 13 Feb 2017) $
6# $Author: vondreele $
7# $Revision: 2701 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2701 2017-02-13 20:58:18Z 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: 2701 $")
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['Type'] = 'Bragg-Brentano'
1348                else:
1349                    rd.Sample['Type'] = 'Debye-Scherrer'
1350                if 'Generic' in choices[res]:
1351                    dlg = G2G.MultiFloatDialog(self,title='Generic TOF detector bank',
1352                        prompts=['Total FP','2-theta',],values=[25.0,150.,],
1353                            limits=[[6.,200.],[5.,175.],],formats=['%6.2f','%6.1f',])
1354                    if dlg.ShowModal() == wx.ID_OK: #strictly empirical approx.
1355                        FP,tth = dlg.GetValues()
1356                        difC = 505.632*FP*sind(tth/2.)
1357                        sig1 = 50.+2.5e-6*(difC/tand(tth/2.))**2
1358                        bet1 = .00226+7.76e+11/difC**4
1359                        rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1360                        Inst = self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)
1361                        Inst[0]['difC'] = [difC,difC,0]
1362                        Inst[0]['sig-1'] = [sig1,sig1,0]
1363                        Inst[0]['beta-1'] = [bet1,bet1,0]
1364                        return Inst    #this is [Inst1,Inst2] a pair of dicts
1365                    dlg.Destroy()
1366                else:
1367                    rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1368                    return self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1369
1370        # stuff we might need from the reader
1371        filename = rd.powderentry[0]
1372        bank = rd.powderentry[2]
1373        numbanks = rd.numbanks
1374        #1st priority: is there an instrument parameter file matching the current file
1375        # with extension .instprm, .prm, .inst, or .ins? If so read it
1376        basename = os.path.splitext(filename)[0]
1377        for ext in '.prm','.inst','.ins','.instprm':
1378            if self.zipfile:
1379                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1380                    selection=os.path.split(basename + ext)[1],parent=self)
1381                if instfile == None:
1382                    continue
1383            else:
1384                instfile = basename + ext
1385            if not os.path.exists(instfile):
1386                continue
1387            if 'instprm' in instfile:
1388                Lines = self.OpenPowderInstprm(instfile)
1389                instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1390                if 'list' in str(type(instParmList)):
1391                    rd.instfile = instfile
1392                    rd.instmsg = 'GSAS-II file '+instfile
1393                    return instParmList
1394                else:
1395                    #print 'debug: open/read failed',instfile
1396                    pass # fail silently
1397            else:
1398                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1399                if Iparm:
1400                    #print 'debug: success'
1401                    rd.instfile = instfile
1402                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1403                    return SetPowderInstParms(Iparm,rd)
1404                else:
1405                    #print 'debug: open/read failed',instfile
1406                    pass # fail silently
1407
1408        #2nd priority: is there an instrument parameter file defined for the current data set?
1409        # or if this is a read on a set of set of files, use the last one again
1410        #if rd.instparm as found in data file header or (lastdatafile == filename and lastIparmfile):
1411        if rd.instparm or lastIparmfile:
1412            if rd.instparm:
1413                instfile = os.path.join(os.path.split(filename)[0],rd.instparm)
1414            else:
1415                # for multiple reads of one data file, reuse the inst parm file
1416                instfile = lastIparmfile
1417#            if self.zipfile:
1418#                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1419#                    selection=os.path.split(instfile)[1],parent=self)
1420            if instfile != None and os.path.exists(instfile):
1421                #print 'debug: try read',instfile
1422                if 'instprm' in instfile:   #GSAS-II file must have .instprm as extension
1423                    Lines = self.OpenPowderInstprm(instfile)
1424                    if Lines is not None:
1425                        instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)   #this is [Inst1,Inst2] a pair of dicts
1426                else:   #old GSAS style iparm file - could be named anything!
1427                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1428                    if Iparm:
1429                        #print 'debug: success'
1430                        rd.instfile = instfile
1431                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1432                        instParmList = SetPowderInstParms(Iparm,rd)     #this is [Inst1,Inst2] a pair of dicts
1433                if 'list' in str(type(instParmList)):   #record stuff & return stuff
1434                    rd.instfile = instfile
1435                    rd.instmsg = 'GSAS-II file '+instfile
1436                    return instParmList
1437                else:   #bad iparms - try default
1438                    rd.instmsg = instParmList   #an error message
1439                    return GetDefaultParms(self,rd)
1440            else:
1441                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1442                    +str(instfile)+' requested by file '+ filename)
1443        #Finally - ask user for Instrument parametrs file - seems it can't be in a zip file
1444        while True: # loop until we get a file that works or we get a cancel
1445            instfile = ''
1446            pth = G2G.GetImportPath(self)
1447            if not pth: pth = '.'
1448            dlg = wx.FileDialog(self,
1449                'Choose inst. param file for "'+rd.idstring+'" (or Cancel for default)',
1450                pth, '',
1451                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1452                'GSAS-II iparm file (*.instprm)|*.instprm|'
1453                'All files (*.*)|*.*', wx.OPEN)
1454            if os.path.exists(lastIparmfile):
1455                dlg.SetFilename(lastIparmfile)
1456            if dlg.ShowModal() == wx.ID_OK:
1457                instfile = dlg.GetPath()
1458            dlg.Destroy()
1459            if not instfile: 
1460                return GetDefaultParms(self,rd) #on Cancel/break
1461            if 'instprm' in instfile:
1462                Lines = self.OpenPowderInstprm(instfile)
1463                if Lines is not None:
1464                    instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1465                if 'list' in str(type(instParmList)):
1466                    rd.instfile = instfile
1467                    rd.instmsg = 'GSAS-II file '+instfile
1468                    return instParmList
1469                else:
1470                    rd.instmsg = instParmList   #an error message
1471                    return GetDefaultParms(self,rd)
1472            else:
1473                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1474                if Iparm:
1475                    #print 'debug: success with',instfile
1476                    rd.instfile = instfile
1477                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1478                    return SetPowderInstParms(Iparm,rd)
1479                else:
1480                    self.ErrorDialog('Read Error',
1481                                     'Error opening/reading file '+str(instfile))
1482    def EnableRefineCommand(self):
1483        haveData = False
1484        # check for phases connected to histograms
1485        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1486        if not sub: return
1487        item, cookie = self.PatternTree.GetFirstChild(sub)
1488        while item: # loop over phases
1489            data = self.PatternTree.GetItemPyData(item)
1490            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1491            UseList = data['Histograms']
1492            if UseList: haveData = True
1493        if haveData:
1494            self.dataFrame.DataMenu.Enable(G2gd.wxID_DATADELETE,True)
1495            for item in self.Refine: item.Enable(True)
1496        else:
1497            self.dataFrame.DataMenu.Enable(G2gd.wxID_DATADELETE,False)
1498            for item in self.Refine: item.Enable(False)
1499
1500       
1501    def OnImportPowder(self,event):
1502        '''Called in response to an Import/Powder Data/... menu item
1503        to read a powder diffraction data set.
1504        dict self.ImportMenuId is used to look up the specific
1505        reader item associated with the menu item, which will be
1506        None for the last menu item, which is the "guess" option
1507        where all appropriate formats will be tried.
1508
1509        Also reads an instrument parameter file for each dataset.
1510        '''
1511        # get a list of existing histograms
1512        PWDRlist = []
1513        if self.PatternTree.GetCount():
1514            item, cookie = self.PatternTree.GetFirstChild(self.root)
1515            while item:
1516                name = self.PatternTree.GetItemText(item)
1517                if name.startswith('PWDR ') and name not in PWDRlist:
1518                    PWDRlist.append(name)
1519                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1520        # look up which format was requested
1521        reqrdr = self.ImportMenuId.get(event.GetId()) 
1522        rdlist = self.OnImportGeneric(
1523            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1524        if len(rdlist) == 0: return
1525        self.CheckNotebook()
1526        Iparm = None
1527        lastIparmfile = ''
1528        lastdatafile = ''
1529        newHistList = []
1530        lastVals = []
1531        self.EnablePlot = False
1532        for rd in rdlist:
1533            if 'Instrument Parameters' in rd.pwdparms:
1534                Iparm1,Iparm2 = rd.pwdparms['Instrument Parameters']
1535            else:
1536                # get instrument parameters for each dataset, unless already set
1537                if lastIparmfile:  # is this histogram like previous?
1538                    if lastVals != (rd.powderdata[0].min(),rd.powderdata[0].max(),len(rd.powderdata[0])):
1539                        lastIparmfile = ''
1540                Iparms = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1541                if not Iparms:  #may have bailed out
1542                    Id = 0
1543                    continue
1544                Iparm1,Iparm2 = Iparms
1545                if rd.repeat_instparm: 
1546                    lastIparmfile = rd.instfile
1547                    lastVals = (rd.powderdata[0].min(),rd.powderdata[0].max(),len(rd.powderdata[0]))
1548                # override any keys in read instrument parameters with ones set in import
1549                for key in Iparm1: 
1550                    if key in rd.instdict:
1551                        Iparm1[key] = rd.instdict[key]
1552            lastdatafile = rd.powderentry[0]
1553            HistName = 'PWDR '+G2obj.StripUnicode(rd.idstring,'_')
1554            # make new histogram names unique
1555            if HistName in PWDRlist:
1556                dlg = wx.MessageDialog(self,'Skip %s?'%(HistName),'Duplicate data name',wx.YES_NO)
1557                try:
1558                    if dlg.ShowModal() == wx.ID_YES:
1559                        Id = 0
1560                        continue
1561                finally:
1562                    dlg.Destroy()
1563            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1564            print('Read powder data '+HistName+ 
1565                ' from file '+rd.readfilename +
1566                ' (format: '+ rd.formatName + 
1567                '). Inst parameters from '+rd.instmsg)
1568            # data are read, now store them in the tree
1569            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1570            if 'T' in Iparm1['Type'][0]:
1571                if not rd.clockWd and rd.GSAS:
1572                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1573                cw = np.diff(rd.powderdata[0])
1574                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1575                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
1576                    npts = min(len(rd.powderdata[0]),len(rd.powderdata[1]),len(cw))
1577                    rd.powderdata[1] = rd.powderdata[1][:npts]/cw[:npts]
1578                    rd.powderdata[2] = rd.powderdata[2][:npts]*cw[:npts]**2  #1/var=w at this point
1579                else:       #NB: from topas/fullprof type files
1580                    rd.powderdata[1] = rd.powderdata[1][:-1]
1581                    rd.powderdata[2] = rd.powderdata[2][:-1]
1582                if 'Itype' in Iparm2:
1583                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1584                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1585                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1586                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1587                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1588                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1589                    var += WYI*rd.powderdata[1]**2
1590                    var /= YI**2
1591                    rd.powderdata[2] = 1./var
1592                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])
1593                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])
1594                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])
1595            Ymin = np.min(rd.powderdata[1])                 
1596            Ymax = np.max(rd.powderdata[1])                 
1597            valuesdict = {
1598                'wtFactor':1.0,
1599                'Dummy':False,
1600                'ranId':ran.randint(0,sys.maxint),
1601                'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
1602                'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
1603                }
1604            # apply user-supplied corrections to powder data
1605            if 'CorrectionCode' in Iparm1:
1606                print('Applying corrections from instprm file')
1607                corr = Iparm1['CorrectionCode'][0]
1608                try:
1609                    exec(corr)
1610                    print('done')
1611                except Exception as err:
1612                    print('error: '+str(err))
1613                    print('with commands -------------------')
1614                    print(str(corr))
1615                    print('---------------------------------')
1616                finally:
1617                    del Iparm1['CorrectionCode']
1618            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1619            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1620            self.PatternTree.SetItemPyData(
1621                self.PatternTree.AppendItem(Id,text='Comments'),
1622                rd.comments)
1623            Tmin = min(rd.powderdata[0])
1624            Tmax = max(rd.powderdata[0])
1625            Tmin1 = Tmin
1626            if 'NT' in Iparm1['Type'][0] and G2lat.Pos2dsp(Iparm1,Tmin) < 0.4:               
1627                Tmin1 = G2lat.Dsp2pos(Iparm1,0.4)
1628            self.PatternTree.SetItemPyData(
1629                self.PatternTree.AppendItem(Id,text='Limits'),
1630                rd.pwdparms.get('Limits',[(Tmin,Tmax),[Tmin1,Tmax]])
1631                )
1632            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1633            self.PatternTree.SetItemPyData(
1634                self.PatternTree.AppendItem(Id,text='Background'),
1635                rd.pwdparms.get('Background',
1636                    [['chebyschev',True,3,1.0,0.0,0.0],{'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1637                    )
1638            self.PatternTree.SetItemPyData(
1639                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1640                [Iparm1,Iparm2])
1641            self.PatternTree.SetItemPyData(
1642                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1643                rd.Sample)
1644            self.PatternTree.SetItemPyData(
1645                self.PatternTree.AppendItem(Id,text='Peak List')
1646                ,{'peaks':[],'sigDict':{}})
1647            self.PatternTree.SetItemPyData(
1648                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1649                [[],[]])
1650            self.PatternTree.SetItemPyData(
1651                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1652                [])
1653            self.PatternTree.SetItemPyData(
1654                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1655                {})
1656            # if any Control values have been set, move them into tree
1657            Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
1658            Controls.update(rd.Controls)
1659            newHistList.append(HistName)
1660            rd.repeat_instparm = False  #clear the iparm reuse flag
1661        else:
1662            self.EnablePlot = True
1663            if Id:
1664                self.PatternTree.Expand(Id)
1665                self.PatternTree.SelectItem(Id)
1666
1667        if not newHistList: return # somehow, no new histograms
1668        # make a list of phase names
1669        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1670        phaseNameList = usedHistograms.keys() # phase names in use
1671        if not phaseNameList: return # no phases yet, nothing to do
1672        header = 'Select phase(s) to link\nto the newly-read data:'
1673        for Name in newHistList:
1674            header += '\n  '+str(Name)
1675
1676        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1677        if not result: return
1678        # connect new phases to histograms
1679        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1680        if not sub:
1681            raise Exception('ERROR -- why are there no phases here?')
1682        item, cookie = self.PatternTree.GetFirstChild(sub)
1683        iph = -1
1684        while item: # loop over (new) phases
1685            iph += 1
1686            data = self.PatternTree.GetItemPyData(item)
1687            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1688            if iph not in result: continue
1689            generalData = data['General']
1690            SGData = generalData['SGData']
1691            UseList = data['Histograms']
1692            NShkl = len(G2spc.MustrainNames(SGData))
1693            NDij = len(G2spc.HStrainNames(SGData))
1694            for histoName in newHistList:
1695                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1696                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1697                refList = self.PatternTree.GetItemPyData(
1698                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1699                refList[generalData['Name']] = []
1700        self.EnableRefineCommand()
1701        return # success
1702
1703    def OnDummyPowder(self,event):
1704        '''Called in response to Import/Powder Data/Simulate menu item
1705        to create a Dummy powder diffraction data set.
1706
1707        Reads an instrument parameter file and then gets input from the user
1708        '''
1709        # get a list of existing histograms
1710        PWDRlist = []
1711        if self.PatternTree.GetCount():
1712            item, cookie = self.PatternTree.GetFirstChild(self.root)
1713            while item:
1714                name = self.PatternTree.GetItemText(item)
1715                if name.startswith('PWDR ') and name not in PWDRlist:
1716                    PWDRlist.append(name)
1717                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1718        # Initialize a base class reader
1719        rd = G2IO.ImportPowderData(
1720            extensionlist=tuple(),
1721            strictExtension=False,
1722            formatName = 'Simulate dataset',
1723            longFormatName = 'Compute a simulated pattern')
1724        rd.powderentry[0] = '' # no filename
1725        # #self.powderentry[1] = pos # bank offset (N/A here)
1726        rd.powderentry[2] = 1 # only one bank
1727        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1728        self.CheckNotebook()
1729        Iparm = None
1730        lastdatafile = ''
1731        self.zipfile = None
1732        # get instrument parameters for it
1733        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, '', lastdatafile)
1734        if 'T' in Iparm1['Type'][0]:
1735            print('TOF simulation not supported yet')
1736            return False
1737        else:
1738            # need to get name, 2theta start, end, step
1739            rd.idstring = ' CW'
1740            if 'X' in Iparm1['Type'][0]:
1741                rd.idstring = 'CW x-ray simulation'
1742            else:
1743                rd.idstring = 'CW neutron simulation'
1744            # base initial range on wavelength
1745            wave = Iparm1.get('Lam')
1746            if wave:
1747                wave = wave[0]
1748            else:
1749                wave = Iparm1.get('Lam1')
1750                if wave:
1751                    wave = wave[0]
1752        N = 0
1753        while (N < 3): # insist on a dataset with a few points
1754            names = ('dataset name', 'start angle', 'end angle', 'step size')
1755            if not wave or wave < 1.0:
1756                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1757            else:
1758                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1759            dlg = G2G.ScrolledMultiEditor(
1760                self,[inp] * len(inp),range(len(inp)),names,
1761                header='Enter simulation name and range',
1762                minvals=(None,0.001,0.001,0.0001),
1763                maxvals=(None,180.,180.,.1),
1764                sizevals=((225,-1),)
1765                )
1766            dlg.CenterOnParent()
1767            if dlg.ShowModal() == wx.ID_OK:
1768                if inp[1] > inp[2]:
1769                    end,start,step = inp[1:]
1770                else:               
1771                    start,end,step = inp[1:]
1772                step = abs(step)
1773            else:
1774                return False
1775            N = int((end-start)/step)+1
1776            x = np.linspace(start,end,N,True)
1777            N = len(x)
1778        rd.powderdata = [
1779            np.array(x), # x-axis values
1780            np.zeros_like(x), # powder pattern intensities
1781            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1782            np.zeros_like(x), # calc. intensities (zero)
1783            np.zeros_like(x), # calc. background (zero)
1784            np.zeros_like(x), # obs-calc profiles
1785            ]
1786        Tmin = rd.powderdata[0][0]
1787        Tmax = rd.powderdata[0][-1]
1788        # data are read, now store them in the tree
1789        HistName = inp[0]
1790        HistName = 'PWDR '+HistName
1791        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1792        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1793        Ymin = np.min(rd.powderdata[1])
1794        Ymax = np.max(rd.powderdata[1])
1795        valuesdict = {
1796            'wtFactor':1.0,
1797            'Dummy':True,
1798            'ranId':ran.randint(0,sys.maxint),
1799            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
1800            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
1801            }
1802        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1803        self.PatternTree.SetItemPyData(
1804            self.PatternTree.AppendItem(Id,text='Comments'),
1805            rd.comments)
1806        self.PatternTree.SetItemPyData(
1807            self.PatternTree.AppendItem(Id,text='Limits'),
1808            [(Tmin,Tmax),[Tmin,Tmax]])
1809        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1810        self.PatternTree.SetItemPyData(
1811            self.PatternTree.AppendItem(Id,text='Background'),
1812            [['chebyschev',True,3,1.0,0.0,0.0],
1813             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1814        self.PatternTree.SetItemPyData(
1815            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1816            [Iparm1,Iparm2])
1817        self.PatternTree.SetItemPyData(
1818            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1819            rd.Sample)
1820        self.PatternTree.SetItemPyData(
1821            self.PatternTree.AppendItem(Id,text='Peak List')
1822            ,{'peaks':[],'sigDict':{}})
1823        self.PatternTree.SetItemPyData(
1824            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1825            [[],[]])
1826        self.PatternTree.SetItemPyData(
1827            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1828            [])
1829        self.PatternTree.SetItemPyData(
1830            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1831            {})
1832        self.PatternTree.Expand(Id)
1833        self.PatternTree.SelectItem(Id)
1834        print('Added simulation powder data '+HistName+ 
1835              ' with parameters from '+str(rd.instmsg))
1836
1837        # make a list of phase names
1838        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1839        phaseNameList = usedHistograms.keys() # phase names in use
1840        if not phaseNameList: return # no phases yet, nothing to do
1841        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1842        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1843        if not result: return
1844        # connect new phases to histograms
1845        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1846        if not sub:
1847            raise Exception('ERROR -- why are there no phases here?')
1848        item, cookie = self.PatternTree.GetFirstChild(sub)
1849        iph = -1
1850        while item: # loop over (new) phases
1851            iph += 1
1852            data = self.PatternTree.GetItemPyData(item)
1853            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1854            if iph not in result: continue
1855            generalData = data['General']
1856            SGData = generalData['SGData']
1857            UseList = data['Histograms']
1858            NShkl = len(G2spc.MustrainNames(SGData))
1859            NDij = len(G2spc.HStrainNames(SGData))
1860            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
1861            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1862            refList = self.PatternTree.GetItemPyData(
1863                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1864            refList[generalData['Name']] = []
1865        self.EnableRefineCommand()
1866        return # success
1867       
1868    def OnPreferences(self,event):
1869        'Edit the GSAS-II configuration variables'
1870        dlg = G2G.SelectConfigSetting(self)
1871        dlg.ShowModal() == wx.ID_OK
1872        dlg.Destroy()
1873
1874    def _Add_ImportMenu_smallangle(self,parent):
1875        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1876        '''
1877        submenu = wx.Menu()
1878        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1879            submenu, help='Import small angle data')
1880        for reader in self.ImportSmallAngleReaderlist:
1881            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1882                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1883            self.ImportMenuId[item.GetId()] = reader
1884            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1885        # item = submenu.Append(wx.ID_ANY,
1886        #     help='Import small angle data, use file to try to determine format',
1887        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1888        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1889
1890    def OnImportSmallAngle(self,event):
1891        '''Called in response to an Import/Small Angle Data/... menu item
1892        to read a small angle diffraction data set.
1893        dict self.ImportMenuId is used to look up the specific
1894        reader item associated with the menu item, which will be
1895        None for the last menu item, which is the "guess" option
1896        where all appropriate formats will be tried.
1897
1898        '''
1899       
1900        def GetSASDIparm(reader):
1901            parm = reader.instdict
1902            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1903                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1904            return Iparm,{}
1905           
1906        # get a list of existing histograms
1907        SASDlist = []
1908        if self.PatternTree.GetCount():
1909            item, cookie = self.PatternTree.GetFirstChild(self.root)
1910            while item:
1911                name = self.PatternTree.GetItemText(item)
1912                if name.startswith('SASD ') and name not in SASDlist:
1913                    SASDlist.append(name)
1914                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1915        # look up which format was requested
1916        reqrdr = self.ImportMenuId.get(event.GetId()) 
1917        rdlist = self.OnImportGeneric(
1918            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1919        if len(rdlist) == 0: return
1920        self.CheckNotebook()
1921        newHistList = []
1922        self.EnablePlot = False
1923        for rd in rdlist:
1924            HistName = rd.idstring
1925            HistName = 'SASD '+HistName
1926            # make new histogram names unique
1927            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1928            print 'Read small angle data '+HistName+ \
1929                ' from file '+self.lastimport
1930            # data are read, now store them in the tree
1931            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1932            Iparm1,Iparm2 = GetSASDIparm(rd)
1933#            if 'T' in Iparm1['Type'][0]:
1934#                if not rd.clockWd and rd.GSAS:
1935#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1936#                cw = np.diff(rd.powderdata[0])
1937#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1938#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1939#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1940#                if 'Itype' in Iparm2:
1941#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1942#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1943#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1944#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1945#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1946#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1947#                    var += WYI*rd.powderdata[1]**2
1948#                    var /= YI**2
1949#                    rd.powderdata[2] = 1./var
1950#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1951#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1952#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1953            Tmin = min(rd.smallangledata[0])
1954            Tmax = max(rd.smallangledata[0])
1955            valuesdict = {
1956                'wtFactor':1.0,
1957                'Dummy':False,
1958                'ranId':ran.randint(0,sys.maxint),
1959                }
1960            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1961            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1962            self.PatternTree.SetItemPyData(
1963                self.PatternTree.AppendItem(Id,text='Comments'),
1964                rd.comments)
1965            self.PatternTree.SetItemPyData(
1966                self.PatternTree.AppendItem(Id,text='Limits'),
1967                [(Tmin,Tmax),[Tmin,Tmax]])
1968            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1969            self.PatternTree.SetItemPyData(
1970                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1971                [Iparm1,Iparm2])
1972            self.PatternTree.SetItemPyData(
1973                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1974            self.PatternTree.SetItemPyData(
1975                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1976                rd.Sample)
1977            self.PatternTree.SetItemPyData(
1978                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1979            newHistList.append(HistName)
1980        else:
1981            self.EnablePlot = True
1982            self.PatternTree.Expand(Id)
1983            self.PatternTree.SelectItem(Id)
1984           
1985        if not newHistList: return # somehow, no new histograms
1986        return # success
1987
1988    def OnMacroRecordStatus(self,event,setvalue=None):
1989        '''Called when the record macro menu item is used which toggles the
1990        value. Alternately a value to be set can be provided. Note that this
1991        routine is made more complex because on the Mac there are lots of menu
1992        items (listed in self.MacroStatusList) and this loops over all of them.
1993        '''
1994        nextvalue = log.ShowLogStatus() != True
1995        if setvalue is not None:
1996            nextvalue = setvalue
1997        if nextvalue:
1998            log.LogOn()
1999            set2 = True
2000        else:
2001            log.LogOff()
2002            set2 = False
2003        for menuitem in self.MacroStatusList:
2004            menuitem.Check(set2)
2005
2006    def _init_Macro(self):
2007        '''Define the items in the macro menu.
2008        '''
2009        menu = self.MacroMenu
2010        item = menu.Append(
2011                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
2012                kind=wx.ITEM_CHECK,text='Record actions')
2013        self.MacroStatusList.append(item)
2014        item.Check(log.ShowLogStatus())
2015        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
2016
2017        # this may only be of value for development work
2018        item = menu.Append(
2019            help='Show logged commands', id=wx.ID_ANY,
2020            kind=wx.ITEM_NORMAL,text='Show log')
2021        def OnShowLog(event):
2022            print 70*'='
2023            print 'List of logged actions'
2024            for i,line in enumerate(log.G2logList):
2025                if line: print i,line
2026            print 70*'='
2027        self.Bind(wx.EVT_MENU, OnShowLog, item)
2028
2029        item = menu.Append(
2030            help='Clear logged commands', id=wx.ID_ANY,
2031            kind=wx.ITEM_NORMAL,text='Clear log')
2032        def OnClearLog(event): log.G2logList=[None]
2033        self.Bind(wx.EVT_MENU, OnClearLog, item)
2034       
2035        item = menu.Append(
2036            help='Save logged commands to file', id=wx.ID_ANY,
2037            kind=wx.ITEM_NORMAL,text='Save log')
2038        def OnSaveLog(event):
2039            import cPickle
2040            defnam = os.path.splitext(
2041                os.path.split(self.GSASprojectfile)[1]
2042                )[0]+'.gcmd'
2043            dlg = wx.FileDialog(self,
2044                'Choose an file to save past actions', '.', defnam, 
2045                'GSAS-II cmd output (*.gcmd)|*.gcmd',
2046                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2047            dlg.CenterOnParent()
2048            try:
2049                if dlg.ShowModal() == wx.ID_OK:
2050                    filename = dlg.GetPath()
2051                    # make sure extension is correct
2052                    filename = os.path.splitext(filename)[0]+'.gcmd'
2053                else:
2054                    filename = None
2055            finally:
2056                dlg.Destroy()
2057            if filename:
2058                fp = open(filename,'wb')
2059                fp.write(str(len(log.G2logList)-1)+'\n')
2060                for item in log.G2logList:
2061                    if item: cPickle.dump(item,fp)
2062                fp.close()
2063        self.Bind(wx.EVT_MENU, OnSaveLog, item)
2064
2065        item = menu.Append(
2066            help='Load logged commands from file', id=wx.ID_ANY,
2067            kind=wx.ITEM_NORMAL,text='Load log')
2068        def OnLoadLog(event):
2069            # this appends. Perhaps we should ask to clear?
2070            import cPickle
2071            defnam = os.path.splitext(
2072                os.path.split(self.GSASprojectfile)[1]
2073                )[0]+'.gcmd'
2074            dlg = wx.FileDialog(self,
2075                'Choose an file to read saved actions', '.', defnam, 
2076                'GSAS-II cmd output (*.gcmd)|*.gcmd',
2077                wx.OPEN)
2078            dlg.CenterOnParent()
2079            try:
2080                if dlg.ShowModal() == wx.ID_OK:
2081                    filename = dlg.GetPath()
2082                    # make sure extension is correct
2083                    filename = os.path.splitext(filename)[0]+'.gcmd'
2084                else:
2085                    filename = None
2086            finally:
2087                dlg.Destroy()
2088            if filename and os.path.exists(filename):
2089                fp = open(filename,'rb')
2090                lines = fp.readline()
2091                for i in range(int(lines)):
2092                    log.G2logList.append(cPickle.load(fp))
2093                fp.close()
2094        self.Bind(wx.EVT_MENU, OnLoadLog, item)
2095
2096        item = menu.Append(
2097            help='Replay saved commands', id=wx.ID_ANY,
2098            kind=wx.ITEM_NORMAL,text='Replay log')
2099        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
2100
2101    def _init_Exports(self,menu):
2102        '''Find exporter routines and add them into menus
2103        '''
2104        # set up the top-level menus
2105        projectmenu = wx.Menu()
2106        item = menu.AppendMenu(
2107            wx.ID_ANY, 'Entire project as',
2108            projectmenu, help='Export entire project')
2109
2110        phasemenu = wx.Menu()
2111        item = menu.AppendMenu(
2112            wx.ID_ANY, 'Phase as',
2113            phasemenu, help='Export phase or sometimes phases')
2114
2115        powdermenu = wx.Menu()
2116        item = menu.AppendMenu(
2117            wx.ID_ANY, 'Powder data as',
2118            powdermenu, help='Export powder diffraction histogram(s)')
2119
2120        singlemenu = wx.Menu()
2121        item = menu.AppendMenu(
2122            wx.ID_ANY, 'Single crystal data as',
2123            singlemenu, help='Export single crystal histogram(s)')
2124
2125        imagemenu = wx.Menu()
2126        item = menu.AppendMenu(
2127            wx.ID_ANY, 'Image data as',
2128            imagemenu, help='Export powder image(s) data')
2129
2130        mapmenu = wx.Menu()
2131        item = menu.AppendMenu(
2132            wx.ID_ANY, 'Maps as',
2133            mapmenu, help='Export density map(s)')
2134
2135        # pdfmenu = wx.Menu()
2136        # item = menu.AppendMenu(
2137        #     wx.ID_ANY, 'PDFs as',
2138        #     pdfmenu, help='Export pair distribution function(s)')
2139
2140        # find all the exporter files
2141        if not self.exporterlist: # this only needs to be done once
2142            pathlist = sys.path
2143            filelist = []
2144            for path in pathlist:
2145                for filename in glob.iglob(os.path.join(path,"G2export*.py")):
2146                    filelist.append(filename)   
2147            filelist = sorted(list(set(filelist))) # remove duplicates
2148            # go through the routines and import them, saving objects that
2149            # have export routines (method Exporter)
2150            for filename in filelist:
2151                path,rootname = os.path.split(filename)
2152                pkg = os.path.splitext(rootname)[0]
2153#                try:
2154                fp = None
2155                fp, fppath,desc = imp.find_module(pkg,[path,])
2156                pkg = imp.load_module(pkg,fp,fppath,desc)
2157                for clss in inspect.getmembers(pkg): # find classes defined in package
2158                    if clss[0].startswith('_'): continue
2159                    if inspect.isclass(clss[1]):
2160                        # check if we have the required methods
2161                        for m in 'Exporter','loadParmDict':
2162                            if not hasattr(clss[1],m): break
2163                            if not callable(getattr(clss[1],m)): break
2164                        else:
2165                            exporter = clss[1](self) # create an export instance
2166                            self.exporterlist.append(exporter)
2167#                except AttributeError:
2168#                    print 'Import_'+errprefix+': Attribute Error'+str(filename)
2169#                    pass
2170#                except ImportError:
2171#                    print 'Import_'+errprefix+': Error importing file'+str(filename)
2172#                    pass
2173                if fp: fp.close()
2174        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
2175        for obj in self.exporterlist:
2176            #print 'exporter',obj
2177            for typ in obj.exporttype:
2178                if typ == "project":
2179                    submenu = projectmenu
2180                elif typ == "phase":
2181                    submenu = phasemenu
2182                elif typ == "powder":
2183                    submenu = powdermenu
2184                elif typ == "single":
2185                    submenu = singlemenu
2186                elif typ == "image":
2187                    submenu = imagemenu
2188                elif typ == "map":
2189                    submenu = mapmenu
2190                # elif typ == "pdf":
2191                #     submenu = pdfmenu
2192                else:
2193                    print("Error, unknown type in "+str(obj))
2194                    break
2195                item = submenu.Append(
2196                    wx.ID_ANY,
2197                    help=obj.longFormatName,
2198                    kind=wx.ITEM_NORMAL,
2199                    text=obj.formatName)
2200                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
2201                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
2202        item = imagemenu.Append(wx.ID_ANY,
2203                        help='Export image controls and masks for multiple images',
2204                        kind=wx.ITEM_NORMAL,
2205                        text='Multiple image controls and masks')
2206        self.Bind(wx.EVT_MENU, self.OnSaveMultipleImg, id=item.GetId())
2207        #code to debug an Exporter. hard-code the routine below, to allow a reload before use
2208        # def DebugExport(event):
2209        #      print 'start reload'
2210        #      reload(G2IO)
2211        #      import G2export_pwdr as dev
2212        #      reload(dev)
2213        #      dev.ExportPowderFXYE(self).Exporter(event)
2214        # item = menu.Append(
2215        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2216        #     help="debug exporter",text="test Export FXYE")
2217        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
2218        # # #self.ExportLookup[item.GetId()] = 'image'
2219        # self.ExportLookup[item.GetId()] = 'powder'
2220
2221    def _Add_ExportMenuItems(self,parent):
2222        # item = parent.Append(
2223        #     help='Select PWDR item to enable',id=wx.ID_ANY,
2224        #     kind=wx.ITEM_NORMAL,
2225        #     text='Export Powder Patterns...')
2226        # self.ExportPattern.append(item)
2227        # item.Enable(False)
2228        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
2229
2230        item = parent.Append(
2231            help='',id=wx.ID_ANY,
2232            kind=wx.ITEM_NORMAL,
2233            text='Export All Peak Lists...')
2234        self.ExportPeakList.append(item)
2235        item.Enable(True)
2236        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
2237
2238        item = parent.Append(
2239            help='',id=wx.ID_ANY,
2240            kind=wx.ITEM_NORMAL,
2241            text='Export HKLs...')
2242        self.ExportHKL.append(item)
2243        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
2244
2245        item = parent.Append(
2246            help='Select PDF item to enable',
2247            id=wx.ID_ANY,
2248            kind=wx.ITEM_NORMAL,
2249            text='Export PDF...')
2250        self.ExportPDF.append(item)
2251        item.Enable(False)
2252        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
2253
2254    def FillMainMenu(self,menubar,addhelp=True):
2255        '''Define contents of the main GSAS-II menu for the (main) data tree window.
2256        For the mac, this is also called for the data item windows as well so that
2257        the main menu items are data menu as well.
2258        '''
2259        File = wx.Menu(title='')
2260        menubar.Append(menu=File, title='&File')
2261        self._Add_FileMenuItems(File)
2262        Data = wx.Menu(title='')
2263        menubar.Append(menu=Data, title='Data')
2264        self._Add_DataMenuItems(Data)
2265        Calculate = wx.Menu(title='')       
2266        menubar.Append(menu=Calculate, title='&Calculate')
2267        self._Add_CalculateMenuItems(Calculate)
2268        Import = wx.Menu(title='')       
2269        menubar.Append(menu=Import, title='Import')
2270        self._Add_ImportMenu_Image(Import)
2271        self._Add_ImportMenu_Phase(Import)
2272        self._Add_ImportMenu_powder(Import)
2273        self._Add_ImportMenu_Sfact(Import)
2274        self._Add_ImportMenu_smallangle(Import)
2275
2276        #======================================================================
2277        # Code to help develop/debug an importer, much is hard-coded below
2278        # but module is reloaded before each use, allowing faster testing
2279        # def DebugImport(event):
2280        #     print 'start reload'
2281        #     import G2phase_ISO as dev
2282        #     reload(dev)
2283        #     rd = dev.ISODISTORTPhaseReader()
2284        #     self.ImportMenuId[event.GetId()] = rd
2285        #     self.OnImportPhase(event)
2286            # or ----------------------------------------------------------------------
2287            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
2288            # special debug code
2289            # or ----------------------------------------------------------------------
2290            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
2291            # fp = open(filename,'Ur')
2292            # if not rd.ContentsValidator(fp):
2293            #     print 'not validated'
2294            #     # make a list of used phase ranId's
2295            # phaseRIdList = []
2296            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2297            # if sub:
2298            #     item, cookie = self.PatternTree.GetFirstChild(sub)
2299            #     while item:
2300            #         phaseName = self.PatternTree.GetItemText(item)
2301            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2302            #         if ranId: phaseRIdList.append(ranId)
2303            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2304            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2305            #     print 'read OK'
2306        # item = Import.Append(
2307        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2308        #     help="debug importer",text="test importer")
2309        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2310        #======================================================================
2311        self.ExportMenu = wx.Menu(title='')
2312        menubar.Append(menu=self.ExportMenu, title='Export')
2313        self._init_Exports(self.ExportMenu)
2314        self._Add_ExportMenuItems(self.ExportMenu)
2315        if GSASIIpath.GetConfigValue('Enable_logging'):
2316            self.MacroMenu = wx.Menu(title='')
2317            menubar.Append(menu=self.MacroMenu, title='Macro')
2318            self._init_Macro()
2319        if addhelp:
2320            HelpMenu=G2G.MyHelp(self,includeTree=True,
2321                morehelpitems=[('&Tutorials','Tutorials'),])
2322            menubar.Append(menu=HelpMenu,title='&Help')
2323           
2324    def _init_ctrls(self, parent):
2325        wx.Frame.__init__(self, name='GSASII', parent=parent,
2326            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2327        clientSize = wx.ClientDisplayRect()
2328        Size = self.GetSize()
2329        xPos = clientSize[2]-Size[0]
2330        self.SetPosition(wx.Point(xPos,clientSize[1]))
2331        self._init_Imports()
2332        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2333        self.MakePDF = []
2334        self.Refine = []
2335        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2336        #self.ExportPattern = []
2337        self.ExportPeakList = []
2338        self.ExportHKL = []
2339        self.ExportPDF = []
2340        self.ExportPhase = []
2341        self.ExportCIF = []
2342        #
2343        self.GSASIIMenu = wx.MenuBar()
2344        # create a list of all dataframe menus (appended in PrefillDataMenu)
2345        self.dataMenuBars = [self.GSASIIMenu]
2346        self.MacroStatusList = []
2347        self.FillMainMenu(self.GSASIIMenu)
2348        self.SetMenuBar(self.GSASIIMenu)
2349        self.Bind(wx.EVT_SIZE, self.OnSize)
2350        self.Status = self.CreateStatusBar()
2351        self.mainPanel = wx.Panel(self,-1)
2352       
2353        wxID_PATTERNTREE = wx.NewId()
2354        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2355        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2356            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2357        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,self.OnDataTreeSelChanged)
2358        self.PatternTree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK,self.OnDataTreeSelChanged)
2359        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2360            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2361        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2362            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2363        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2364            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2365        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2366            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2367        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2368            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2369        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2370            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2371        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2372        self.root = self.PatternTree.root
2373        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2374            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2375        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame,G2frame=self)
2376        plotFrame.Show()
2377       
2378        self.dataDisplay = None
2379       
2380    def __init__(self, parent):
2381        self.ExportLookup = {}
2382        self.exporterlist = []
2383        self._init_ctrls(parent)
2384        self.Image = wx.Image(
2385            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2386            wx.BITMAP_TYPE_ICO)
2387        if "wxMSW" in wx.PlatformInfo:
2388            img = self.Image.Scale(16, 16).ConvertToBitmap()
2389        elif "wxGTK" in wx.PlatformInfo:
2390            img = self.Image.Scale(22, 22).ConvertToBitmap()
2391        else:
2392            img = self.Image.ConvertToBitmap()
2393        self.SetIcon(wx.IconFromBitmap(img))
2394        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2395        # various defaults
2396        self.oldFocus = None
2397        self.GSASprojectfile = ''
2398        self.undofile = ''
2399        self.TreeItemDelete = False
2400        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2401        self.Weight = False
2402        self.IfPlot = False
2403        self.DDShowAll = False
2404        self.atmSel = ''
2405        self.PatternId = 0
2406        self.PickId = 0
2407        self.PickIdText = None
2408        self.PeakTable = []
2409        self.LimitsTable = []
2410        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2411        self.HKL = []
2412        self.Lines = []
2413        self.itemPicked = None
2414        self.dataFrame = None
2415        self.Interpolate = 'nearest'
2416        self.ContourColor = 'Paired'
2417        self.VcovColor = 'RdYlGn'
2418        self.RamaColor = 'Blues'
2419        self.Projection = 'equal area'
2420        self.logPlot = False
2421        self.plusPlot = True
2422        self.sqPlot = False
2423        self.ErrorBars = False
2424        self.Contour = False
2425        self.Legend = False
2426        self.SinglePlot = True
2427        self.Waterfall = False
2428        self.selections= None
2429        self.PDFselections = None
2430        self.SubBack = False
2431        self.seqReverse = False
2432        self.seqLines = True #draw lines between points
2433        self.plotView = 0
2434        self.Image = 0
2435        self.oldImagefile = '' # the name of the last image file read
2436        self.oldImageTag = None # the name of the tag for multi-image files
2437        self.PauseIntegration = False
2438        self.ImageZ = []
2439        self.Integrate = 0
2440        self.imageDefault = {}
2441        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2442        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2443        self.autoIntFrame = None
2444        self.IntegratedList = [] # list of already integrated IMG tree items
2445        self.Sngl = False
2446        self.ifGetRing = False
2447        self.MaskKey = ''           #trigger for making image masks
2448        self.MskDelete = False      #trigger for mask delete
2449        self.StrainKey = ''         #ditto for new strain d-zeros
2450        self.EnablePlot = True
2451        self.hist = ''              # selected histogram in Phase/Data tab
2452        self.dirname = os.path.abspath(os.path.expanduser('~'))       #start in the users home directory by default; may be meaningless
2453        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2454        self.LastImportDir = None # last-used directory where an import was done
2455        self.LastGPXdir = None    # directory where a GPX file was last read
2456        self.LastExportDir = None  # the last directory used for exports, if any.
2457        self.dataDisplayPhaseText = ''
2458        self.lastTreeSetting = []
2459        self.ExpandingAll = False
2460               
2461        arg = sys.argv
2462        if len(arg) > 1 and arg[1]:
2463            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2464            self.dirname = os.path.abspath(os.path.dirname(arg[1]))
2465            if self.dirname: os.chdir(self.dirname)
2466            try:
2467                self.StartProject()         #open the file if possible
2468                return
2469            except Exception:
2470                print 'Error opening or reading file',arg[1]
2471                import traceback
2472                print traceback.format_exc()
2473               
2474        if GSASIIpath.GetConfigValue('Starting_directory'):
2475            try:
2476                pth = GSASIIpath.GetConfigValue('Starting_directory')
2477                pth = os.path.expanduser(pth) 
2478                os.chdir(pth)
2479                self.LastGPXdir = pth
2480            except:
2481                print('Ignoring Config Starting_directory value: '+
2482                      GSASIIpath.GetConfigValue('Starting_directory'))
2483
2484    def GetTreeItemsList(self,item):
2485        return self.PatternTree._getTreeItemsList(item)
2486
2487    def OnSize(self,event):
2488        'Called to make PatternTree fill mainPanel'
2489        w,h = self.GetClientSizeTuple()
2490        self.mainPanel.SetSize(wx.Size(w,h))
2491        self.PatternTree.SetSize(wx.Size(w,h))
2492                       
2493    def OnDataTreeSelChanged(self, event):
2494        '''Called when a data tree item is selected'''
2495        if self.TreeItemDelete:
2496            self.TreeItemDelete = False
2497        else:
2498            if self.ExpandingAll:
2499                #if GSASIIpath.GetConfigValue('debug'): print('Skipping Tree selection due to ExpandAll')
2500                return
2501            pltNum = self.G2plotNB.nb.GetSelection()
2502            if pltNum >= 0:                         #to avoid the startup with no plot!
2503                self.G2plotNB.nb.GetPage(pltNum)
2504            item = event.GetItem()
2505            wx.CallAfter(G2gd.SelectDataTreeItem,self,item)
2506            if self.oldFocus: # Why do this?
2507                self.oldFocus.SetFocus()
2508       
2509    def OnPatternTreeItemCollapsed(self, event):
2510        'Called when a tree item is collapsed - all children will be collapsed'
2511        self.PatternTree.CollapseAllChildren(event.GetItem())
2512
2513    def OnPatternTreeItemExpanded(self, event):
2514        'Called when a tree item is expanded'
2515        self.OnDataTreeSelChanged(event)
2516        event.Skip()
2517       
2518    def OnPatternTreeItemDelete(self, event):
2519        'Called when a tree item is deleted -- not sure what this does'
2520        self.TreeItemDelete = True
2521
2522    def OnPatternTreeItemActivated(self, event):
2523        'Called when a tree item is activated'
2524        event.Skip()
2525       
2526    def OnPatternTreeBeginRDrag(self,event):
2527        event.Allow()
2528        self.BeginDragId = event.GetItem()
2529        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2530        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2531        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2532        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2533        while item:     #G2 data tree has no sub children under a child of a tree item
2534            name = self.PatternTree.GetItemText(item)
2535            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2536            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2537       
2538    def OnPatternTreeEndDrag(self,event):
2539        event.Allow()
2540        self.EndDragId = event.GetItem()
2541        try:
2542            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2543        except:
2544            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2545            NewParent = self.root
2546        if self.ParentId != NewParent:
2547            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2548        else:
2549            Name,Item = self.DragData[0]
2550            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2551            self.PatternTree.SetItemPyData(NewId,Item)
2552            for name,item in self.DragData[1:]:     #loop over children
2553                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2554                self.PatternTree.SetItemPyData(Id,item)
2555            self.PatternTree.Delete(self.BeginDragId)
2556            G2gd.SelectDataTreeItem(self,NewId)
2557       
2558    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2559        'Allows stepping through the tree with the up/down arrow keys'
2560        self.oldFocus = wx.Window.FindFocus()
2561        keyevt = event.GetKeyEvent()
2562        key = event.GetKeyCode()
2563        item = self.PatternTree.GetSelection()
2564        if type(item) is int: return # is this the toplevel in tree?
2565        name = self.PatternTree.GetItemText(item)
2566        parent = self.PatternTree.GetItemParent(item)
2567        if key == wx.WXK_UP:
2568            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2569                if type(parent) is int: return # is this the toplevel in tree?
2570                prev = self.PatternTree.GetPrevSibling(parent)
2571                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2572                if NewId:
2573                    self.PatternTree.Collapse(parent)
2574                    self.PatternTree.Expand(prev)
2575                    self.oldFocus = wx.Window.FindFocus()
2576                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2577                else:
2578                    wx.CallAfter(self.PatternTree.SelectItem,item)
2579            else:   
2580                self.PatternTree.GetPrevSibling(item)
2581                self.PatternTree.SelectItem(item)
2582        elif key == wx.WXK_DOWN:
2583            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2584                next = self.PatternTree.GetNextSibling(parent)
2585                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2586                if NewId:
2587                    self.PatternTree.Collapse(parent)
2588                    self.PatternTree.Expand(next)
2589                    self.oldFocus = wx.Window.FindFocus()
2590                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2591                else:
2592                    wx.CallAfter(self.PatternTree.SelectItem,item)
2593            else:   
2594                self.PatternTree.GetNextSibling(item)
2595                self.PatternTree.SelectItem(item)
2596               
2597    def OnReadPowderPeaks(self,event):
2598        'Bound to menu Data/Read Powder Peaks'
2599        self.CheckNotebook()
2600        pth = G2G.GetImportPath(self)
2601        if not pth: pth = '.'
2602        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2603            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2604        try:
2605            if dlg.ShowModal() == wx.ID_OK:
2606                self.HKL = []
2607                self.powderfile = dlg.GetPath()
2608                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2609                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2610                data = ['PKS',wave,0.0]
2611                names = ['Type','Lam','Zero'] 
2612                codes = [0,0,0]
2613                inst = [G2IO.makeInstDict(names,data,codes),{}]
2614                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2615                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2616                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2617                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2618                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2619                self.PatternTree.Expand(Id)
2620                self.PatternTree.SelectItem(Id)
2621                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2622        finally:
2623            dlg.Destroy()
2624                       
2625    def OnImageRead(self,event):
2626        '''Called to read in an image in any known format. *** Depreciated. ***
2627        '''
2628        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2629
2630    def CheckNotebook(self):
2631        '''Make sure the data tree has the minimally expected controls.
2632        '''
2633        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2634            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2635            self.PatternTree.SetItemPyData(sub,[''])
2636        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2637            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2638            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2639        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2640            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2641            self.PatternTree.SetItemPyData(sub,{})
2642        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2643            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2644            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2645        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2646            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2647            self.PatternTree.SetItemPyData(sub,{})
2648        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2649            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2650            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2651                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2652               
2653    class CopyDialog(wx.Dialog):
2654        '''Creates a dialog for copying control settings between
2655        data tree items'''
2656        def __init__(self,parent,title,text,data):
2657            wx.Dialog.__init__(self,parent,-1,title, 
2658                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2659            self.data = data
2660            panel = wx.Panel(self)
2661            mainSizer = wx.BoxSizer(wx.VERTICAL)
2662            topLabl = wx.StaticText(panel,-1,text)
2663            mainSizer.Add((10,10),1)
2664            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2665            mainSizer.Add((10,10),1)
2666            ncols = len(data)/40+1
2667            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2668            for id,item in enumerate(self.data):
2669                ckbox = wx.CheckBox(panel,id,item[1])
2670                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2671                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2672            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2673            OkBtn = wx.Button(panel,-1,"Ok")
2674            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2675            cancelBtn = wx.Button(panel,-1,"Cancel")
2676            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2677            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2678            btnSizer.Add((20,20),1)
2679            btnSizer.Add(OkBtn)
2680            btnSizer.Add((20,20),1)
2681            btnSizer.Add(cancelBtn)
2682            btnSizer.Add((20,20),1)
2683           
2684            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2685            panel.SetSizer(mainSizer)
2686            panel.Fit()
2687            self.Fit()
2688       
2689        def OnCopyChange(self,event):
2690            id = event.GetId()
2691            self.data[id][0] = self.FindWindowById(id).GetValue()       
2692           
2693        def OnOk(self,event):
2694            parent = self.GetParent()
2695            parent.Raise()
2696            self.EndModal(wx.ID_OK)             
2697           
2698        def OnCancel(self,event):
2699            parent = self.GetParent()
2700            parent.Raise()
2701            self.EndModal(wx.ID_CANCEL)             
2702           
2703        def GetData(self):
2704            return self.data
2705       
2706    class SumDialog(wx.Dialog):
2707        '''Allows user to supply scale factor(s) when summing data -
2708        TODO: CAN WE PREVIEW RESULT HERE?'''
2709        def __init__(self,parent,title,text,dataType,data,dataList):
2710            wx.Dialog.__init__(self,parent,-1,title,size=(400,250),
2711                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
2712            self.plotFrame = wx.Frame(self,-1,'Sum Plots',size=wx.Size(700,600), \
2713                style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2714            self.G2plotNB = G2plt.G2PlotNoteBook(self.plotFrame,G2frame=self)
2715            self.data = data
2716            self.dataList = dataList
2717            self.dataType = dataType
2718            size = (450,350)
2719            panel = wxscroll.ScrolledPanel(self, wx.ID_ANY,size=size,
2720                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
2721            mainSizer = wx.BoxSizer(wx.VERTICAL)
2722            topLabl = wx.StaticText(panel,-1,text)
2723            mainSizer.Add((10,10),1)
2724            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2725            mainSizer.Add((10,10),1)
2726            self.dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2727            for id,item in enumerate(self.data[:-1]):
2728                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(300,20))
2729                name.SetEditable(False)
2730#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2731                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2732                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2733                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2734                self.dataGridSizer.Add(scale,0,wx.LEFT,10)
2735                self.dataGridSizer.Add(name,0,wx.RIGHT,10)
2736            if self.dataType:
2737                self.dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+self.dataType),0, \
2738                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2739                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(300,20),style=wx.TE_PROCESS_ENTER)
2740                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2741                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2742                self.dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2743                self.dataGridSizer.Add(wx.StaticText(panel,label='All scales value: '),0,  \
2744                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2745#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2746                allScale = wx.TextCtrl(panel,value='',style=wx.TE_PROCESS_ENTER)
2747                allScale.Bind(wx.EVT_TEXT_ENTER,self.OnAllScale)
2748                allScale.Bind(wx.EVT_KILL_FOCUS,self.OnAllScale)
2749                self.dataGridSizer.Add(allScale,0,WACV)
2750            mainSizer.Add(self.dataGridSizer,0,wx.EXPAND)
2751            OkBtn = wx.Button(panel,-1,"Ok")
2752            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2753            cancelBtn = wx.Button(panel,-1,"Cancel")
2754            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2755            btnSizer = wx.FlexGridSizer(0,3,10,20)
2756            if self.dataType =='PWDR':
2757                TestBtn = wx.Button(panel,-1,"Test")
2758                TestBtn.Bind(wx.EVT_BUTTON, self.OnTest)
2759                btnSizer.Add(TestBtn)
2760            btnSizer.Add(OkBtn)
2761            btnSizer.Add(cancelBtn)
2762           
2763            panel.SetSizer(mainSizer)
2764            panel.SetAutoLayout(1)
2765            panel.SetupScrolling()
2766            mainSizer.Add((10,10),1)
2767            mainSizer.Add(btnSizer,0,wx.CENTER)
2768            panel.SetSizer(mainSizer)
2769            panel.Fit()
2770            self.Fit()
2771
2772        def OnScaleChange(self,event):
2773            event.Skip()
2774            id = event.GetId()
2775            value = self.FindWindowById(id).GetValue()
2776            try:
2777                self.data[id][0] = float(value)
2778                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2779            except ValueError:
2780                if value and '-' not in value[0]:
2781                    print 'bad input - numbers only'
2782                    self.FindWindowById(id).SetValue('0.000')
2783                   
2784        def OnAllScale(self,event):
2785            event.Skip()
2786            id = event.GetId()
2787            try:
2788                scale = float(self.FindWindowById(id).GetValue())
2789                self.FindWindowById(id).SetValue('%.3f'%(scale))
2790                entries = self.dataGridSizer.GetChildren()
2791                for i,item in enumerate(self.data[:-1]):
2792                    item[0] = scale
2793                    entries[2*i].GetWindow().SetValue('%.3f'%(scale))
2794                 
2795            except ValueError:
2796                print 'bad input - numbers only'
2797                self.FindWindowById(id).SetValue('')
2798                   
2799           
2800        def OnNameChange(self,event):
2801            event.Skip()
2802            self.data[-1] = self.name.GetValue()
2803           
2804        def OnTest(self,event):
2805            lenX = 0
2806            Xminmax = [0,0]
2807            XY = []
2808            Xsum = []
2809            Ysum = []
2810            Vsum = []
2811            result = self.data
2812            for i,item in enumerate(result[:-1]):
2813                scale,name = item
2814                data = self.dataList[i]
2815                if scale:
2816                    x,y,w,yc,yb,yd = data   #numpy arrays!
2817                    XY.append([x,scale*y])
2818                    v = 1./w
2819                    if lenX:
2820                        if lenX != len(x):
2821                            self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2822                                '\nExpected:'+str(lenX)+ \
2823                                '\nFound:   '+str(len(x))+'\nfor '+name)
2824                            self.OnCancel(event)
2825                    else:
2826                        lenX = len(x)
2827                    if Xminmax[1]:
2828                        if Xminmax != [x[0],x[-1]]:
2829                            self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2830                                '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2831                                '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2832                            self.OnCancel(event)
2833                        else:
2834                            for j,yi in enumerate(y):
2835                                 Ysum[j] += scale*yi
2836                                 Vsum[j] += abs(scale)*v[j]
2837                    else:
2838                        Xminmax = [x[0],x[-1]]
2839                        Xsum = x
2840                        Ysum = scale*y
2841                        Vsum = abs(scale*v)
2842            Wsum = 1./np.array(Vsum)
2843            YCsum = np.zeros(lenX)
2844            YBsum = np.zeros(lenX)
2845            YDsum = np.zeros(lenX)
2846            XY.append([Xsum,Ysum])
2847            self.result = [Xsum,Ysum,Wsum,YCsum,YBsum,YDsum]
2848            # N.B. PlotXY expects the first arg to point to G2frame. In this case, we
2849            # create a duplicate (temporary) Plot notebook window that is a child of the
2850            # modal SumDialog dialog (self). This nicely gets deleted when the dialog is destroyed,
2851            # but the plot window is not fully functional, at least on the Mac.
2852            G2plt.PlotXY(self,XY,lines=True,Title='Sum:'+self.data[-1],labelY='Intensity',)
2853            self.plotFrame.Show()
2854                       
2855        def OnOk(self,event):
2856            if self.dataType == 'PWDR': self.OnTest(event)
2857            parent = self.GetParent()
2858            parent.Raise()
2859            self.EndModal(wx.ID_OK)             
2860           
2861        def OnCancel(self,event):
2862            parent = self.GetParent()
2863            parent.Raise()
2864            self.EndModal(wx.ID_CANCEL)             
2865           
2866        def GetData(self):
2867            if self.dataType == 'PWDR':
2868                return self.data,self.result
2869            else:
2870                return self.data
2871                       
2872    def OnPwdrSum(self,event):
2873        'Sum together powder data(?)'
2874        TextList = []
2875        DataList = []
2876        Names = []
2877        Inst = None
2878        Comments = ['Sum equals: \n']
2879        if self.PatternTree.GetCount():
2880            item, cookie = self.PatternTree.GetFirstChild(self.root)
2881            while item:
2882                name = self.PatternTree.GetItemText(item)
2883                Names.append(name)
2884                if 'PWDR' in name:
2885                    TextList.append([0.0,name])
2886                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2887                    if not Inst:
2888                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2889                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2890            if len(TextList) < 2:
2891                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2892                return
2893            TextList.append('default_sum_name')               
2894            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList,DataList)
2895            try:
2896                if dlg.ShowModal() == wx.ID_OK:
2897                    result,sumData = dlg.GetData()
2898                    Xsum,Ysum,Wsum,YCsum,YBsum,YDsum = sumData
2899                    Xminmax = [Xsum[0],Xsum[-1]]
2900                    outname = 'PWDR '+result[-1]
2901                    Id = 0
2902                    if outname in Names:
2903                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2904                        try:
2905                            if dlg2.ShowModal() == wx.ID_OK:
2906                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2907                                self.PatternTree.Delete(Id)
2908                        finally:
2909                            dlg2.Destroy()
2910                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2911                    if Id:
2912                        Sample = G2pdG.SetDefaultSample()
2913                        Ymin = np.min(Ysum)
2914                        Ymax = np.max(Ysum)
2915                        valuesdict = {
2916                            'wtFactor':1.0,
2917                            'Dummy':False,
2918                            'ranId':ran.randint(0,sys.maxint),
2919                            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
2920                            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
2921                            }
2922                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2923                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2924                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2925                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2926                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2927                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2928                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2929                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2930                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2931                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2932                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2933                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2934                        self.PatternTree.SelectItem(Id)
2935                        self.PatternTree.Expand(Id)
2936            finally:
2937                dlg.Destroy()
2938
2939    def OnImageSum(self,event):
2940        'Sum together image data'
2941        TextList = []
2942        DataList = []
2943        IdList = []
2944        Names = []
2945        Comments = ['Sum equals: \n']
2946        if self.PatternTree.GetCount():
2947            item, cookie = self.PatternTree.GetFirstChild(self.root)
2948            while item:
2949                name = self.PatternTree.GetItemText(item)
2950                Names.append(name)
2951                if 'IMG' in name:
2952                    TextList.append([0.0,name])
2953                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2954                    IdList.append(item)
2955                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2956                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2957            if len(TextList) < 2:
2958                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2959                return
2960            TextList.append('default_sum_name')               
2961            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList,DataList)
2962            try:
2963                if dlg.ShowModal() == wx.ID_OK:
2964                    imSize = 0
2965                    result = dlg.GetData()
2966                    First = True
2967                    Found = False
2968                    for i,item in enumerate(result[:-1]):
2969                        scale,name = item
2970                        if scale:
2971                            Found = True                               
2972                            Comments.append("%10.3f %s" % (scale,' * '+name))
2973                            Npix,imagefile,imagetag = DataList[i]
2974                            imagefile = G2IO.GetCheckImageFile(self,IdList[i])[1]
2975                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2976                            if First:
2977                                newImage = np.zeros_like(image)
2978                                First = False
2979                            if imSize:
2980                                if imSize != Npix:
2981                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2982                                        '\nExpected:'+str(imSize)+ \
2983                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2984                                    return
2985                                newImage = newImage+scale*image
2986                            else:
2987                                imSize = Npix
2988                                newImage = newImage+scale*image
2989                            del(image)
2990                    if not Found:
2991                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2992                        return
2993                       
2994                       
2995                    newImage = np.array(newImage,dtype=np.int32)                       
2996                    outname = 'IMG '+result[-1]
2997                    Id = 0
2998                    if outname in Names:
2999                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
3000                        try:
3001                            if dlg2.ShowModal() == wx.ID_OK:
3002                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
3003                        finally:
3004                            dlg2.Destroy()
3005                    else:
3006                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
3007                    if Id:
3008                        pth = G2G.GetExportPath(self)
3009                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth,outname.split('IMG ')[1], 
3010                            'G2img files (*.G2img)|*.G2img', 
3011                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
3012                        if dlg.ShowModal() == wx.ID_OK:
3013                            newimagefile = dlg.GetPath()
3014                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
3015                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
3016                            Imax = np.amax(newImage)
3017                            Imin = np.amin(newImage)
3018                            newImage = []
3019                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
3020                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
3021                        del(newImage)
3022                        if self.imageDefault:
3023                            Data = copy.copy(self.imageDefault)
3024                        Data['formatName'] = 'GSAS-II image'
3025                        Data['showLines'] = True
3026                        Data['ring'] = []
3027                        Data['rings'] = []
3028                        Data['cutoff'] = 10
3029                        Data['pixLimit'] = 20
3030                        Data['ellipses'] = []
3031                        Data['calibrant'] = ''
3032                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
3033                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
3034                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
3035                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
3036                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
3037                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
3038                        self.PatternTree.SelectItem(Id)
3039                        self.PatternTree.Expand(Id)
3040                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
3041                        self.Image = self.PickId
3042            finally:
3043                dlg.Destroy()
3044                     
3045    def OnAddPhase(self,event):
3046        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
3047        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3048            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
3049        else:
3050            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3051        PhaseName = ''
3052        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
3053            style=wx.OK)
3054        if dlg.ShowModal() == wx.ID_OK:
3055            PhaseName = dlg.GetValue()
3056        dlg.Destroy()
3057        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
3058        E,SGData = G2spc.SpcGroup('P 1')
3059        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
3060        G2gd.SelectDataTreeItem(self,sub) #bring up new phase General tab
3061       
3062    def OnDeletePhase(self,event):
3063        'Delete a phase from the tree. Called by Data/Delete Phase menu'
3064        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
3065        if self.dataFrame:
3066            self.dataFrame.Clear() 
3067        TextList = []
3068        DelList = []
3069        DelItemList = []
3070        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3071            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3072        else:
3073            return
3074        if sub:
3075            item, cookie = self.PatternTree.GetFirstChild(sub)
3076            while item:
3077                TextList.append(self.PatternTree.GetItemText(item))
3078                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
3079            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
3080            try:
3081                if dlg.ShowModal() == wx.ID_OK:
3082                    result = dlg.GetSelections()
3083                    for i in result: DelList.append([i,TextList[i]])
3084                    item, cookie = self.PatternTree.GetFirstChild(sub)
3085                    i = 0
3086                    while item:
3087                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
3088                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3089                        i += 1
3090                    for item in DelItemList:
3091                        name = self.PatternTree.GetItemText(item)
3092                        self.PatternTree.Delete(item)
3093                        self.G2plotNB.Delete(name)
3094                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3095                    while item:
3096                        name = self.PatternTree.GetItemText(item)
3097                        if 'PWDR' in name:
3098                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
3099                            refList = self.PatternTree.GetItemPyData(Id)
3100                            if len(refList):
3101                                for i,item in DelList:
3102                                    if item in refList:
3103                                        del(refList[item])
3104#                            self.PatternTree.SetItemPyData(Id,refList)
3105                        elif 'HKLF' in name:
3106                            data = self.PatternTree.GetItemPyData(item)
3107                            data[0] = {}
3108#                            self.PatternTree.SetItemPyData(item,data)
3109                           
3110                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3111            finally:
3112                dlg.Destroy()
3113               
3114    def OnRenameData(self,event):
3115        'Renames an existing phase. Called by Data/Rename Phase menu'
3116        name = self.PatternTree.GetItemText(self.PickId)     
3117        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
3118            if 'Bank' in name:
3119                names = name.split('Bank')
3120                names[1] = ' Bank'+names[1]
3121            elif 'Azm' in name:
3122                names = name.split('Azm')
3123                names[1] = ' Azm'+names[1]
3124            else:
3125                names = [name,'']
3126            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
3127            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
3128                defaultValue=names[0][names[0].index(' ')+1:])
3129            try:
3130                if dlg.ShowModal() == wx.ID_OK:
3131                    name = dataType+dlg.GetValue()+names[1]
3132                    self.PatternTree.SetItemText(self.PickId,name)
3133            finally:
3134                dlg.Destroy()
3135       
3136    def GetFileList(self,fileType,skip=None):        #potentially useful?
3137        'Appears unused. Note routine of same name in GSASIIpwdGUI'
3138        fileList = []
3139        Source = ''
3140        id, cookie = self.PatternTree.GetFirstChild(self.root)
3141        while id:
3142            name = self.PatternTree.GetItemText(id)
3143            if fileType in name:
3144                if id == skip:
3145                    Source = name
3146                else:
3147                    fileList.append([False,name,id])
3148            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3149        if skip:
3150            return fileList,Source
3151        else:
3152            return fileList
3153           
3154    def OnDataDelete(self, event):
3155        '''Delete one or more histograms from data tree. Called by the
3156        Data/DeleteData menu
3157        '''
3158        TextList = []
3159        DelList = []
3160        DelItemList = []
3161        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
3162        PDFnames = []
3163        if self.PatternTree.GetCount():
3164            item, cookie = self.PatternTree.GetFirstChild(self.root)
3165            while item:
3166                name = self.PatternTree.GetItemText(item)
3167                if name not in ['Notebook','Controls','Covariance','Constraints',
3168                    'Restraints','Phases','Rigid bodies'] and 'Sequential' not in name:
3169                    if 'PWDR' in name: nItems['PWDR'] += 1
3170                    if 'SASD' in name: nItems['SASD'] += 1
3171                    if 'IMG' in name:  nItems['IMG'] += 1
3172                    if 'HKLF' in name: nItems['HKLF'] += 1
3173                    if 'PDF' in name:
3174                        PDFnames.append(name)
3175                        nItems['PDF'] += 1
3176                    TextList.append(name)
3177                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3178            for pdfName in PDFnames:
3179                TextList.remove('PWDR'+pdfName[4:])
3180            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
3181            try:
3182                if dlg.ShowModal() == wx.ID_OK:
3183                    result = dlg.GetSelections()
3184                    for i in result: DelList.append(TextList[i])
3185                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3186                    while item:
3187                        itemName = self.PatternTree.GetItemText(item)
3188                        if itemName in DelList:
3189                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
3190                            elif 'SASD' in itemName: nItems['SASD'] -= 1
3191                            elif 'IMG' in itemName: nItems['IMG'] -= 1
3192                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
3193                            elif 'PDF' in itemName: nItems['PDF'] -= 1
3194                            DelItemList.append(item)
3195                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3196                    for item in DelItemList:
3197                        self.PatternTree.Delete(item)
3198                    self.PickId = 0
3199                    self.PickIdText = None
3200                    self.PatternId = 0
3201                    if nItems['PWDR']:
3202                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3203                    else:
3204                        self.G2plotNB.Delete('Powder Patterns')
3205                    if not nItems['IMG']:
3206                        self.G2plotNB.Delete('2D Powder Image')
3207                    if not nItems['HKLF']:
3208                        self.G2plotNB.Delete('Structure Factors')
3209                        if '3D Structure Factors' in self.G2plotNB.plotList:
3210                            self.G2plotNB.Delete('3D Structure Factors')
3211            finally:
3212                dlg.Destroy()
3213
3214    def OnFileOpen(self, event, filename=None):
3215        '''Gets a GSAS-II .gpx project file in response to the
3216        File/Open Project menu button
3217        '''
3218        result = wx.ID_OK
3219        self.EnablePlot = False
3220        if self.PatternTree.GetChildrenCount(self.root,False):
3221            if self.dataFrame:
3222                self.dataFrame.Clear() 
3223            dlg = wx.MessageDialog(
3224                self,
3225                'Do you want to overwrite the current project? '+
3226                'Any unsaved changes in current project will be lost. Press OK to continue.',
3227                'Overwrite?',  wx.OK | wx.CANCEL)
3228            try:
3229                result = dlg.ShowModal()
3230                if result == wx.ID_OK:
3231                    self.PatternTree.DeleteChildren(self.root)
3232                    self.GSASprojectfile = ''
3233                    self.HKL = []
3234                    if self.G2plotNB.plotList:
3235                        self.G2plotNB.clear()
3236            finally:
3237                dlg.Destroy()
3238        if result != wx.ID_OK: return
3239
3240        if not filename:
3241            if self.dataDisplay: self.dataDisplay.Destroy()
3242            if self.LastGPXdir:
3243                pth = self.LastGPXdir
3244            else:
3245                pth = '.'
3246            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3247                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3248            try:
3249                if dlg.ShowModal() != wx.ID_OK: return
3250                self.GSASprojectfile = dlg.GetPath()
3251                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3252                self.dirname = dlg.GetDirectory()
3253            finally:
3254                dlg.Destroy()
3255        else:
3256            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3257            self.dirname = os.path.split(filename)[0]
3258
3259        try:
3260            self.StartProject()         #open the file if possible
3261        except:
3262            print '\nError opening file ',filename
3263            import traceback
3264            print traceback.format_exc()
3265       
3266    def StartProject(self):
3267        '''Opens a GSAS-II project file & selects the 1st available data set to
3268        display (PWDR, HKLF or SASD)
3269        '''
3270       
3271        Id = 0
3272        phaseId = None
3273        G2IO.ProjFileOpen(self)
3274        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3275        self.PatternTree.Expand(self.root)
3276        self.HKL = []
3277        item, cookie = self.PatternTree.GetFirstChild(self.root)
3278        while item and not Id:
3279            name = self.PatternTree.GetItemText(item)
3280            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3281                Id = item
3282            elif name == "Phases":
3283                phaseId = item
3284            elif name == 'Controls':
3285                data = self.PatternTree.GetItemPyData(item)
3286                if data:
3287                    for item in self.Refine: item.Enable(True)
3288                    self.EnableSeqRefineMenu()
3289            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3290        if phaseId: # show all phases
3291            self.PatternTree.Expand(phaseId)
3292        if Id:
3293            self.EnablePlot = True
3294            self.PatternTree.SelectItem(Id)
3295            self.PatternTree.Expand(Id)
3296        elif phaseId:
3297            self.PatternTree.SelectItem(phaseId)
3298        self.CheckNotebook()
3299        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3300        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3301        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3302        self.LastGPXdir = pth
3303
3304    def OnFileClose(self, event):
3305        '''Clears the data tree in response to the
3306        File/New Project menu button. User is given option to save
3307        the project.
3308        '''
3309        if self.dataFrame:
3310            self.dataFrame.Clear()
3311            self.dataFrame.SetLabel('GSAS-II data display') 
3312        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3313        try:
3314            result = dlg.ShowModal()
3315            if result == wx.ID_OK:
3316                self.OnFileSaveMenu(event)
3317            if result != wx.ID_CANCEL:
3318                self.GSASprojectfile = ''
3319                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3320                self.PatternTree.DeleteChildren(self.root)
3321                if self.HKL: self.HKL = []
3322                if self.G2plotNB.plotList:
3323                    self.G2plotNB.clear()
3324        finally:
3325            dlg.Destroy()
3326
3327    def OnFileSave(self, event):
3328        '''Save the current project in response to the
3329        File/Save Project menu button
3330        '''
3331       
3332        if self.GSASprojectfile: 
3333            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3334            self.CheckNotebook()
3335            G2IO.ProjFileSave(self)
3336        else:
3337            self.OnFileSaveas(event)
3338
3339    def OnFileSaveas(self, event):
3340        '''Save the current project in response to the
3341        File/Save as menu button
3342        '''
3343        if GSASIIpath.GetConfigValue('Starting_directory'):
3344            pth = GSASIIpath.GetConfigValue('Starting_directory')
3345            pth = os.path.expanduser(pth) 
3346        elif self.LastGPXdir:
3347            pth = self.LastGPXdir
3348        else:
3349            pth = '.'
3350        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3351            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3352        try:
3353            if dlg.ShowModal() == wx.ID_OK:
3354                self.GSASprojectfile = dlg.GetPath()
3355                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3356                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3357                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3358                self.CheckNotebook()
3359                G2IO.ProjFileSave(self)
3360                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3361        finally:
3362            dlg.Destroy()
3363           
3364    def ExpandAll(self,event):
3365        '''Expand all tree items or those of a single type
3366        '''
3367        txt = self.GetMenuBar().GetLabel(event.Id)
3368        if txt == 'all':
3369            self.ExpandingAll = True
3370            try:
3371                self.PatternTree.ExpandAll()
3372            finally:
3373                self.ExpandingAll = False
3374        else:
3375            self.ExpandingAll = True
3376            try:
3377                item, cookie = self.PatternTree.GetFirstChild(self.root)
3378                while item:
3379                    name = self.PatternTree.GetItemText(item)
3380                    if name.startswith(txt+' '): self.PatternTree.Expand(item)
3381                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3382            finally:
3383                self.ExpandingAll = False
3384
3385    def MoveTreeItems(self,event):
3386        '''Move tree items of a single type to the end of the tree
3387        '''
3388        txt = self.GetMenuBar().GetLabel(event.Id)
3389        # make a list of items to copy
3390        copyList = []
3391        item, cookie = self.PatternTree.GetFirstChild(self.root)
3392        while item:
3393            if self.PatternTree.GetItemText(item).startswith(txt+' '):
3394                copyList.append(item)
3395            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3396       
3397        self.ExpandingAll = True
3398        try:
3399            for item in copyList:
3400                name = self.PatternTree.GetItemText(item)
3401                newId = self.PatternTree.AppendItem(self.root,name)
3402                self.PatternTree.SetItemPyData(newId,self.PatternTree.GetItemPyData(item))
3403                chld, chldcookie = self.PatternTree.GetFirstChild(item)
3404                while chld:
3405                    chname = self.PatternTree.GetItemText(chld)
3406                    newCh = self.PatternTree.AppendItem(newId,chname)
3407                    self.PatternTree.SetItemPyData(newCh,self.PatternTree.GetItemPyData(chld))
3408                    chld, chldcookie = self.PatternTree.GetNextChild(item, chldcookie)
3409                self.PatternTree.Delete(item)
3410        finally:
3411            self.ExpandingAll = False
3412        G2gd.SelectDataTreeItem(self,self.root)
3413           
3414    def ExitMain(self, event):
3415        '''Called if the main window is closed'''
3416        if self.G2plotNB:
3417            self.G2plotNB.Destroy()
3418        if self.dataFrame:
3419            self.dataFrame.Clear() 
3420            self.dataFrame.Destroy()
3421        if self.undofile:
3422            os.remove(self.undofile)
3423        sys.exit()
3424       
3425    def OnFileExit(self, event):
3426        '''Called in response to the File/Quit menu button'''
3427        if self.G2plotNB:
3428            self.G2plotNB.Destroy()
3429        if self.dataFrame:
3430            self.dataFrame.Clear() 
3431            self.dataFrame.Destroy()
3432        self.Close()
3433       
3434    def OnExportPeakList(self,event):
3435        nptand = lambda x: np.tan(x*math.pi/180.)
3436        pth = G2G.GetExportPath(self)
3437        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3438            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3439        try:
3440            if dlg.ShowModal() == wx.ID_OK:
3441                self.peaklistfile = dlg.GetPath()
3442                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3443                file = open(self.peaklistfile,'w')               
3444                item, cookie = self.PatternTree.GetFirstChild(self.root)
3445                while item:
3446                    name = self.PatternTree.GetItemText(item)
3447                    if 'PWDR' in name:
3448                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3449                        wave = 0.0
3450                        while item2:
3451                            name2 = self.PatternTree.GetItemText(item2)
3452                            if name2 == 'Instrument Parameters':
3453                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3454                                Type = Inst['Type'][0]
3455                                if 'T' not in Type:
3456                                    wave = G2mth.getWave(Inst)
3457                            elif name2 == 'Peak List':
3458                                pkdata = self.PatternTree.GetItemPyData(item2)
3459                                peaks = pkdata['peaks']
3460                                sigDict = pkdata['sigDict']
3461                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3462                        file.write("#%s \n" % (name+' Peak List'))
3463                        if wave:
3464                            file.write('#wavelength = %10.6f\n'%(wave))
3465                        if 'T' in Type:
3466                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3467                        else:
3468                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3469                        for ip,peak in enumerate(peaks):
3470                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3471                            if 'T' in Type:  #TOF - more cols
3472                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3473                                for name in esds.keys():
3474                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3475                                sig = np.sqrt(peak[8])
3476                                gam = peak[10]
3477                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3478                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3479                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3480                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3481                            else:               #CW
3482                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3483                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3484                                for name in esds.keys():
3485                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3486                                sig = np.sqrt(peak[4]) #var -> sig
3487                                gam = peak[6]
3488                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3489                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3490                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3491                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3492                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3493                file.close()
3494        finally:
3495            dlg.Destroy()
3496       
3497    def OnExportHKL(self,event):
3498        pth = G2G.GetExportPath(self)
3499        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3500            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3501        try:
3502            if dlg.ShowModal() == wx.ID_OK:
3503                self.peaklistfile = dlg.GetPath()
3504                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3505                file = open(self.peaklistfile,'w')               
3506                item, cookie = self.PatternTree.GetFirstChild(self.root)
3507                while item:
3508                    name = self.PatternTree.GetItemText(item)
3509                    if 'PWDR' in name:
3510                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3511                        while item2:
3512                            name2 = self.PatternTree.GetItemText(item2)
3513                            if name2 == 'Reflection Lists':
3514                                data = self.PatternTree.GetItemPyData(item2)
3515                                phases = data.keys()
3516                                for phase in phases:
3517                                    peaks = data[phase]
3518                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3519                                    if 'T' in peaks.get('Type','PXC'):
3520                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3521                                    else:               
3522                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3523                                    for peak in peaks['RefList']:
3524                                        if 'T' in peaks.get('Type','PXC'):
3525                                            sig = np.sqrt(peak[6])
3526                                            gam = peak[7]
3527                                            FWHM = G2pwd.getgamFW(gam,sig)
3528                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3529                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3530                                        else:
3531                                            sig = np.sqrt(peak[6])
3532                                            gam = peak[7]
3533                                            FWHM = G2pwd.getgamFW(gam,sig)
3534                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3535                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3536                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3537                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3538                file.close()
3539        finally:
3540            dlg.Destroy()
3541       
3542    def OnExportPDF(self,event):
3543        #need S(Q) and G(R) to be saved here - probably best from selection?
3544        names = ['All']
3545        exports = []
3546        item, cookie = self.PatternTree.GetFirstChild(self.root)
3547        while item:
3548            name = self.PatternTree.GetItemText(item)
3549            if 'PDF' in name:
3550                names.append(name)
3551            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3552        if names:
3553            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3554            if dlg.ShowModal() == wx.ID_OK:
3555                sel = dlg.GetSelections()
3556                if sel[0] == 0:
3557                    exports = names[1:]
3558                else:
3559                    for x in sel:
3560                        exports.append(names[x])
3561            dlg.Destroy()
3562        if exports:
3563            G2IO.PDFSave(self,exports)
3564       
3565    def OnMakePDFs(self,event):
3566        '''Sets up PDF data structure filled with defaults; if found chemical formula is inserted
3567        so a default PDF can be made.
3568        '''
3569        sind = lambda x: math.sin(x*math.pi/180.)
3570        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3571        tof2q = lambda t,C:2.0*math.pi*C/t
3572        TextList = []
3573        ElLists = []
3574        Qlimits = []
3575        Names = []
3576        if self.PatternTree.GetCount():
3577            id, cookie = self.PatternTree.GetFirstChild(self.root)
3578            while id:
3579                name = self.PatternTree.GetItemText(id)
3580                Names.append(name)
3581                if 'PWDR' in name:
3582                    TextList.append(name)
3583                    Data = self.PatternTree.GetItemPyData(id)[1]
3584                    pwdrMin = np.min(Data[1])
3585                    Comments = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Comments'))
3586                    Parms = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Instrument Parameters'))[0]
3587                    fullLimits = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Limits'))[0]
3588                    if 'C' in Parms['Type'][0]:
3589                        wave = G2mth.getWave(Parms)
3590                        qMax = tth2q(fullLimits[1],wave)
3591                    else:   #'T'of
3592                        qMax = tof2q(fullLimits[0],Parms['difC'][1])
3593                    Qlimits.append([0.9*qMax,qMax])
3594                    ElList = {}
3595                    for item in Comments:           #grab chemical formula from Comments
3596                        if 'formula' in item[:15].lower():
3597                            formula = item.split('=')[1].split()
3598                            elems = formula[::2]
3599                            nums = formula[1::2]
3600                            formula = zip(elems,nums)
3601                            for [elem,num] in formula:
3602                                ElData = G2elem.GetElInfo(elem,Parms)
3603                                ElData['FormulaNo'] = float(num)
3604                                ElList[elem] = ElData
3605                    ElLists.append(ElList)
3606                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3607            if len(TextList) < 1:
3608                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3609                return
3610            dlg = G2G.G2MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3611            try:
3612                if dlg.ShowModal() == wx.ID_OK:
3613                    for i in dlg.GetSelections():
3614                        PDFnames = G2gd.GetPatternTreeDataNames(self,['PDF ',])
3615                        G2obj.CreatePDFitems(self,TextList[i],ElLists[i],Qlimits[i],pwdrMin,PDFnames)
3616                for item in self.ExportPDF: item.Enable(True)
3617            finally:
3618                dlg.Destroy()
3619               
3620    def GetPWDRdatafromTree(self,PWDRname):
3621        ''' Returns powder data from GSASII tree
3622
3623        :param str PWDRname: a powder histogram name as obtained from
3624          :meth:`GSASIIstruct.GetHistogramNames`
3625
3626        :returns: PWDRdata = powder data dictionary with
3627          Powder data arrays, Limits, Instrument Parameters,
3628          Sample Parameters           
3629        '''
3630        PWDRdata = {}
3631        try:
3632            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3633        except ValueError:
3634            PWDRdata['wtFactor'] = 1.0
3635        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3636        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3637        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3638        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3639        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3640        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3641        if 'ranId' not in PWDRdata:  # patch, add a random Id
3642            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3643        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3644            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3645        return PWDRdata
3646
3647    def GetHKLFdatafromTree(self,HKLFname):
3648        ''' Returns single crystal data from GSASII tree
3649
3650        :param str HKLFname: a single crystal histogram name as obtained
3651          from
3652          :meth:`GSASIIstruct.GetHistogramNames`
3653
3654        :returns: HKLFdata = single crystal data list of reflections
3655
3656        '''
3657        HKLFdata = {}
3658        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3659#        try:
3660#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3661#        except ValueError:
3662#            HKLFdata['wtFactor'] = 1.0
3663        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3664        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3665        return HKLFdata
3666       
3667    def GetPhaseData(self):
3668        '''Returns a dict with defined phases.
3669        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3670        get same info from GPX file.
3671        '''
3672        phaseData = {}
3673        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3674            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3675        else:
3676            print 'no phases found in GetPhaseData'
3677            sub = None
3678        if sub:
3679            item, cookie = self.PatternTree.GetFirstChild(sub)
3680            while item:
3681                phaseName = self.PatternTree.GetItemText(item)
3682                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3683                if 'ranId' not in phaseData[phaseName]:
3684                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3685                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3686        return phaseData
3687
3688    def GetPhaseInfofromTree(self):
3689        '''Get the phase names and their rId values,
3690        also the histograms used in each phase.
3691
3692        :returns: (phaseRIdList, usedHistograms) where
3693
3694          * phaseRIdList is a list of random Id values for each phase
3695          * usedHistograms is a dict where the keys are the phase names
3696            and the values for each key are a list of the histogram names
3697            used in each phase.
3698        '''
3699        phaseRIdList = []
3700        usedHistograms = {}
3701        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3702        if sub:
3703            item, cookie = self.PatternTree.GetFirstChild(sub)
3704            while item:
3705                phaseName = self.PatternTree.GetItemText(item)
3706                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3707                if ranId: phaseRIdList.append(ranId)
3708                data = self.PatternTree.GetItemPyData(item)
3709                UseList = data['Histograms']
3710                usedHistograms[phaseName] = UseList.keys()
3711                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3712        return phaseRIdList,usedHistograms
3713
3714    def GetPhaseNames(self):
3715        '''Returns a list of defined phases.
3716        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3717        get same info from GPX file.
3718        '''
3719        phaseNames = []
3720        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3721            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3722        else:
3723            print 'no phases found in GetPhaseNames'
3724            sub = None
3725        if sub:
3726            item, cookie = self.PatternTree.GetFirstChild(sub)
3727            while item:
3728                phase = self.PatternTree.GetItemText(item)
3729                phaseNames.append(phase)
3730                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3731        return phaseNames
3732   
3733    def GetHistogramNames(self,hType):
3734        """ Returns a list of histogram names found in the GSASII data tree
3735        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3736        get same info from GPX file.
3737       
3738        :param str hType: list of histogram types
3739        :return: list of histogram names
3740       
3741        """
3742        HistogramNames = []
3743        if self.PatternTree.GetCount():
3744            item, cookie = self.PatternTree.GetFirstChild(self.root)
3745            while item:
3746                name = self.PatternTree.GetItemText(item)
3747                if name[:4] in hType:
3748                    HistogramNames.append(name)       
3749                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3750
3751        return HistogramNames
3752                   
3753    def GetUsedHistogramsAndPhasesfromTree(self):
3754        ''' Returns all histograms that are found in any phase
3755        and any phase that uses a histogram.
3756        This also assigns numbers to used phases and histograms by the
3757        order they appear in the file.
3758        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3759        get same info from GPX file.
3760
3761        :returns: (Histograms,Phases)
3762
3763            * Histograms = dictionary of histograms as {name:data,...}
3764            * Phases = dictionary of phases that use histograms
3765        '''
3766        Histograms = {}
3767        Phases = {}
3768        phaseNames = self.GetPhaseNames()
3769        phaseData = self.GetPhaseData()
3770        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3771
3772        for phase in phaseData:
3773            Phase = phaseData[phase]
3774            pId = phaseNames.index(phase)
3775            Phase['pId'] = pId
3776            if Phase['Histograms']:
3777                if phase not in Phases:
3778                    Phases[phase] = Phase
3779                for hist in Phase['Histograms']:
3780                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3781                        Phase['Histograms'][hist]['Use'] = True         
3782                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3783                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3784                        if item:
3785                            if 'PWDR' in hist[:4]: 
3786                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3787                            elif 'HKLF' in hist[:4]:
3788                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3789                            hId = histoList.index(hist)
3790                            Histograms[hist]['hId'] = hId
3791                        else: # would happen if a referenced histogram were renamed or deleted
3792                            print('For phase "'+phase+
3793                                  '" unresolved reference to histogram "'+hist+'"')
3794        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3795        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3796        return Histograms,Phases
3797       
3798    def MakeLSParmDict(self):
3799        '''Load all parameters used for computation from the tree into a
3800        dict of paired values [value, refine flag]. Note that this is
3801        different than the parmDict used in the refinement, which only has
3802        values.
3803
3804        Note that similar things are done in
3805        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3806        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3807        a GPX file).
3808
3809        :returns: (parmDict,varyList) where:
3810
3811         * parmDict is a dict with values and refinement flags
3812           for each parameter and
3813         * varyList is a list of variables (refined parameters).
3814        '''
3815        parmDict = {}
3816        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3817        for phase in Phases:
3818            if 'pId' not in Phases[phase]:
3819                self.ErrorDialog('View parameter error','You must run least squares at least once')
3820                raise Exception,'No pId for phase '+str(phase)
3821        rigidbodyDict = self.PatternTree.GetItemPyData(   
3822            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3823        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3824        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3825        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3826        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3827        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3828        varyList = rbVary+phaseVary+hapVary+histVary
3829        parmDict.update(rbDict)
3830        parmDict.update(phaseDict)
3831        parmDict.update(hapDict)
3832        parmDict.update(histDict)
3833        for parm in parmDict:
3834            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3835                'Omega','Chi','Phi','nDebye','nPeaks']:
3836                parmDict[parm] = [parmDict[parm],'-']
3837            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3838                parmDict[parm] = [parmDict[parm],'-']
3839            elif parm in varyList:
3840                parmDict[parm] = [parmDict[parm],'T']
3841            else:
3842                parmDict[parm] = [parmDict[parm],'F']
3843        # for i in parmDict: print i,'\t',parmDict[i]
3844        # fl = open('parmDict.dat','wb')
3845        # import cPickle
3846        # cPickle.dump(parmDict,fl,1)
3847        # fl.close()
3848        return parmDict,varyList
3849
3850    def OnShowLSParms(self,event):
3851        '''Displays a window showing all parameters in the refinement.
3852        Called from the Calculate/View LS Parms menu.
3853        '''
3854        time0 = time.time()
3855        parmDict,varyList = self.MakeLSParmDict()
3856        parmValDict = {}
3857        for i in parmDict:
3858            parmValDict[i] = parmDict[i][0]
3859           
3860        reqVaryList = tuple(varyList) # save requested variables
3861        try:
3862            # process constraints
3863            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3864            Constraints = self.PatternTree.GetItemPyData(sub)
3865            constList = []
3866            for item in Constraints:
3867                if item.startswith('_'): continue
3868                constList += Constraints[item]
3869            G2mv.InitVars()
3870            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3871            groups,parmlist = G2mv.GroupConstraints(constrDict)
3872            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3873            G2mv.Map2Dict(parmValDict,varyList)
3874        except:
3875            pass
3876        print ' Setup time: %.3f'%(time.time()-time0)
3877        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3878        dlg.ShowModal()
3879        dlg.Destroy()
3880
3881    def OnRefine(self,event):
3882        '''Perform a refinement.
3883        Called from the Calculate/Refine menu.
3884        '''
3885        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3886        if Id:
3887            dlg = wx.MessageDialog(
3888                self,
3889                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3890                'Remove sequential results?',wx.OK|wx.CANCEL)
3891            if dlg.ShowModal() == wx.ID_OK:
3892                self.PatternTree.Delete(Id)
3893                dlg.Destroy()
3894            else:
3895                dlg.Destroy()
3896                return
3897        self.OnFileSave(event)
3898        # check that constraints are OK here
3899        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3900        if errmsg:
3901            self.ErrorDialog('Refinement error',errmsg)
3902            return
3903        if warnmsg:
3904            print('Conflict between refinment flag settings and constraints:\n'+
3905                warnmsg+'\nRefinement not possible')
3906            self.ErrorDialog('Refinement Flag Error',
3907                'Conflict between refinement flag settings and constraints:\n'+
3908                warnmsg+'\nRefinement not possible')
3909            return
3910        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3911            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3912            parent=self)
3913        Size = dlg.GetSize()
3914        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3915            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3916        dlg.CenterOnParent()
3917        Rw = 100.00
3918        self.SaveTreeSetting()
3919        self.PatternTree.SaveExposedItems()       
3920        try:
3921            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)    #Msg is Rvals dict if Ok=True
3922        finally:
3923            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3924            dlg.Destroy()
3925            wx.Yield()
3926        if OK:
3927            Rw = Msg['Rwp']
3928            lamMax = Msg.get('lamMax',0.001)
3929            text = 'Load new result?'
3930            if lamMax >= 10.:
3931                text += '\nWARNING: Steepest descents dominates;'+   \
3932                ' minimum may not have been reached\nor result may be false minimum.'+  \
3933                ' You should reconsider your parameter suite'
3934            dlg2 = wx.MessageDialog(self,text,'Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3935            try:
3936                if dlg2.ShowModal() == wx.ID_OK:
3937                    self.PatternTree.DeleteChildren(self.root)
3938                    self.HKL = []
3939                    G2IO.ProjFileOpen(self,False)
3940                    self.PatternTree.RestoreExposedItems()       
3941                    self.ResetPlots()
3942            finally:
3943                dlg2.Destroy()
3944        else:
3945            self.ErrorDialog('Refinement error',Msg)
3946       
3947    def SaveTreeSetting(self):
3948        'Save the last tree setting'
3949        oldId =  self.PatternTree.GetSelection()        #retain current selection
3950        oldPath = self.GetTreeItemsList(oldId)
3951        self.lastTreeSetting = oldPath
3952        # note that for reasons unclear, it does not seem necessary to reload the Atoms tab
3953        #parentName = ''
3954        #tabId = None
3955        # parentId = self.PatternTree.GetItemParent(oldId)
3956        # if parentId:
3957        #     parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3958        #     if 'Phases' in parentName:
3959        #         tabId = self.dataDisplay.GetSelection()
3960        #self.lastTreeSetting = oldPath,tabId
3961        #GSASIIpath.IPyBreak()
3962       
3963    def TestResetPlot(self,event):
3964        '''Debug code to test cleaning up plots after a refinement'''
3965        #for i in range(self.G2plotNB.nb.GetPageCount()):
3966        #    [self.G2plotNB.nb.GetPageText(i)
3967        # save current tree item and (if needed) atoms tab
3968        self.SaveTreeSetting()
3969        self.ResetPlots()
3970       
3971    def ResetPlots(self):
3972        '''This reloads the current tree item, often drawing a plot. It refreshes any plots
3973        that have registered a refresh routine (see G2plotNB.RegisterRedrawRoutine)
3974        and deletes all plots that have not been refreshed and
3975        require one (see G2plotNB.SetNoDelete).
3976        '''
3977        lastRaisedPlotTab = self.G2plotNB.lastRaisedPlotTab # save the last page saved
3978        #print 'lastRaisedPlotTab=',lastRaisedPlotTab
3979        self.G2plotNB.lastRaisedPlotTab = None
3980        # mark displayed plots as invalid
3981        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
3982            frame.plotInvalid = True
3983        # reload current tree item, triggering the routine to redraw the data window and possibly a plot
3984        #oldPath,tabId = self.lastTreeSetting
3985        oldPath = self.lastTreeSetting
3986        Id = self.root
3987        for txt in oldPath:
3988            Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3989        self.PickIdText = None  #force reload of page
3990        if Id:
3991            self.PickId = Id
3992            self.PatternTree.SelectItem(Id)
3993        # update other self-updating plots
3994        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
3995            if frame.plotInvalid and frame.replotFunction:
3996                frame.replotFunction(*frame.replotArgs,**frame.replotKWargs)
3997        # delete any remaining plots that are still invalid and need a refresh
3998        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
3999            if frame.plotInvalid and frame.plotRequiresRedraw:
4000                self.G2plotNB.Delete(lbl)
4001        # put the previously last-raised tab on top, if present. If not, use the one corresponding to
4002        # the last tree item to be selected
4003        wx.CallAfter(self.G2plotNB.RaiseLastPage,lastRaisedPlotTab,self.G2plotNB.lastRaisedPlotTab)
4004       
4005    def OnSeqRefine(self,event):
4006        '''Perform a sequential refinement.
4007        Called from the Calculate/Sequential refine menu.
4008        '''
4009        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4010        if not Id:
4011            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
4012            self.PatternTree.SetItemPyData(Id,{})           
4013        self.G2plotNB.Delete('Sequential refinement')    #clear away probably invalid plot
4014        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
4015        if not Controls.get('Seq Data'):
4016            print('Error: a sequential refinement has not been set up')
4017            return
4018        Controls['ShowCell'] = True
4019        self.OnFileSave(event)
4020        # check that constraints are OK here
4021        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
4022        if errmsg:
4023            self.ErrorDialog('Refinement error',errmsg)
4024            return
4025        if warnmsg:
4026            print('Conflict between refinment flag settings and constraints:\n'+
4027                  warnmsg+'\nRefinement not possible')
4028            self.ErrorDialog('Refinement Flag Error',
4029                             'Conflict between refinment flag settings and constraints:\n'+
4030                             warnmsg+'\nRefinement not possible')
4031            return
4032        self.PatternTree.SaveExposedItems()       
4033        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
4034            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
4035            parent=self)           
4036        Size = dlg.GetSize()
4037        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
4038            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
4039        dlg.CenterOnParent()
4040        try:
4041            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)     #Msg is Rvals dict if Ok=True
4042        finally:
4043            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
4044            dlg.Destroy()
4045            wx.Yield()
4046        if OK:
4047            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
4048            try:
4049                if dlg.ShowModal() == wx.ID_OK:
4050                    self.PickIdText = None  #force reload of PickId contents
4051                    self.PatternTree.DeleteChildren(self.root)
4052                    if len(self.HKL): self.HKL = []
4053                    if self.G2plotNB.plotList:
4054                        self.G2plotNB.clear()
4055                    G2IO.ProjFileOpen(self,False)
4056                    self.PatternTree.RestoreExposedItems()
4057                    self.ResetPlots()
4058                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4059                    self.PatternTree.SelectItem(Id)
4060            finally:
4061                dlg.Destroy()
4062        else:
4063            self.ErrorDialog('Sequential refinement error',Msg)
4064       
4065    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
4066        'Display an error message'
4067        result = None
4068        if parent is None:
4069            dlg = wx.MessageDialog(self, message, title,  wtype)
4070        else:
4071            dlg = wx.MessageDialog(parent, message, title,  wtype)
4072            dlg.CenterOnParent() # not working on Mac
4073        try:
4074            result = dlg.ShowModal()
4075        finally:
4076            dlg.Destroy()
4077        return result
4078   
4079    def OnSaveMultipleImg(self,event):
4080        '''Select and save multiple image parameter and mask files
4081        '''
4082        G2IO.SaveMultipleImg(self)
4083       
4084class GSASIImain(wx.App):
4085    '''Defines a wxApp for GSAS-II
4086
4087    Creates a wx frame (self.main) which contains the display of the
4088    data tree.
4089    '''
4090    def OnInit(self):
4091        '''Called automatically when the app is created.'''
4092        import platform
4093        if '2.7' not in sys.version[:5]:
4094            dlg = wx.MessageDialog(None, 
4095                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
4096                'Python version error',  wx.OK)
4097            try:
4098                dlg.ShowModal()
4099            finally:
4100                dlg.Destroy()
4101            sys.exit()
4102        self.main = GSASII(None)
4103        self.main.Show()
4104        self.SetTopWindow(self.main)
4105        # save the current package versions
4106        self.main.PackageVersions = []
4107        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
4108        for p in (wx,mpl,np,sp,ogl):
4109            self.main.PackageVersions.append([p.__name__,p.__version__])
4110        try:
4111            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
4112        except:
4113            try:
4114                from PIL import PILLOW_VERSION
4115                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
4116            except:
4117                pass
4118        self.main.PackageVersions.append(['Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
4119       
4120        return True
4121    # def MacOpenFile(self, filename):
4122    #     '''Called on Mac every time a file is dropped on the app when it is running,
4123    #     treat this like a File/Open project menu action.
4124    #     Should be ignored on other platforms
4125    #     '''
4126    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
4127    #     print 'MacOpen',filename
4128    #     if os.path.splitext(filename)[1] == '.py': return
4129    #     # end PATCH
4130    #     self.main.OnFileOpen(None,filename)
4131    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
4132       
4133def main():
4134    '''Start up the GSAS-II application'''
4135    #application = GSASIImain() # don't redirect output, someday we
4136    # may want to do this if we can
4137    application = GSASIImain(0)
4138    if GSASIIpath.GetConfigValue('wxInspector'):
4139        import wx.lib.inspection as wxeye
4140        wxeye.InspectionTool().Show()
4141
4142    #application.main.OnRefine(None)
4143    application.MainLoop()
4144   
4145if __name__ == '__main__':
4146    # print versions
4147    print "Python module versions loaded:"
4148    print "  Python:     ",sys.version.split()[0]
4149    print "  wx:         ",wx.__version__
4150    print "  matplotlib: ",mpl.__version__
4151    print "  numpy:      ",np.__version__
4152    print "  scipy:      ",sp.__version__
4153    print "  OpenGL:     ",ogl.__version__
4154    try:
4155        from PIL import Image
4156        try:
4157            from PIL import PILLOW_VERSION
4158            version = PILLOW_VERSION
4159        except:
4160            version = Image.VERSION
4161        print "  PIL.Image:  ",version
4162    except ImportError:
4163        try:
4164            import Image
4165            print "Image (PIL):",Image.VERSION
4166        except ImportError:
4167            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
4168    import platform
4169    print "  Platform:   ",sys.platform,platform.architecture()[0],platform.machine()
4170    try:
4171        import mkl
4172        print "  Max threads:",mkl.get_max_threads()
4173    except:
4174        pass
4175    #print "wxPython description",wx.PlatformInfo
4176    print "This is GSAS-II revision "+str(GSASIIpath.GetVersionNumber())+'\n'
4177    GSASIIpath.InvokeDebugOpts()
4178    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.