source: trunk/GSASII.py @ 2502

Last change on this file since 2502 was 2502, checked in by toby, 7 years ago

prevent double-draw of data window after refinements

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