source: trunk/GSASII.py @ 2563

Last change on this file since 2563 was 2563, checked in by vondreele, 7 years ago

Speed up image integration on Mac by reducing ProgressBar? calls from 3*nBlk*nBlk+3 to Nblk+3
Allow PDF setup to include chemical formula from tif macrofile
fix contour bug for G(R), etc.
fix copy error for masks

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