source: trunk/GSASII.py @ 2593

Last change on this file since 2593 was 2593, checked in by toby, 5 years ago

Add Pawley settings menu item; open Phases and selected data tree item on GPX open

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