source: trunk/GSASII.py @ 2699

Last change on this file since 2699 was 2699, checked in by vondreele, 5 years ago

fix bug involving input of lab data - Sample defaults for Bragg-Brentano not correctly set - fixed

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