source: trunk/GSASII.py @ 2518

Last change on this file since 2518 was 2518, checked in by toby, 5 years ago

bind for Sequential Refine in wrong place

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 191.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2016-11-09 16:06:10 +0000 (Wed, 09 Nov 2016) $
6# $Author: toby $
7# $Revision: 2518 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2518 2016-11-09 16:06:10Z toby $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import 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: 2518 $")
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='Debug graphics refresh')
252#            self.Bind(wx.EVT_MENU, self.TestResetPlot, 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 = style=wx.OPEN | wx.MULTIPLE
445        else:
446            mode = style=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_COLLAPSED,
2334            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2335        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2336            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2337        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2338            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2339        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2340            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2341        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2342            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2343        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2344            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2345        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2346        self.root = self.PatternTree.root
2347        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2348            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2349        #self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
2350        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame,G2frame=self)
2351        plotFrame.Show()
2352       
2353        self.dataDisplay = None
2354       
2355    def __init__(self, parent):
2356        self.ExportLookup = {}
2357        self.exporterlist = []
2358        self._init_ctrls(parent)
2359        self.Image = wx.Image(
2360            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2361            wx.BITMAP_TYPE_ICO)
2362        if "wxMSW" in wx.PlatformInfo:
2363            img = self.Image.Scale(16, 16).ConvertToBitmap()
2364        elif "wxGTK" in wx.PlatformInfo:
2365            img = self.Image.Scale(22, 22).ConvertToBitmap()
2366        else:
2367            img = self.Image.ConvertToBitmap()
2368        self.SetIcon(wx.IconFromBitmap(img))
2369        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2370        # various defaults
2371        self.oldFocus = None
2372        self.GSASprojectfile = ''
2373        self.undofile = ''
2374        self.TreeItemDelete = False
2375        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2376        self.Weight = False
2377        self.IfPlot = False
2378        self.DDShowAll = False
2379        self.atmSel = ''
2380        self.PatternId = 0
2381        self.PickId = 0
2382        self.PickIdText = None
2383        self.PeakTable = []
2384        self.LimitsTable = []
2385        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2386        self.HKL = []
2387        self.Lines = []
2388        self.itemPicked = None
2389        self.dataFrame = None
2390        self.Interpolate = 'nearest'
2391        self.ContourColor = 'Paired'
2392        self.VcovColor = 'RdYlGn'
2393        self.RamaColor = 'Blues'
2394        self.Projection = 'equal area'
2395        self.logPlot = False
2396        self.plusPlot = True
2397        self.sqPlot = False
2398        self.ErrorBars = False
2399        self.Contour = False
2400        self.Legend = False
2401        self.SinglePlot = True
2402        self.SubBack = False
2403        self.seqReverse = False
2404        self.seqLines = True #draw lines between points
2405        self.plotView = 0
2406        self.Image = 0
2407        self.oldImagefile = '' # the name of the last image file read
2408        self.oldImageTag = None # the name of the tag for multi-image files
2409        self.PauseIntegration = False
2410        self.ImageZ = []
2411        self.Integrate = 0
2412        self.imageDefault = {}
2413        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2414        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2415        self.autoIntFrame = None
2416        self.IntegratedList = [] # list of already integrated IMG tree items
2417        self.Sngl = False
2418        self.ifGetRing = False
2419        self.MaskKey = ''           #trigger for making image masks
2420        self.StrainKey = ''         #ditto for new strain d-zeros
2421        self.EnablePlot = True
2422        self.hist = ''              # selected histogram in Phase/Data tab
2423        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
2424        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2425        self.LastImportDir = None # last-used directory where an import was done
2426        self.LastGPXdir = None    # directory where a GPX file was last read
2427        self.LastExportDir = None  # the last directory used for exports, if any.
2428        self.dataDisplayPhaseText = ''
2429        self.lastTreeSetting = []
2430       
2431        arg = sys.argv
2432        if len(arg) > 1 and arg[1]:
2433            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2434            self.dirname = os.path.dirname(arg[1])
2435            if self.dirname: os.chdir(self.dirname)
2436            try:
2437                self.StartProject()         #open the file if possible
2438                return
2439            except Exception:
2440                print 'Error opening or reading file',arg[1]
2441                import traceback
2442                print traceback.format_exc()
2443               
2444        if GSASIIpath.GetConfigValue('Starting_directory'):
2445            try:
2446                pth = GSASIIpath.GetConfigValue('Starting_directory')
2447                pth = os.path.expanduser(pth) 
2448                os.chdir(pth)
2449                self.LastGPXdir = pth
2450            except:
2451                print('Ignoring Config Starting_directory value: '+
2452                      GSASIIpath.GetConfigValue('Starting_directory'))
2453
2454    def GetTreeItemsList(self,item):
2455        return self.PatternTree._getTreeItemsList(item)
2456
2457    def OnSize(self,event):
2458        'Called to make PatternTree fill mainPanel'
2459        w,h = self.GetClientSizeTuple()
2460        self.mainPanel.SetSize(wx.Size(w,h))
2461        self.PatternTree.SetSize(wx.Size(w,h))
2462                       
2463    def OnPatternTreeSelChanged(self, event):
2464        '''Called when a data tree item is selected'''
2465        if self.TreeItemDelete:
2466            self.TreeItemDelete = False
2467        else:
2468            pltNum = self.G2plotNB.nb.GetSelection()
2469            if pltNum >= 0:                         #to avoid the startup with no plot!
2470                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2471                pltPlot = pltPage.figure
2472            item = event.GetItem()
2473            G2gd.MovePatternTreeToGrid(self,item)
2474            if self.oldFocus:
2475                self.oldFocus.SetFocus()
2476       
2477    def OnPatternTreeItemCollapsed(self, event):
2478        'Called when a tree item is collapsed - all children will be collapsed'
2479        self.PatternTree.CollapseAllChildren(event.GetItem())
2480
2481    def OnPatternTreeItemExpanded(self, event):
2482        'Called when a tree item is expanded'
2483        self.OnPatternTreeSelChanged(event)
2484        event.Skip()
2485       
2486    def OnPatternTreeItemDelete(self, event):
2487        'Called when a tree item is deleted -- not sure what this does'
2488        self.TreeItemDelete = True
2489
2490    def OnPatternTreeItemActivated(self, event):
2491        'Called when a tree item is activated'
2492        event.Skip()
2493       
2494    def OnPatternTreeBeginRDrag(self,event):
2495        event.Allow()
2496        self.BeginDragId = event.GetItem()
2497        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2498        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2499        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2500        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2501        while item:     #G2 data tree has no sub children under a child of a tree item
2502            name = self.PatternTree.GetItemText(item)
2503            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2504            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2505       
2506    def OnPatternTreeEndDrag(self,event):
2507        event.Allow()
2508        self.EndDragId = event.GetItem()
2509        try:
2510            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2511        except:
2512            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2513            NewParent = self.root
2514        if self.ParentId != NewParent:
2515            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2516        else:
2517            Name,Item = self.DragData[0]
2518            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2519            self.PatternTree.SetItemPyData(NewId,Item)
2520            for name,item in self.DragData[1:]:     #loop over children
2521                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2522                self.PatternTree.SetItemPyData(Id,item)
2523            self.PatternTree.Delete(self.BeginDragId)
2524            G2gd.MovePatternTreeToGrid(self,NewId)
2525       
2526    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2527        'Allows stepping through the tree with the up/down arrow keys'
2528        self.oldFocus = wx.Window.FindFocus()
2529        keyevt = event.GetKeyEvent()
2530        key = event.GetKeyCode()
2531        item = self.PatternTree.GetSelection()
2532        if type(item) is int: return # is this the toplevel in tree?
2533        name = self.PatternTree.GetItemText(item)
2534        parent = self.PatternTree.GetItemParent(item)
2535        if key == wx.WXK_UP:
2536            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2537                if type(parent) is int: return # is this the toplevel in tree?
2538                prev = self.PatternTree.GetPrevSibling(parent)
2539                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2540                if NewId:
2541                    self.PatternTree.Collapse(parent)
2542                    self.PatternTree.Expand(prev)
2543                    self.oldFocus = wx.Window.FindFocus()
2544                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2545                else:
2546                    wx.CallAfter(self.PatternTree.SelectItem,item)
2547            else:   
2548                self.PatternTree.GetPrevSibling(item)
2549                self.PatternTree.SelectItem(item)
2550        elif key == wx.WXK_DOWN:
2551            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2552                next = self.PatternTree.GetNextSibling(parent)
2553                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2554                if NewId:
2555                    self.PatternTree.Collapse(parent)
2556                    self.PatternTree.Expand(next)
2557                    self.oldFocus = wx.Window.FindFocus()
2558                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2559                else:
2560                    wx.CallAfter(self.PatternTree.SelectItem,item)
2561            else:   
2562                self.PatternTree.GetNextSibling(item)
2563                self.PatternTree.SelectItem(item)
2564               
2565    def OnReadPowderPeaks(self,event):
2566        'Bound to menu Data/Read Powder Peaks'
2567        Cuka = 1.54052
2568        self.CheckNotebook()
2569        pth = G2G.GetImportPath(self)
2570        if not pth: pth = '.'
2571        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2572            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2573        try:
2574            if dlg.ShowModal() == wx.ID_OK:
2575                self.HKL = []
2576                self.powderfile = dlg.GetPath()
2577                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2578                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2579                data = ['PKS',wave,0.0]
2580                names = ['Type','Lam','Zero'] 
2581                codes = [0,0,0]
2582                inst = [G2IO.makeInstDict(names,data,codes),{}]
2583                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2584                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2585                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2586                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2587                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2588                self.PatternTree.Expand(Id)
2589                self.PatternTree.SelectItem(Id)
2590                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2591        finally:
2592            dlg.Destroy()
2593                       
2594    def OnImageRead(self,event):
2595        '''Called to read in an image in any known format. *** Depreciated. ***
2596        '''
2597        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2598
2599    def CheckNotebook(self):
2600        '''Make sure the data tree has the minimally expected controls.
2601        '''
2602        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2603            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2604            self.PatternTree.SetItemPyData(sub,[''])
2605        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2606            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2607            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2608        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2609            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2610            self.PatternTree.SetItemPyData(sub,{})
2611        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2612            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2613            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2614        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2615            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2616            self.PatternTree.SetItemPyData(sub,{})
2617        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2618            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2619            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2620                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2621               
2622    class CopyDialog(wx.Dialog):
2623        '''Creates a dialog for copying control settings between
2624        data tree items'''
2625        def __init__(self,parent,title,text,data):
2626            wx.Dialog.__init__(self,parent,-1,title, 
2627                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2628            self.data = data
2629            panel = wx.Panel(self)
2630            mainSizer = wx.BoxSizer(wx.VERTICAL)
2631            topLabl = wx.StaticText(panel,-1,text)
2632            mainSizer.Add((10,10),1)
2633            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2634            mainSizer.Add((10,10),1)
2635            ncols = len(data)/40+1
2636            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2637            for id,item in enumerate(self.data):
2638                ckbox = wx.CheckBox(panel,id,item[1])
2639                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2640                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2641            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2642            OkBtn = wx.Button(panel,-1,"Ok")
2643            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2644            cancelBtn = wx.Button(panel,-1,"Cancel")
2645            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2646            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2647            btnSizer.Add((20,20),1)
2648            btnSizer.Add(OkBtn)
2649            btnSizer.Add((20,20),1)
2650            btnSizer.Add(cancelBtn)
2651            btnSizer.Add((20,20),1)
2652           
2653            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2654            panel.SetSizer(mainSizer)
2655            panel.Fit()
2656            self.Fit()
2657       
2658        def OnCopyChange(self,event):
2659            id = event.GetId()
2660            self.data[id][0] = self.FindWindowById(id).GetValue()       
2661           
2662        def OnOk(self,event):
2663            parent = self.GetParent()
2664            parent.Raise()
2665            self.EndModal(wx.ID_OK)             
2666           
2667        def OnCancel(self,event):
2668            parent = self.GetParent()
2669            parent.Raise()
2670            self.EndModal(wx.ID_CANCEL)             
2671           
2672        def GetData(self):
2673            return self.data
2674       
2675    class SumDialog(wx.Dialog):
2676        'Allows user to supply scale factor(s) when summing data'
2677        def __init__(self,parent,title,text,dataType,data):
2678            wx.Dialog.__init__(self,parent,-1,title,size=(400,250),
2679                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
2680            self.data = data
2681            size = (400,250)
2682            panel = wxscroll.ScrolledPanel(self, wx.ID_ANY,size=size,
2683                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
2684            mainSizer = wx.BoxSizer(wx.VERTICAL)
2685            topLabl = wx.StaticText(panel,-1,text)
2686            mainSizer.Add((10,10),1)
2687            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2688            mainSizer.Add((10,10),1)
2689            self.dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2690            for id,item in enumerate(self.data[:-1]):
2691                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(300,20))
2692                name.SetEditable(False)
2693                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2694                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2695                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2696                self.dataGridSizer.Add(scale,0,wx.LEFT,10)
2697                self.dataGridSizer.Add(name,0,wx.RIGHT,10)
2698            if dataType:
2699                self.dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2700                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2701                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(300,20),style=wx.TE_PROCESS_ENTER)
2702                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2703                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2704                self.dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2705                self.dataGridSizer.Add(wx.StaticText(panel,label='All scales value: '),0,  \
2706                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2707                allScale = wx.TextCtrl(panel,value='',style=wx.TE_PROCESS_ENTER)
2708                allScale.Bind(wx.EVT_TEXT_ENTER,self.OnAllScale)
2709                allScale.Bind(wx.EVT_KILL_FOCUS,self.OnAllScale)
2710                self.dataGridSizer.Add(allScale,0,WACV)
2711            mainSizer.Add(self.dataGridSizer,0,wx.EXPAND)
2712            OkBtn = wx.Button(panel,-1,"Ok")
2713            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2714            cancelBtn = wx.Button(panel,-1,"Cancel")
2715            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2716            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2717            btnSizer.Add((20,20),1)
2718            btnSizer.Add(OkBtn)
2719            btnSizer.Add((20,20),1)
2720            btnSizer.Add(cancelBtn)
2721            btnSizer.Add((20,20),1)
2722           
2723            panel.SetSizer(mainSizer)
2724            panel.SetAutoLayout(1)
2725            panel.SetupScrolling()
2726            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2727            panel.SetSizer(mainSizer)
2728            panel.Fit()
2729            self.Fit()
2730
2731        def OnScaleChange(self,event):
2732            event.Skip()
2733            id = event.GetId()
2734            value = self.FindWindowById(id).GetValue()
2735            try:
2736                self.data[id][0] = float(value)
2737                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2738            except ValueError:
2739                if value and '-' not in value[0]:
2740                    print 'bad input - numbers only'
2741                    self.FindWindowById(id).SetValue('0.000')
2742                   
2743        def OnAllScale(self,event):
2744            event.Skip()
2745            id = event.GetId()
2746            try:
2747                scale = float(self.FindWindowById(id).GetValue())
2748                self.FindWindowById(id).SetValue('%.3f'%(scale))
2749                entries = self.dataGridSizer.GetChildren()
2750                for i,item in enumerate(self.data[:-1]):
2751                    item[0] = scale
2752                    entries[2*i].GetWindow().SetValue('%.3f'%(scale))
2753                 
2754            except ValueError:
2755                print 'bad input - numbers only'
2756                self.FindWindowById(id).SetValue('')
2757                   
2758           
2759        def OnNameChange(self,event):
2760            event.Skip()
2761            self.data[-1] = self.name.GetValue() 
2762           
2763        def OnOk(self,event):
2764            parent = self.GetParent()
2765            parent.Raise()
2766            self.EndModal(wx.ID_OK)             
2767           
2768        def OnCancel(self,event):
2769            parent = self.GetParent()
2770            parent.Raise()
2771            self.EndModal(wx.ID_CANCEL)             
2772           
2773        def GetData(self):
2774            return self.data
2775                       
2776    def OnPwdrSum(self,event):
2777        'Sum together powder data(?)'
2778        TextList = []
2779        DataList = []
2780        SumList = []
2781        Names = []
2782        Inst = None
2783        SumItemList = []
2784        Comments = ['Sum equals: \n']
2785        if self.PatternTree.GetCount():
2786            item, cookie = self.PatternTree.GetFirstChild(self.root)
2787            while item:
2788                name = self.PatternTree.GetItemText(item)
2789                Names.append(name)
2790                if 'PWDR' in name:
2791                    TextList.append([0.0,name])
2792                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2793                    if not Inst:
2794                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2795                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2796            if len(TextList) < 2:
2797                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2798                return
2799            TextList.append('default_sum_name')               
2800            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2801            try:
2802                if dlg.ShowModal() == wx.ID_OK:
2803                    lenX = 0
2804                    Xminmax = [0,0]
2805                    Xsum = []
2806                    Ysum = []
2807                    Vsum = []
2808                    result = dlg.GetData()
2809                    for i,item in enumerate(result[:-1]):
2810                        scale,name = item
2811                        data = DataList[i]
2812                        if scale:
2813                            Comments.append("%10.3f %s" % (scale,' * '+name))
2814                            x,y,w,yc,yb,yd = data   #numpy arrays!
2815                            v = 1./w
2816                            if lenX:
2817                                if lenX != len(x):
2818                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2819                                        '\nExpected:'+str(lenX)+ \
2820                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2821                                    return
2822                            else:
2823                                lenX = len(x)
2824                            if Xminmax[1]:
2825                                if Xminmax != [x[0],x[-1]]:
2826                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2827                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2828                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2829                                    return
2830                                else:
2831                                    for j,yi in enumerate(y):
2832                                         Ysum[j] += scale*yi
2833                                         Vsum[j] += abs(scale)*v[j]
2834                            else:
2835                                Xminmax = [x[0],x[-1]]
2836                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2837                                for j,yi in enumerate(y):
2838                                    Xsum.append(x[j])
2839                                    Ysum.append(scale*yi)
2840                                    Vsum.append(abs(scale*v[j]))
2841                    Wsum = 1./np.array(Vsum)
2842                    outname = 'PWDR '+result[-1]
2843                    Id = 0
2844                    if outname in Names:
2845                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2846                        try:
2847                            if dlg2.ShowModal() == wx.ID_OK:
2848                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2849                                self.PatternTree.Delete(Id)
2850                        finally:
2851                            dlg2.Destroy()
2852                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2853                    if Id:
2854                        Sample = G2pdG.SetDefaultSample()
2855                        Ymax = np.max(Ysum)
2856                        valuesdict = {
2857                            'wtFactor':1.0,
2858                            'Dummy':False,
2859                            'ranId':ran.randint(0,sys.maxint),
2860                            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
2861                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2862                            }
2863                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2864                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2865                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2866                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2867                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2868                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2869                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2870                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2871                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2872                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2873                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2874                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2875                        self.PatternTree.SelectItem(Id)
2876                        self.PatternTree.Expand(Id)
2877            finally:
2878                dlg.Destroy()
2879
2880    def OnImageSum(self,event):
2881        'Sum together image data'
2882        TextList = []
2883        DataList = []
2884        SumList = []
2885        Names = []
2886        Inst = []
2887        SumItemList = []
2888        Comments = ['Sum equals: \n']
2889        if self.PatternTree.GetCount():
2890            item, cookie = self.PatternTree.GetFirstChild(self.root)
2891            while item:
2892                name = self.PatternTree.GetItemText(item)
2893                Names.append(name)
2894                if 'IMG' in name:
2895                    TextList.append([0.0,name])
2896                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2897                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2898                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2899            if len(TextList) < 2:
2900                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2901                return
2902            TextList.append('default_sum_name')               
2903            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2904            try:
2905                if dlg.ShowModal() == wx.ID_OK:
2906                    imSize = 0
2907                    result = dlg.GetData()
2908                    First = True
2909                    Found = False
2910                    for i,item in enumerate(result[:-1]):
2911                        scale,name = item
2912                        if scale:
2913                            Found = True                               
2914                            Comments.append("%10.3f %s" % (scale,' * '+name))
2915                            Npix,imagefile,imagetag = DataList[i]
2916                            imagefile = G2IO.CheckImageFile(self,imagefile)
2917                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2918                            if First:
2919                                newImage = np.zeros_like(image)
2920                                First = False
2921                            if imSize:
2922                                if imSize != Npix:
2923                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2924                                        '\nExpected:'+str(imSize)+ \
2925                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2926                                    return
2927                                newImage = newImage+scale*image
2928                            else:
2929                                imSize = Npix
2930                                newImage = newImage+scale*image
2931                            del(image)
2932                    if not Found:
2933                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2934                        return
2935                       
2936                       
2937                    newImage = np.array(newImage,dtype=np.int32)                       
2938                    outname = 'IMG '+result[-1]
2939                    Id = 0
2940                    if outname in Names:
2941                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2942                        try:
2943                            if dlg2.ShowModal() == wx.ID_OK:
2944                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2945                        finally:
2946                            dlg2.Destroy()
2947                    else:
2948                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2949                    if Id:
2950                        pth = G2G.GetExportPath(self)
2951                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth,outname.split('IMG ')[1], 
2952                            'G2img files (*.G2img)|*.G2img', 
2953                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
2954                        if dlg.ShowModal() == wx.ID_OK:
2955                            newimagefile = dlg.GetPath()
2956                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2957                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2958                            Imax = np.amax(newImage)
2959                            Imin = np.amin(newImage)
2960                            newImage = []
2961                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2962                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2963                        del(newImage)
2964                        if self.imageDefault:
2965                            Data = copy.copy(self.imageDefault)
2966                        Data['formatName'] = 'GSAS-II image'
2967                        Data['showLines'] = True
2968                        Data['ring'] = []
2969                        Data['rings'] = []
2970                        Data['cutoff'] = 10
2971                        Data['pixLimit'] = 20
2972                        Data['ellipses'] = []
2973                        Data['calibrant'] = ''
2974                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2975                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2976                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2977                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2978                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2979                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2980                        self.PatternTree.SelectItem(Id)
2981                        self.PatternTree.Expand(Id)
2982                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2983                        self.Image = self.PickId
2984            finally:
2985                dlg.Destroy()
2986                     
2987    def OnAddPhase(self,event):
2988        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2989        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2990            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2991        else:
2992            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2993        PhaseName = ''
2994        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2995            style=wx.OK)
2996        if dlg.ShowModal() == wx.ID_OK:
2997            PhaseName = dlg.GetValue()
2998        dlg.Destroy()
2999        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
3000        E,SGData = G2spc.SpcGroup('P 1')
3001        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
3002        G2gd.MovePatternTreeToGrid(self,sub) #bring up new phase General tab
3003       
3004    def OnDeletePhase(self,event):
3005        'Delete a phase from the tree. Called by Data/Delete Phase menu'
3006        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
3007        if self.dataFrame:
3008            self.dataFrame.Clear() 
3009        TextList = []
3010        DelList = []
3011        DelItemList = []
3012        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3013            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3014        else:
3015            return
3016        if sub:
3017            item, cookie = self.PatternTree.GetFirstChild(sub)
3018            while item:
3019                TextList.append(self.PatternTree.GetItemText(item))
3020                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
3021            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
3022            try:
3023                if dlg.ShowModal() == wx.ID_OK:
3024                    result = dlg.GetSelections()
3025                    for i in result: DelList.append([i,TextList[i]])
3026                    item, cookie = self.PatternTree.GetFirstChild(sub)
3027                    i = 0
3028                    while item:
3029                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
3030                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3031                        i += 1
3032                    for item in DelItemList:
3033                        name = self.PatternTree.GetItemText(item)
3034                        self.PatternTree.Delete(item)
3035                        self.G2plotNB.Delete(name)
3036                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3037                    while item:
3038                        name = self.PatternTree.GetItemText(item)
3039                        if 'PWDR' in name:
3040                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
3041                            refList = self.PatternTree.GetItemPyData(Id)
3042                            if len(refList):
3043                                for i,item in DelList:
3044                                    if item in refList:
3045                                        del(refList[item])
3046#                            self.PatternTree.SetItemPyData(Id,refList)
3047                        elif 'HKLF' in name:
3048                            data = self.PatternTree.GetItemPyData(item)
3049                            data[0] = {}
3050#                            self.PatternTree.SetItemPyData(item,data)
3051                           
3052                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3053            finally:
3054                dlg.Destroy()
3055               
3056    def OnRenameData(self,event):
3057        'Renames an existing phase. Called by Data/Rename Phase menu'
3058        name = self.PatternTree.GetItemText(self.PickId)     
3059        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
3060            if 'Bank' in name:
3061                names = name.split('Bank')
3062                names[1] = ' Bank'+names[1]
3063            elif 'Azm' in name:
3064                names = name.split('Azm')
3065                names[1] = ' Azm'+names[1]
3066            else:
3067                names = [name,'']
3068            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
3069            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
3070                defaultValue=names[0][names[0].index(' ')+1:])
3071            try:
3072                if dlg.ShowModal() == wx.ID_OK:
3073                    name = dataType+dlg.GetValue()+names[1]
3074                    self.PatternTree.SetItemText(self.PickId,name)
3075            finally:
3076                dlg.Destroy()
3077       
3078    def GetFileList(self,fileType,skip=None):        #potentially useful?
3079        'Appears unused. Note routine of same name in GSASIIpwdGUI'
3080        fileList = []
3081        Source = ''
3082        id, cookie = self.PatternTree.GetFirstChild(self.root)
3083        while id:
3084            name = self.PatternTree.GetItemText(id)
3085            if fileType in name:
3086                if id == skip:
3087                    Source = name
3088                else:
3089                    fileList.append([False,name,id])
3090            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3091        if skip:
3092            return fileList,Source
3093        else:
3094            return fileList
3095           
3096    def OnDataDelete(self, event):
3097        '''Delete one or more histograms from data tree. Called by the
3098        Data/DeleteData menu
3099        '''
3100        TextList = []
3101        DelList = []
3102        DelItemList = []
3103        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
3104        ifPWDR = False
3105        ifSASD = False
3106        ifIMG = False
3107        ifHKLF = False
3108        ifPDF = False
3109        if self.PatternTree.GetCount():
3110            item, cookie = self.PatternTree.GetFirstChild(self.root)
3111            while item:
3112                name = self.PatternTree.GetItemText(item)
3113                if name not in ['Notebook','Controls','Covariance','Constraints',
3114                    'Restraints','Phases','Rigid bodies'] or 'Sequential' not in name:
3115                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
3116                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
3117                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
3118                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
3119                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
3120                    TextList.append(name)
3121                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3122            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
3123            try:
3124                if dlg.ShowModal() == wx.ID_OK:
3125                    result = dlg.GetSelections()
3126                    for i in result: DelList.append(TextList[i])
3127                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3128                    while item:
3129                        itemName = self.PatternTree.GetItemText(item)
3130                        if itemName in DelList:
3131                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
3132                            elif 'SASD' in itemName: nItems['SASD'] -= 1
3133                            elif 'IMG' in itemName: nItems['IMG'] -= 1
3134                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
3135                            elif 'PDF' in itemName: nItems['PDF'] -= 1
3136                            DelItemList.append(item)
3137                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3138                    for item in DelItemList:
3139                        self.PatternTree.Delete(item)
3140                    self.PickId = 0
3141                    self.PickIdText = None
3142                    self.PatternId = 0
3143                    if nItems['PWDR']:
3144                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3145                    else:
3146                        self.G2plotNB.Delete('Powder Patterns')
3147                    if not nItems['IMG']:
3148                        self.G2plotNB.Delete('2D Powder Image')
3149                    if not nItems['HKLF']:
3150                        self.G2plotNB.Delete('Structure Factors')
3151                        if '3D Structure Factors' in self.G2plotNB.plotList:
3152                            self.G2plotNB.Delete('3D Structure Factors')
3153            finally:
3154                dlg.Destroy()
3155
3156    def OnFileOpen(self, event, filename=None):
3157        '''Gets a GSAS-II .gpx project file in response to the
3158        File/Open Project menu button
3159        '''
3160        result = wx.ID_OK
3161        self.EnablePlot = False
3162        if self.PatternTree.GetChildrenCount(self.root,False):
3163            if self.dataFrame:
3164                self.dataFrame.Clear() 
3165            dlg = wx.MessageDialog(
3166                self,
3167                'Do you want to overwrite the current project? '+
3168                'Any unsaved changes in current project will be lost. Press OK to continue.',
3169                'Overwrite?',  wx.OK | wx.CANCEL)
3170            try:
3171                result = dlg.ShowModal()
3172                if result == wx.ID_OK:
3173                    self.PatternTree.DeleteChildren(self.root)
3174                    self.GSASprojectfile = ''
3175                    self.HKL = []
3176                    if self.G2plotNB.plotList:
3177                        self.G2plotNB.clear()
3178            finally:
3179                dlg.Destroy()
3180        if result != wx.ID_OK: return
3181
3182        if not filename:
3183            if self.dataDisplay: self.dataDisplay.Destroy()
3184            if self.LastGPXdir:
3185                pth = self.LastGPXdir
3186            else:
3187                pth = '.'
3188            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3189                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3190            try:
3191                if dlg.ShowModal() != wx.ID_OK: return
3192                self.GSASprojectfile = dlg.GetPath()
3193                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3194                self.dirname = dlg.GetDirectory()
3195            finally:
3196                dlg.Destroy()
3197        else:
3198            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3199            self.dirname = os.path.split(filename)[0]
3200
3201        try:
3202            self.StartProject()         #open the file if possible
3203        except:
3204            print '\nError opening file ',filename
3205            import traceback
3206            print traceback.format_exc()
3207       
3208    def StartProject(self):
3209        '''Opens a GSAS-II project file & selects the 1st available data set to
3210        display (PWDR, HKLF or SASD)
3211        '''
3212       
3213        Id = 0
3214        G2IO.ProjFileOpen(self)
3215        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3216        self.PatternTree.Expand(self.root)
3217        self.HKL = []
3218        item, cookie = self.PatternTree.GetFirstChild(self.root)
3219        while item and not Id:
3220            name = self.PatternTree.GetItemText(item)
3221            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3222                Id = item
3223            elif name == 'Controls':
3224                data = self.PatternTree.GetItemPyData(item)
3225                if data:
3226                    for item in self.Refine: item.Enable(True)
3227                    self.EnableSeqRefineMenu()
3228            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3229        if Id:
3230            self.EnablePlot = True
3231            self.PatternTree.SelectItem(Id)
3232        self.CheckNotebook()
3233        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3234        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3235        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3236        self.LastGPXdir = pth
3237
3238    def OnFileClose(self, event):
3239        '''Clears the data tree in response to the
3240        File/New Project menu button. User is given option to save
3241        the project.
3242        '''
3243        if self.dataFrame:
3244            self.dataFrame.Clear()
3245            self.dataFrame.SetLabel('GSAS-II data display') 
3246        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3247        try:
3248            result = dlg.ShowModal()
3249            if result == wx.ID_OK:
3250                self.OnFileSaveMenu(event)
3251            if result != wx.ID_CANCEL:
3252                self.GSASprojectfile = ''
3253                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3254                self.PatternTree.DeleteChildren(self.root)
3255                if self.HKL: self.HKL = []
3256                if self.G2plotNB.plotList:
3257                    self.G2plotNB.clear()
3258        finally:
3259            dlg.Destroy()
3260
3261    def OnFileSave(self, event):
3262        '''Save the current project in response to the
3263        File/Save Project menu button
3264        '''
3265       
3266        if self.GSASprojectfile: 
3267            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3268            self.CheckNotebook()
3269            G2IO.ProjFileSave(self)
3270        else:
3271            self.OnFileSaveas(event)
3272
3273    def OnFileSaveas(self, event):
3274        '''Save the current project in response to the
3275        File/Save as menu button
3276        '''
3277        if GSASIIpath.GetConfigValue('Starting_directory'):
3278            pth = GSASIIpath.GetConfigValue('Starting_directory')
3279            pth = os.path.expanduser(pth) 
3280        elif self.LastGPXdir:
3281            pth = self.LastGPXdir
3282        else:
3283            pth = '.'
3284        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3285            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3286        try:
3287            if dlg.ShowModal() == wx.ID_OK:
3288                self.GSASprojectfile = dlg.GetPath()
3289                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3290                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3291                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3292                self.CheckNotebook()
3293                G2IO.ProjFileSave(self)
3294                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3295        finally:
3296            dlg.Destroy()
3297
3298    def ExitMain(self, event):
3299        '''Called if the main window is closed'''
3300        if self.G2plotNB:
3301            self.G2plotNB.Destroy()
3302        if self.dataFrame:
3303            self.dataFrame.Clear() 
3304            self.dataFrame.Destroy()
3305        if self.undofile:
3306            os.remove(self.undofile)
3307        sys.exit()
3308       
3309    def OnFileExit(self, event):
3310        '''Called in response to the File/Quit menu button'''
3311        if self.G2plotNB:
3312            self.G2plotNB.Destroy()
3313        if self.dataFrame:
3314            self.dataFrame.Clear() 
3315            self.dataFrame.Destroy()
3316        self.Close()
3317       
3318    def OnExportPeakList(self,event):
3319        nptand = lambda x: np.tan(x*math.pi/180.)
3320        pth = G2G.GetExportPath(self)
3321        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3322            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3323        try:
3324            if dlg.ShowModal() == wx.ID_OK:
3325                self.peaklistfile = dlg.GetPath()
3326                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3327                file = open(self.peaklistfile,'w')               
3328                item, cookie = self.PatternTree.GetFirstChild(self.root)
3329                while item:
3330                    name = self.PatternTree.GetItemText(item)
3331                    if 'PWDR' in name:
3332                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3333                        wave = 0.0
3334                        while item2:
3335                            name2 = self.PatternTree.GetItemText(item2)
3336                            if name2 == 'Instrument Parameters':
3337                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3338                                Type = Inst['Type'][0]
3339                                if 'T' not in Type:
3340                                    wave = G2mth.getWave(Inst)
3341                            elif name2 == 'Peak List':
3342                                pkdata = self.PatternTree.GetItemPyData(item2)
3343                                peaks = pkdata['peaks']
3344                                sigDict = pkdata['sigDict']
3345                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3346                        file.write("#%s \n" % (name+' Peak List'))
3347                        if wave:
3348                            file.write('#wavelength = %10.6f\n'%(wave))
3349                        if 'T' in Type:
3350                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3351                        else:
3352                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3353                        for ip,peak in enumerate(peaks):
3354                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3355                            if 'T' in Type:  #TOF - more cols
3356                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3357                                for name in esds.keys():
3358                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3359                                sig = np.sqrt(peak[8])
3360                                gam = peak[10]
3361                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3362                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3363                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3364                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3365                            else:               #CW
3366                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3367                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3368                                for name in esds.keys():
3369                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3370                                sig = np.sqrt(peak[4]) #var -> sig
3371                                gam = peak[6]
3372                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3373                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3374                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3375                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3376                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3377                file.close()
3378        finally:
3379            dlg.Destroy()
3380       
3381    def OnExportHKL(self,event):
3382        pth = G2G.GetExportPath(self)
3383        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3384            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3385        try:
3386            if dlg.ShowModal() == wx.ID_OK:
3387                self.peaklistfile = dlg.GetPath()
3388                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3389                file = open(self.peaklistfile,'w')               
3390                item, cookie = self.PatternTree.GetFirstChild(self.root)
3391                while item:
3392                    name = self.PatternTree.GetItemText(item)
3393                    if 'PWDR' in name:
3394                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3395                        while item2:
3396                            name2 = self.PatternTree.GetItemText(item2)
3397                            if name2 == 'Reflection Lists':
3398                                data = self.PatternTree.GetItemPyData(item2)
3399                                phases = data.keys()
3400                                for phase in phases:
3401                                    peaks = data[phase]
3402                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3403                                    if 'T' in peaks.get('Type','PXC'):
3404                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3405                                    else:               
3406                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3407                                    for peak in peaks['RefList']:
3408                                        if 'T' in peaks.get('Type','PXC'):
3409                                            sig = np.sqrt(peak[6])
3410                                            gam = peak[7]
3411                                            FWHM = G2pwd.getgamFW(gam,sig)
3412                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3413                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3414                                        else:
3415                                            sig = np.sqrt(peak[6])
3416                                            gam = peak[7]
3417                                            FWHM = G2pwd.getgamFW(gam,sig)
3418                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3419                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3420                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3421                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3422                file.close()
3423        finally:
3424            dlg.Destroy()
3425       
3426    def OnExportPDF(self,event):
3427        #need S(Q) and G(R) to be saved here - probably best from selection?
3428        names = ['All']
3429        exports = []
3430        item, cookie = self.PatternTree.GetFirstChild(self.root)
3431        while item:
3432            name = self.PatternTree.GetItemText(item)
3433            if 'PDF' in name:
3434                names.append(name)
3435            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3436        if names:
3437            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3438            if dlg.ShowModal() == wx.ID_OK:
3439                sel = dlg.GetSelections()
3440                if sel[0] == 0:
3441                    exports = names[1:]
3442                else:
3443                    for x in sel:
3444                        exports.append(names[x])
3445            dlg.Destroy()
3446        if exports:
3447            G2IO.PDFSave(self,exports)
3448       
3449    def OnMakePDFs(self,event):
3450        '''Calculates PDFs
3451        '''
3452        sind = lambda x: math.sin(x*math.pi/180.)
3453        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3454        TextList = []
3455        PDFlist = []
3456        Names = []
3457        if self.PatternTree.GetCount():
3458            id, cookie = self.PatternTree.GetFirstChild(self.root)
3459            while id:
3460                name = self.PatternTree.GetItemText(id)
3461                Names.append(name)
3462                if 'PWDR' in name:
3463                    TextList.append(name)
3464                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3465            if len(TextList) < 1:
3466                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3467                return
3468            dlg = G2G.G2MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3469            try:
3470                if dlg.ShowModal() == wx.ID_OK:
3471                    PDFlist = [TextList[i] for i in dlg.GetSelections()]
3472                    for item in PDFlist:
3473                        PWDRname = item[4:]
3474                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3475                        Data = {
3476                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3477                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3478                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3479                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3480                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3481                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3482                            'Lorch':True,'BackRatio':0.0}
3483                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3484                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3485                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3486                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3487                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3488                for item in self.ExportPDF: item.Enable(True)
3489            finally:
3490                dlg.Destroy()
3491               
3492    def GetPWDRdatafromTree(self,PWDRname):
3493        ''' Returns powder data from GSASII tree
3494
3495        :param str PWDRname: a powder histogram name as obtained from
3496          :meth:`GSASIIstruct.GetHistogramNames`
3497
3498        :returns: PWDRdata = powder data dictionary with
3499          Powder data arrays, Limits, Instrument Parameters,
3500          Sample Parameters           
3501        '''
3502        PWDRdata = {}
3503        try:
3504            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3505        except ValueError:
3506            PWDRdata['wtFactor'] = 1.0
3507        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3508        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3509        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3510        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3511        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3512        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3513        if 'ranId' not in PWDRdata:  # patch, add a random Id
3514            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3515        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3516            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3517        return PWDRdata
3518
3519    def GetHKLFdatafromTree(self,HKLFname):
3520        ''' Returns single crystal data from GSASII tree
3521
3522        :param str HKLFname: a single crystal histogram name as obtained
3523          from
3524          :meth:`GSASIIstruct.GetHistogramNames`
3525
3526        :returns: HKLFdata = single crystal data list of reflections
3527
3528        '''
3529        HKLFdata = {}
3530        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3531#        try:
3532#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3533#        except ValueError:
3534#            HKLFdata['wtFactor'] = 1.0
3535        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3536        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3537        return HKLFdata
3538       
3539    def GetPhaseData(self):
3540        '''Returns a dict with defined phases.
3541        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3542        get same info from GPX file.
3543        '''
3544        phaseData = {}
3545        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3546            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3547        else:
3548            print 'no phases found in GetPhaseData'
3549            sub = None
3550        if sub:
3551            item, cookie = self.PatternTree.GetFirstChild(sub)
3552            while item:
3553                phaseName = self.PatternTree.GetItemText(item)
3554                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3555                if 'ranId' not in phaseData[phaseName]:
3556                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3557                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3558        return phaseData
3559
3560    def GetPhaseInfofromTree(self):
3561        '''Get the phase names and their rId values,
3562        also the histograms used in each phase.
3563
3564        :returns: (phaseRIdList, usedHistograms) where
3565
3566          * phaseRIdList is a list of random Id values for each phase
3567          * usedHistograms is a dict where the keys are the phase names
3568            and the values for each key are a list of the histogram names
3569            used in each phase.
3570        '''
3571        phaseRIdList = []
3572        usedHistograms = {}
3573        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3574        if sub:
3575            item, cookie = self.PatternTree.GetFirstChild(sub)
3576            while item:
3577                phaseName = self.PatternTree.GetItemText(item)
3578                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3579                if ranId: phaseRIdList.append(ranId)
3580                data = self.PatternTree.GetItemPyData(item)
3581                UseList = data['Histograms']
3582                usedHistograms[phaseName] = UseList.keys()
3583                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3584        return phaseRIdList,usedHistograms
3585
3586    def GetPhaseNames(self):
3587        '''Returns a list of defined phases.
3588        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3589        get same info from GPX file.
3590        '''
3591        phaseNames = []
3592        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3593            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3594        else:
3595            print 'no phases found in GetPhaseNames'
3596            sub = None
3597        if sub:
3598            item, cookie = self.PatternTree.GetFirstChild(sub)
3599            while item:
3600                phase = self.PatternTree.GetItemText(item)
3601                phaseNames.append(phase)
3602                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3603        return phaseNames
3604   
3605    def GetHistogramNames(self,hType):
3606        """ Returns a list of histogram names found in the GSASII data tree
3607        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3608        get same info from GPX file.
3609       
3610        :param str hType: list of histogram types
3611        :return: list of histogram names
3612       
3613        """
3614        HistogramNames = []
3615        if self.PatternTree.GetCount():
3616            item, cookie = self.PatternTree.GetFirstChild(self.root)
3617            while item:
3618                name = self.PatternTree.GetItemText(item)
3619                if name[:4] in hType:
3620                    HistogramNames.append(name)       
3621                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3622
3623        return HistogramNames
3624                   
3625    def GetUsedHistogramsAndPhasesfromTree(self):
3626        ''' Returns all histograms that are found in any phase
3627        and any phase that uses a histogram.
3628        This also assigns numbers to used phases and histograms by the
3629        order they appear in the file.
3630        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3631        get same info from GPX file.
3632
3633        :returns: (Histograms,Phases)
3634
3635            * Histograms = dictionary of histograms as {name:data,...}
3636            * Phases = dictionary of phases that use histograms
3637        '''
3638        Histograms = {}
3639        Phases = {}
3640        phaseNames = self.GetPhaseNames()
3641        phaseData = self.GetPhaseData()
3642        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3643
3644        for phase in phaseData:
3645            Phase = phaseData[phase]
3646            pId = phaseNames.index(phase)
3647            Phase['pId'] = pId
3648            if Phase['Histograms']:
3649                if phase not in Phases:
3650                    Phases[phase] = Phase
3651                for hist in Phase['Histograms']:
3652                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3653                        Phase['Histograms'][hist]['Use'] = True         
3654                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3655                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3656                        if item:
3657                            if 'PWDR' in hist[:4]: 
3658                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3659                            elif 'HKLF' in hist[:4]:
3660                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3661                            hId = histoList.index(hist)
3662                            Histograms[hist]['hId'] = hId
3663                        else: # would happen if a referenced histogram were renamed or deleted
3664                            print('For phase "'+str(phase)+
3665                                  '" unresolved reference to histogram "'+str(hist)+'"')
3666        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3667        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3668        return Histograms,Phases
3669       
3670    def MakeLSParmDict(self):
3671        '''Load all parameters used for computation from the tree into a
3672        dict of paired values [value, refine flag]. Note that this is
3673        different than the parmDict used in the refinement, which only has
3674        values.
3675
3676        Note that similar things are done in
3677        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3678        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3679        a GPX file).
3680
3681        :returns: (parmDict,varyList) where:
3682
3683         * parmDict is a dict with values and refinement flags
3684           for each parameter and
3685         * varyList is a list of variables (refined parameters).
3686        '''
3687        parmDict = {}
3688        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3689        for phase in Phases:
3690            if 'pId' not in Phases[phase]:
3691                self.ErrorDialog('View parameter error','You must run least squares at least once')
3692                raise Exception,'No pId for phase '+str(phase)
3693        rigidbodyDict = self.PatternTree.GetItemPyData(   
3694            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3695        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3696        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3697        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3698        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3699        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3700        varyList = rbVary+phaseVary+hapVary+histVary
3701        parmDict.update(rbDict)
3702        parmDict.update(phaseDict)
3703        parmDict.update(hapDict)
3704        parmDict.update(histDict)
3705        for parm in parmDict:
3706            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3707                'Omega','Chi','Phi','nDebye','nPeaks']:
3708                parmDict[parm] = [parmDict[parm],'-']
3709            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3710                parmDict[parm] = [parmDict[parm],'-']
3711            elif parm in varyList:
3712                parmDict[parm] = [parmDict[parm],'T']
3713            else:
3714                parmDict[parm] = [parmDict[parm],'F']
3715        # for i in parmDict: print i,'\t',parmDict[i]
3716        # fl = open('parmDict.dat','wb')
3717        # import cPickle
3718        # cPickle.dump(parmDict,fl,1)
3719        # fl.close()
3720        return parmDict,varyList
3721
3722    def ShowLSParms(self,event):
3723        '''Displays a window showing all parameters in the refinement.
3724        Called from the Calculate/View LS Parms menu.
3725        '''
3726        parmDict,varyList = self.MakeLSParmDict()
3727        parmValDict = {}
3728        for i in parmDict:
3729            parmValDict[i] = parmDict[i][0]
3730           
3731        reqVaryList = tuple(varyList) # save requested variables
3732        try:
3733            # process constraints
3734            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3735            Constraints = self.PatternTree.GetItemPyData(sub)
3736            constList = []
3737            for item in Constraints:
3738                if item.startswith('_'): continue
3739                constList += Constraints[item]
3740            G2mv.InitVars()
3741            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3742            groups,parmlist = G2mv.GroupConstraints(constrDict)
3743            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3744            G2mv.Map2Dict(parmValDict,varyList)
3745        except:
3746            pass
3747        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3748        dlg.ShowModal()
3749        dlg.Destroy()
3750       
3751    def OnRefine(self,event):
3752        '''Perform a refinement.
3753        Called from the Calculate/Refine menu.
3754        '''
3755        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3756        if Id:
3757            dlg = wx.MessageDialog(
3758                self,
3759                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3760                'Remove sequential results?',wx.OK|wx.CANCEL)
3761            if dlg.ShowModal() == wx.ID_OK:
3762                self.PatternTree.Delete(Id)
3763                dlg.Destroy()
3764            else:
3765                dlg.Destroy()
3766                return
3767        self.OnFileSave(event)
3768        # check that constraints are OK here
3769        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3770        if errmsg:
3771            self.ErrorDialog('Refinement error',errmsg)
3772            return
3773        if warnmsg:
3774            print('Conflict between refinment flag settings and constraints:\n'+
3775                warnmsg+'\nRefinement not possible')
3776            self.ErrorDialog('Refinement Flag Error',
3777                'Conflict between refinement flag settings and constraints:\n'+
3778                warnmsg+'\nRefinement not possible')
3779            return
3780        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3781            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3782            parent=self)
3783        Size = dlg.GetSize()
3784        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3785            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3786        dlg.CenterOnParent()
3787        Rw = 100.00
3788        self.SaveTreeSetting()
3789        try:
3790            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)    #Msg is Rvals dict if Ok=True
3791        finally:
3792            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3793            dlg.Destroy()
3794            wx.Yield()
3795        if OK:
3796            Rw = Msg['Rwp']
3797            lamMax = Msg.get('lamMax',0.001)
3798            text = 'Load new result?'
3799            if lamMax >= 10.:
3800                text += '\nWARNING: Steepest descents dominates;'+   \
3801                ' minimum may not have been reached\nor result may be false minimum.'+  \
3802                ' You should reconsider your parameter suite'
3803            dlg2 = wx.MessageDialog(self,text,'Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3804            try:
3805                if dlg2.ShowModal() == wx.ID_OK:
3806                    self.PatternTree.DeleteChildren(self.root)
3807                    self.HKL = []
3808                    G2IO.ProjFileOpen(self,False)
3809                    self.ResetPlots()
3810            finally:
3811                dlg2.Destroy()
3812        else:
3813            self.ErrorDialog('Refinement error',Msg)
3814       
3815    def SaveTreeSetting(self):
3816        'Save the last tree setting'
3817        oldId =  self.PatternTree.GetSelection()        #retain current selection
3818        oldPath = self.GetTreeItemsList(oldId)
3819        self.lastTreeSetting = oldPath
3820        # note that for reasons unclear, it does not seem necessary to reload the Atoms tab
3821        #parentName = ''
3822        #tabId = None
3823        # parentId = self.PatternTree.GetItemParent(oldId)
3824        # if parentId:
3825        #     parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3826        #     if 'Phases' in parentName:
3827        #         tabId = self.dataDisplay.GetSelection()
3828        #self.lastTreeSetting = oldPath,tabId
3829        #GSASIIpath.IPyBreak()
3830       
3831    def TestResetPlot(self,event):
3832        '''Debug code to test cleaning up plots after a refinement'''
3833        #for i in range(self.G2plotNB.nb.GetPageCount()):
3834        #    [self.G2plotNB.nb.GetPageText(i)
3835        # save current tree item and (if needed) atoms tab
3836        self.SaveTreeSetting()
3837        self.ResetPlots()
3838       
3839    def ResetPlots(self):
3840        '''This reloads the current tree item, often drawing a plot. It refreshes any plots
3841        that have registered a refresh routine (see G2plotNB.RegisterRedrawRoutine)
3842        and deletes all plots that have not been refreshed and
3843        require one (see G2plotNB.SetNoDelete).
3844        '''
3845        lastRaisedPlotTab = self.G2plotNB.lastRaisedPlotTab # save the last page saved
3846        #print 'lastRaisedPlotTab=',lastRaisedPlotTab
3847        self.G2plotNB.lastRaisedPlotTab = None
3848        # mark displayed plots as invalid
3849        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
3850            frame.plotInvalid = True
3851        # reload current tree item, triggering the routine to redraw the data window and possibly a plot
3852        #oldPath,tabId = self.lastTreeSetting
3853        oldPath = self.lastTreeSetting
3854        Id = self.root
3855        for txt in oldPath:
3856            Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3857        self.PickIdText = None  #force reload of page
3858        if Id:
3859            self.PickId = Id
3860            self.PatternTree.SelectItem(Id)
3861            #G2gd.MovePatternTreeToGrid(self,Id) # fails on Mac -- SelectItem already calls MovePatternTreeToGrid; double call fails to complete properly
3862        # update other self-updating plots
3863        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
3864            if frame.plotInvalid and frame.replotFunction:
3865                frame.replotFunction(*frame.replotArgs,**frame.replotKWargs)
3866        # delete any remaining plots that are still invalid and need a refresh
3867        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
3868            if frame.plotInvalid and frame.plotRequiresRedraw:
3869                self.G2plotNB.Delete(lbl)
3870        # put the previously last-raised tab on top, if present. If not, use the one corresponding to
3871        # the last tree item to be selected
3872        wx.CallAfter(self.G2plotNB.RaiseLastPage,lastRaisedPlotTab,self.G2plotNB.lastRaisedPlotTab)
3873       
3874    def OnSeqRefine(self,event):
3875        '''Perform a sequential refinement.
3876        Called from the Calculate/Sequential refine menu.
3877        '''
3878        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3879        if not Id:
3880            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3881            self.PatternTree.SetItemPyData(Id,{})           
3882        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3883        if not Controls.get('Seq Data'):
3884            print('Error: a sequential refinement has not been set up')
3885            return
3886        Controls['ShowCell'] = True
3887        self.OnFileSave(event)
3888        # check that constraints are OK here
3889        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3890        if errmsg:
3891            self.ErrorDialog('Refinement error',errmsg)
3892            return
3893        if warnmsg:
3894            print('Conflict between refinment flag settings and constraints:\n'+
3895                  warnmsg+'\nRefinement not possible')
3896            self.ErrorDialog('Refinement Flag Error',
3897                             'Conflict between refinment flag settings and constraints:\n'+
3898                             warnmsg+'\nRefinement not possible')
3899            return
3900        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3901            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3902            parent=self)           
3903        Size = dlg.GetSize()
3904        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3905            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3906        dlg.CenterOnParent()
3907        try:
3908            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)     #Msg is Rvals dict if Ok=True
3909        finally:
3910            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3911            dlg.Destroy()
3912            wx.Yield()
3913        if OK:
3914            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3915            try:
3916                if dlg.ShowModal() == wx.ID_OK:
3917                    self.PickIdText = None  #force reload of PickId contents
3918                    self.PatternTree.DeleteChildren(self.root)
3919                    if len(self.HKL): self.HKL = []
3920                    if self.G2plotNB.plotList:
3921                        self.G2plotNB.clear()
3922                    G2IO.ProjFileOpen(self,False)
3923                    self.ResetPlots()
3924                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3925                    self.PatternTree.SelectItem(Id)
3926                    #G2gd.MovePatternTreeToGrid(self,Id) # fails on Mac -- SelectItem already calls MovePatternTreeToGrid; double call fails to complete properly
3927            finally:
3928                dlg.Destroy()
3929        else:
3930            self.ErrorDialog('Sequential refinement error',Msg)
3931       
3932    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3933        'Display an error message'
3934        result = None
3935        if parent is None:
3936            dlg = wx.MessageDialog(self, message, title,  wtype)
3937        else:
3938            dlg = wx.MessageDialog(parent, message, title,  wtype)
3939            dlg.CenterOnParent() # not working on Mac
3940        try:
3941            result = dlg.ShowModal()
3942        finally:
3943            dlg.Destroy()
3944        return result
3945   
3946    def OnSaveMultipleImg(self,event):
3947        '''Select and save multiple image parameter and mask files
3948        '''
3949        G2IO.SaveMultipleImg(self)
3950       
3951class GSASIImain(wx.App):
3952    '''Defines a wxApp for GSAS-II
3953
3954    Creates a wx frame (self.main) which contains the display of the
3955    data tree.
3956    '''
3957    def OnInit(self):
3958        '''Called automatically when the app is created.'''
3959        import platform
3960        if '2.7' not in sys.version[:5]:
3961            dlg = wx.MessageDialog(None, 
3962                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
3963                'Python version error',  wx.OK)
3964            try:
3965                result = dlg.ShowModal()
3966            finally:
3967                dlg.Destroy()
3968            sys.exit()
3969        self.main = GSASII(None)
3970        self.main.Show()
3971        self.SetTopWindow(self.main)
3972        # save the current package versions
3973        self.main.PackageVersions = []
3974        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
3975        for p in (wx,mpl,np,sp,ogl):
3976            self.main.PackageVersions.append([p.__name__,p.__version__])
3977        try:
3978            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
3979        except:
3980            try:
3981                from PIL import PILLOW_VERSION
3982                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
3983            except:
3984                pass
3985        self.main.PackageVersions.append([' Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
3986       
3987        return True
3988    # def MacOpenFile(self, filename):
3989    #     '''Called on Mac every time a file is dropped on the app when it is running,
3990    #     treat this like a File/Open project menu action.
3991    #     Should be ignored on other platforms
3992    #     '''
3993    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3994    #     print 'MacOpen',filename
3995    #     if os.path.splitext(filename)[1] == '.py': return
3996    #     # end PATCH
3997    #     self.main.OnFileOpen(None,filename)
3998    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3999       
4000def main():
4001    '''Start up the GSAS-II application'''
4002    #application = GSASIImain() # don't redirect output, someday we
4003    # may want to do this if we can
4004    application = GSASIImain(0)
4005    if GSASIIpath.GetConfigValue('wxInspector'):
4006        import wx.lib.inspection as wxeye
4007        wxeye.InspectionTool().Show()
4008
4009    #application.main.OnRefine(None)
4010    application.MainLoop()
4011   
4012if __name__ == '__main__':
4013    # print versions
4014    print "Python module versions loaded:"
4015    print "python:     ",sys.version.split()[0]
4016    print "wxpython:   ",wx.__version__
4017    print "matplotlib: ",mpl.__version__
4018    print "numpy:      ",np.__version__
4019    print "scipy:      ",sp.__version__
4020    print "OpenGL:     ",ogl.__version__
4021    try:
4022        from PIL import Image
4023        try:
4024            from PIL import PILLOW_VERSION
4025            version = PILLOW_VERSION
4026        except:
4027            version = Image.VERSION
4028        print "pillow:     ",version
4029    except ImportError:
4030        try:
4031            import Image
4032            print "Image (PIL):",Image.VERSION
4033        except ImportError:
4034            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
4035    try:
4036        import mkl
4037        print "Max threads ",mkl.get_max_threads()
4038    except:
4039        pass
4040    import platform
4041    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
4042    #print "wxPython description",wx.PlatformInfo
4043    print "This is GSAS-II version:     ",__version__,' revision #'+str(GSASIIpath.GetVersionNumber())
4044    GSASIIpath.InvokeDebugOpts()
4045    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.