source: trunk/GSASII.py @ 2542

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

deal a bit better with Unicode file names

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