source: trunk/GSASII.py @ 2635

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

add Yield to allow grid update completion

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