source: trunk/GSASII.py @ 2532

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

clean up display of version info. etc. on console.

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