source: trunk/GSASII.py @ 2634

Last change on this file since 2634 was 2634, checked in by vondreele, 6 years ago

add ExpandAll? to main File menu
change PDF plotting to use color schemes for multiplots
's' option on multiplot changed to color scheme selection
removed G9r0-4pi*r plot

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