source: trunk/GSASII.py @ 2715

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

put histogram numbers in sorted order for View LS parms
remove some commented lines from G2pwdrGUI
force seq refinement to skip unused phases in each histogram - hap parameters are ignored & seq results table shows blanks for them
seq plot plot skips blanks - leaves blank spots in any parameter line

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Author Revision URL Id
File size: 198.0 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2017-02-18 22:22:21 +0000 (Sat, 18 Feb 2017) $
6# $Author: vondreele $
7# $Revision: 2715 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2715 2017-02-18 22:22:21Z 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: 2715 $")
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.OnShowLSParms, 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.update({'Type':'Bragg-Brentano','Shift':[0.,False],'Transparency':[0.,False],
1347                        'SurfRoughA':[0.,False],'SurfRoughB':[0.,False]})
1348                else:
1349                    rd.Sample.update({'Type':'Debye-Scherrer','Absorption':[0.,False],'DisplaceX':[0.,False],
1350                        'DisplaceY':[0.,False]})
1351                if 'Generic' in choices[res]:
1352                    dlg = G2G.MultiFloatDialog(self,title='Generic TOF detector bank',
1353                        prompts=['Total FP','2-theta',],values=[25.0,150.,],
1354                            limits=[[6.,200.],[5.,175.],],formats=['%6.2f','%6.1f',])
1355                    if dlg.ShowModal() == wx.ID_OK: #strictly empirical approx.
1356                        FP,tth = dlg.GetValues()
1357                        difC = 505.632*FP*sind(tth/2.)
1358                        sig1 = 50.+2.5e-6*(difC/tand(tth/2.))**2
1359                        bet1 = .00226+7.76e+11/difC**4
1360                        rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1361                        Inst = self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)
1362                        Inst[0]['difC'] = [difC,difC,0]
1363                        Inst[0]['sig-1'] = [sig1,sig1,0]
1364                        Inst[0]['beta-1'] = [bet1,bet1,0]
1365                        return Inst    #this is [Inst1,Inst2] a pair of dicts
1366                    dlg.Destroy()
1367                else:
1368                    rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1369                    return self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1370
1371        # stuff we might need from the reader
1372        filename = rd.powderentry[0]
1373        bank = rd.powderentry[2]
1374        numbanks = rd.numbanks
1375        #1st priority: is there an instrument parameter file matching the current file
1376        # with extension .instprm, .prm, .inst, or .ins? If so read it
1377        basename = os.path.splitext(filename)[0]
1378        for ext in '.prm','.inst','.ins','.instprm':
1379            if self.zipfile:
1380                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1381                    selection=os.path.split(basename + ext)[1],parent=self)
1382                if instfile == None:
1383                    continue
1384            else:
1385                instfile = basename + ext
1386            if not os.path.exists(instfile):
1387                continue
1388            if 'instprm' in instfile:
1389                Lines = self.OpenPowderInstprm(instfile)
1390                instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1391                if 'list' in str(type(instParmList)):
1392                    rd.instfile = instfile
1393                    rd.instmsg = 'GSAS-II file '+instfile
1394                    return instParmList
1395                else:
1396                    #print 'debug: open/read failed',instfile
1397                    pass # fail silently
1398            else:
1399                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1400                if Iparm:
1401                    #print 'debug: success'
1402                    rd.instfile = instfile
1403                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1404                    return SetPowderInstParms(Iparm,rd)
1405                else:
1406                    #print 'debug: open/read failed',instfile
1407                    pass # fail silently
1408
1409        #2nd priority: is there an instrument parameter file defined for the current data set?
1410        # or if this is a read on a set of set of files, use the last one again
1411        #if rd.instparm as found in data file header or (lastdatafile == filename and lastIparmfile):
1412        if rd.instparm or lastIparmfile:
1413            if rd.instparm:
1414                instfile = os.path.join(os.path.split(filename)[0],rd.instparm)
1415            else:
1416                # for multiple reads of one data file, reuse the inst parm file
1417                instfile = lastIparmfile
1418#            if self.zipfile:
1419#                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1420#                    selection=os.path.split(instfile)[1],parent=self)
1421            if instfile != None and os.path.exists(instfile):
1422                #print 'debug: try read',instfile
1423                if 'instprm' in instfile:   #GSAS-II file must have .instprm as extension
1424                    Lines = self.OpenPowderInstprm(instfile)
1425                    if Lines is not None:
1426                        instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)   #this is [Inst1,Inst2] a pair of dicts
1427                else:   #old GSAS style iparm file - could be named anything!
1428                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1429                    if Iparm:
1430                        #print 'debug: success'
1431                        rd.instfile = instfile
1432                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1433                        instParmList = SetPowderInstParms(Iparm,rd)     #this is [Inst1,Inst2] a pair of dicts
1434                if 'list' in str(type(instParmList)):   #record stuff & return stuff
1435                    rd.instfile = instfile
1436                    rd.instmsg = 'GSAS-II file '+instfile
1437                    return instParmList
1438                else:   #bad iparms - try default
1439                    rd.instmsg = instParmList   #an error message
1440                    return GetDefaultParms(self,rd)
1441            else:
1442                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1443                    +str(instfile)+' requested by file '+ filename)
1444        #Finally - ask user for Instrument parametrs file - seems it can't be in a zip file
1445        while True: # loop until we get a file that works or we get a cancel
1446            instfile = ''
1447            pth = G2G.GetImportPath(self)
1448            if not pth: pth = '.'
1449            dlg = wx.FileDialog(self,
1450                'Choose inst. param file for "'+rd.idstring+'" (or Cancel for default)',
1451                pth, '',
1452                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1453                'GSAS-II iparm file (*.instprm)|*.instprm|'
1454                'All files (*.*)|*.*', wx.OPEN)
1455            if os.path.exists(lastIparmfile):
1456                dlg.SetFilename(lastIparmfile)
1457            if dlg.ShowModal() == wx.ID_OK:
1458                instfile = dlg.GetPath()
1459            dlg.Destroy()
1460            if not instfile: 
1461                return GetDefaultParms(self,rd) #on Cancel/break
1462            if 'instprm' in instfile:
1463                Lines = self.OpenPowderInstprm(instfile)
1464                if Lines is not None:
1465                    instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1466                if 'list' in str(type(instParmList)):
1467                    rd.instfile = instfile
1468                    rd.instmsg = 'GSAS-II file '+instfile
1469                    return instParmList
1470                else:
1471                    rd.instmsg = instParmList   #an error message
1472                    return GetDefaultParms(self,rd)
1473            else:
1474                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1475                if Iparm:
1476                    #print 'debug: success with',instfile
1477                    rd.instfile = instfile
1478                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1479                    return SetPowderInstParms(Iparm,rd)
1480                else:
1481                    self.ErrorDialog('Read Error',
1482                                     'Error opening/reading file '+str(instfile))
1483    def EnableRefineCommand(self):
1484        haveData = False
1485        # check for phases connected to histograms
1486        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1487        if not sub: return
1488        item, cookie = self.PatternTree.GetFirstChild(sub)
1489        while item: # loop over phases
1490            data = self.PatternTree.GetItemPyData(item)
1491            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1492            UseList = data['Histograms']
1493            if UseList: haveData = True
1494        if haveData:
1495            self.dataFrame.DataMenu.Enable(G2gd.wxID_DATADELETE,True)
1496            for item in self.Refine: item.Enable(True)
1497        else:
1498            self.dataFrame.DataMenu.Enable(G2gd.wxID_DATADELETE,False)
1499            for item in self.Refine: item.Enable(False)
1500
1501       
1502    def OnImportPowder(self,event):
1503        '''Called in response to an Import/Powder Data/... menu item
1504        to read a powder diffraction data set.
1505        dict self.ImportMenuId is used to look up the specific
1506        reader item associated with the menu item, which will be
1507        None for the last menu item, which is the "guess" option
1508        where all appropriate formats will be tried.
1509
1510        Also reads an instrument parameter file for each dataset.
1511        '''
1512        # get a list of existing histograms
1513        PWDRlist = []
1514        if self.PatternTree.GetCount():
1515            item, cookie = self.PatternTree.GetFirstChild(self.root)
1516            while item:
1517                name = self.PatternTree.GetItemText(item)
1518                if name.startswith('PWDR ') and name not in PWDRlist:
1519                    PWDRlist.append(name)
1520                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1521        # look up which format was requested
1522        reqrdr = self.ImportMenuId.get(event.GetId()) 
1523        rdlist = self.OnImportGeneric(
1524            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1525        if len(rdlist) == 0: return
1526        self.CheckNotebook()
1527        Iparm = None
1528        lastIparmfile = ''
1529        lastdatafile = ''
1530        newHistList = []
1531        lastVals = []
1532        self.EnablePlot = False
1533        for rd in rdlist:
1534            if 'Instrument Parameters' in rd.pwdparms:
1535                Iparm1,Iparm2 = rd.pwdparms['Instrument Parameters']
1536            else:
1537                # get instrument parameters for each dataset, unless already set
1538                if lastIparmfile:  # is this histogram like previous?
1539                    if lastVals != (rd.powderdata[0].min(),rd.powderdata[0].max(),len(rd.powderdata[0])):
1540                        lastIparmfile = ''
1541                Iparms = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1542                if not Iparms:  #may have bailed out
1543                    Id = 0
1544                    continue
1545                Iparm1,Iparm2 = Iparms
1546                if rd.repeat_instparm: 
1547                    lastIparmfile = rd.instfile
1548                    lastVals = (rd.powderdata[0].min(),rd.powderdata[0].max(),len(rd.powderdata[0]))
1549                # override any keys in read instrument parameters with ones set in import
1550                for key in Iparm1: 
1551                    if key in rd.instdict:
1552                        Iparm1[key] = rd.instdict[key]
1553            lastdatafile = rd.powderentry[0]
1554            HistName = 'PWDR '+G2obj.StripUnicode(rd.idstring,'_')
1555            # make new histogram names unique
1556            if HistName in PWDRlist:
1557                dlg = wx.MessageDialog(self,'Skip %s?'%(HistName),'Duplicate data name',wx.YES_NO)
1558                try:
1559                    if dlg.ShowModal() == wx.ID_YES:
1560                        Id = 0
1561                        continue
1562                finally:
1563                    dlg.Destroy()
1564            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1565            print('Read powder data '+HistName+ 
1566                ' from file '+rd.readfilename +
1567                ' (format: '+ rd.formatName + 
1568                '). Inst parameters from '+rd.instmsg)
1569            # data are read, now store them in the tree
1570            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1571            if 'T' in Iparm1['Type'][0]:
1572                if not rd.clockWd and rd.GSAS:
1573                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1574                cw = np.diff(rd.powderdata[0])
1575                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1576                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
1577                    npts = min(len(rd.powderdata[0]),len(rd.powderdata[1]),len(cw))
1578                    rd.powderdata[1] = rd.powderdata[1][:npts]/cw[:npts]
1579                    rd.powderdata[2] = rd.powderdata[2][:npts]*cw[:npts]**2  #1/var=w at this point
1580                else:       #NB: from topas/fullprof type files
1581                    rd.powderdata[1] = rd.powderdata[1][:-1]
1582                    rd.powderdata[2] = rd.powderdata[2][:-1]
1583                if 'Itype' in Iparm2:
1584                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1585                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1586                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1587                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1588                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1589                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1590                    var += WYI*rd.powderdata[1]**2
1591                    var /= YI**2
1592                    rd.powderdata[2] = 1./var
1593                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])
1594                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])
1595                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])
1596            Ymin = np.min(rd.powderdata[1])                 
1597            Ymax = np.max(rd.powderdata[1])                 
1598            valuesdict = {
1599                'wtFactor':1.0,
1600                'Dummy':False,
1601                'ranId':ran.randint(0,sys.maxint),
1602                'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
1603                'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
1604                }
1605            # apply user-supplied corrections to powder data
1606            if 'CorrectionCode' in Iparm1:
1607                print('Applying corrections from instprm file')
1608                corr = Iparm1['CorrectionCode'][0]
1609                try:
1610                    exec(corr)
1611                    print('done')
1612                except Exception as err:
1613                    print('error: '+str(err))
1614                    print('with commands -------------------')
1615                    print(str(corr))
1616                    print('---------------------------------')
1617                finally:
1618                    del Iparm1['CorrectionCode']
1619            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1620            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1621            self.PatternTree.SetItemPyData(
1622                self.PatternTree.AppendItem(Id,text='Comments'),
1623                rd.comments)
1624            Tmin = min(rd.powderdata[0])
1625            Tmax = max(rd.powderdata[0])
1626            Tmin1 = Tmin
1627            if 'NT' in Iparm1['Type'][0] and G2lat.Pos2dsp(Iparm1,Tmin) < 0.4:               
1628                Tmin1 = G2lat.Dsp2pos(Iparm1,0.4)
1629            self.PatternTree.SetItemPyData(
1630                self.PatternTree.AppendItem(Id,text='Limits'),
1631                rd.pwdparms.get('Limits',[(Tmin,Tmax),[Tmin1,Tmax]])
1632                )
1633            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1634            self.PatternTree.SetItemPyData(
1635                self.PatternTree.AppendItem(Id,text='Background'),
1636                rd.pwdparms.get('Background',
1637                    [['chebyschev',True,3,1.0,0.0,0.0],{'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1638                    )
1639            self.PatternTree.SetItemPyData(
1640                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1641                [Iparm1,Iparm2])
1642            self.PatternTree.SetItemPyData(
1643                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1644                rd.Sample)
1645            self.PatternTree.SetItemPyData(
1646                self.PatternTree.AppendItem(Id,text='Peak List')
1647                ,{'peaks':[],'sigDict':{}})
1648            self.PatternTree.SetItemPyData(
1649                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1650                [[],[]])
1651            self.PatternTree.SetItemPyData(
1652                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1653                [])
1654            self.PatternTree.SetItemPyData(
1655                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1656                {})
1657            # if any Control values have been set, move them into tree
1658            Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
1659            Controls.update(rd.Controls)
1660            newHistList.append(HistName)
1661            rd.repeat_instparm = False  #clear the iparm reuse flag
1662        else:
1663            self.EnablePlot = True
1664            if Id:
1665                self.PatternTree.Expand(Id)
1666                self.PatternTree.SelectItem(Id)
1667
1668        if not newHistList: return # somehow, no new histograms
1669        # make a list of phase names
1670        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1671        phaseNameList = usedHistograms.keys() # phase names in use
1672        if not phaseNameList: return # no phases yet, nothing to do
1673        header = 'Select phase(s) to link\nto the newly-read data:'
1674        for Name in newHistList:
1675            header += '\n  '+str(Name)
1676
1677        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1678        if not result: return
1679        # connect new phases to histograms
1680        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1681        if not sub:
1682            raise Exception('ERROR -- why are there no phases here?')
1683        item, cookie = self.PatternTree.GetFirstChild(sub)
1684        iph = -1
1685        while item: # loop over (new) phases
1686            iph += 1
1687            data = self.PatternTree.GetItemPyData(item)
1688            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1689            if iph not in result: continue
1690            generalData = data['General']
1691            SGData = generalData['SGData']
1692            UseList = data['Histograms']
1693            NShkl = len(G2spc.MustrainNames(SGData))
1694            NDij = len(G2spc.HStrainNames(SGData))
1695            for histoName in newHistList:
1696                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1697                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1698                refList = self.PatternTree.GetItemPyData(
1699                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1700                refList[generalData['Name']] = []
1701        self.EnableRefineCommand()
1702        return # success
1703
1704    def OnDummyPowder(self,event):
1705        '''Called in response to Import/Powder Data/Simulate menu item
1706        to create a Dummy powder diffraction data set.
1707
1708        Reads an instrument parameter file and then gets input from the user
1709        '''
1710        # get a list of existing histograms
1711        PWDRlist = []
1712        if self.PatternTree.GetCount():
1713            item, cookie = self.PatternTree.GetFirstChild(self.root)
1714            while item:
1715                name = self.PatternTree.GetItemText(item)
1716                if name.startswith('PWDR ') and name not in PWDRlist:
1717                    PWDRlist.append(name)
1718                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1719        # Initialize a base class reader
1720        rd = G2IO.ImportPowderData(
1721            extensionlist=tuple(),
1722            strictExtension=False,
1723            formatName = 'Simulate dataset',
1724            longFormatName = 'Compute a simulated pattern')
1725        rd.powderentry[0] = '' # no filename
1726        # #self.powderentry[1] = pos # bank offset (N/A here)
1727        rd.powderentry[2] = 1 # only one bank
1728        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1729        self.CheckNotebook()
1730        Iparm = None
1731        lastdatafile = ''
1732        self.zipfile = None
1733        # get instrument parameters for it
1734        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, '', lastdatafile)
1735        if 'T' in Iparm1['Type'][0]:
1736            print('TOF simulation not supported yet')
1737            return False
1738        else:
1739            # need to get name, 2theta start, end, step
1740            rd.idstring = ' CW'
1741            if 'X' in Iparm1['Type'][0]:
1742                rd.idstring = 'CW x-ray simulation'
1743            else:
1744                rd.idstring = 'CW neutron simulation'
1745            # base initial range on wavelength
1746            wave = Iparm1.get('Lam')
1747            if wave:
1748                wave = wave[0]
1749            else:
1750                wave = Iparm1.get('Lam1')
1751                if wave:
1752                    wave = wave[0]
1753        N = 0
1754        while (N < 3): # insist on a dataset with a few points
1755            names = ('dataset name', 'start angle', 'end angle', 'step size')
1756            if not wave or wave < 1.0:
1757                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1758            else:
1759                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1760            dlg = G2G.ScrolledMultiEditor(
1761                self,[inp] * len(inp),range(len(inp)),names,
1762                header='Enter simulation name and range',
1763                minvals=(None,0.001,0.001,0.0001),
1764                maxvals=(None,180.,180.,.1),
1765                sizevals=((225,-1),)
1766                )
1767            dlg.CenterOnParent()
1768            if dlg.ShowModal() == wx.ID_OK:
1769                if inp[1] > inp[2]:
1770                    end,start,step = inp[1:]
1771                else:               
1772                    start,end,step = inp[1:]
1773                step = abs(step)
1774            else:
1775                return False
1776            N = int((end-start)/step)+1
1777            x = np.linspace(start,end,N,True)
1778            N = len(x)
1779        rd.powderdata = [
1780            np.array(x), # x-axis values
1781            np.zeros_like(x), # powder pattern intensities
1782            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1783            np.zeros_like(x), # calc. intensities (zero)
1784            np.zeros_like(x), # calc. background (zero)
1785            np.zeros_like(x), # obs-calc profiles
1786            ]
1787        Tmin = rd.powderdata[0][0]
1788        Tmax = rd.powderdata[0][-1]
1789        # data are read, now store them in the tree
1790        HistName = inp[0]
1791        HistName = 'PWDR '+HistName
1792        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1793        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1794        Ymin = np.min(rd.powderdata[1])
1795        Ymax = np.max(rd.powderdata[1])
1796        valuesdict = {
1797            'wtFactor':1.0,
1798            'Dummy':True,
1799            'ranId':ran.randint(0,sys.maxint),
1800            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
1801            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
1802            }
1803        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1804        self.PatternTree.SetItemPyData(
1805            self.PatternTree.AppendItem(Id,text='Comments'),
1806            rd.comments)
1807        self.PatternTree.SetItemPyData(
1808            self.PatternTree.AppendItem(Id,text='Limits'),
1809            [(Tmin,Tmax),[Tmin,Tmax]])
1810        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1811        self.PatternTree.SetItemPyData(
1812            self.PatternTree.AppendItem(Id,text='Background'),
1813            [['chebyschev',True,3,1.0,0.0,0.0],
1814             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1815        self.PatternTree.SetItemPyData(
1816            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1817            [Iparm1,Iparm2])
1818        self.PatternTree.SetItemPyData(
1819            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1820            rd.Sample)
1821        self.PatternTree.SetItemPyData(
1822            self.PatternTree.AppendItem(Id,text='Peak List')
1823            ,{'peaks':[],'sigDict':{}})
1824        self.PatternTree.SetItemPyData(
1825            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1826            [[],[]])
1827        self.PatternTree.SetItemPyData(
1828            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1829            [])
1830        self.PatternTree.SetItemPyData(
1831            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1832            {})
1833        self.PatternTree.Expand(Id)
1834        self.PatternTree.SelectItem(Id)
1835        print('Added simulation powder data '+HistName+ 
1836              ' with parameters from '+str(rd.instmsg))
1837
1838        # make a list of phase names
1839        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1840        phaseNameList = usedHistograms.keys() # phase names in use
1841        if not phaseNameList: return # no phases yet, nothing to do
1842        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1843        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1844        if not result: return
1845        # connect new phases to histograms
1846        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1847        if not sub:
1848            raise Exception('ERROR -- why are there no phases here?')
1849        item, cookie = self.PatternTree.GetFirstChild(sub)
1850        iph = -1
1851        while item: # loop over (new) phases
1852            iph += 1
1853            data = self.PatternTree.GetItemPyData(item)
1854            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1855            if iph not in result: continue
1856            generalData = data['General']
1857            SGData = generalData['SGData']
1858            UseList = data['Histograms']
1859            NShkl = len(G2spc.MustrainNames(SGData))
1860            NDij = len(G2spc.HStrainNames(SGData))
1861            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
1862            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1863            refList = self.PatternTree.GetItemPyData(
1864                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1865            refList[generalData['Name']] = []
1866        self.EnableRefineCommand()
1867        return # success
1868       
1869    def OnPreferences(self,event):
1870        'Edit the GSAS-II configuration variables'
1871        dlg = G2G.SelectConfigSetting(self)
1872        dlg.ShowModal() == wx.ID_OK
1873        dlg.Destroy()
1874
1875    def _Add_ImportMenu_smallangle(self,parent):
1876        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1877        '''
1878        submenu = wx.Menu()
1879        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1880            submenu, help='Import small angle data')
1881        for reader in self.ImportSmallAngleReaderlist:
1882            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1883                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1884            self.ImportMenuId[item.GetId()] = reader
1885            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1886        # item = submenu.Append(wx.ID_ANY,
1887        #     help='Import small angle data, use file to try to determine format',
1888        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1889        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1890
1891    def OnImportSmallAngle(self,event):
1892        '''Called in response to an Import/Small Angle Data/... menu item
1893        to read a small angle diffraction data set.
1894        dict self.ImportMenuId is used to look up the specific
1895        reader item associated with the menu item, which will be
1896        None for the last menu item, which is the "guess" option
1897        where all appropriate formats will be tried.
1898
1899        '''
1900       
1901        def GetSASDIparm(reader):
1902            parm = reader.instdict
1903            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1904                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1905            return Iparm,{}
1906           
1907        # get a list of existing histograms
1908        SASDlist = []
1909        if self.PatternTree.GetCount():
1910            item, cookie = self.PatternTree.GetFirstChild(self.root)
1911            while item:
1912                name = self.PatternTree.GetItemText(item)
1913                if name.startswith('SASD ') and name not in SASDlist:
1914                    SASDlist.append(name)
1915                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1916        # look up which format was requested
1917        reqrdr = self.ImportMenuId.get(event.GetId()) 
1918        rdlist = self.OnImportGeneric(
1919            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1920        if len(rdlist) == 0: return
1921        self.CheckNotebook()
1922        newHistList = []
1923        self.EnablePlot = False
1924        for rd in rdlist:
1925            HistName = rd.idstring
1926            HistName = 'SASD '+HistName
1927            # make new histogram names unique
1928            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1929            print 'Read small angle data '+HistName+ \
1930                ' from file '+self.lastimport
1931            # data are read, now store them in the tree
1932            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1933            Iparm1,Iparm2 = GetSASDIparm(rd)
1934#            if 'T' in Iparm1['Type'][0]:
1935#                if not rd.clockWd and rd.GSAS:
1936#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1937#                cw = np.diff(rd.powderdata[0])
1938#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1939#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1940#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1941#                if 'Itype' in Iparm2:
1942#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1943#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1944#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1945#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1946#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1947#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1948#                    var += WYI*rd.powderdata[1]**2
1949#                    var /= YI**2
1950#                    rd.powderdata[2] = 1./var
1951#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1952#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1953#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1954            Tmin = min(rd.smallangledata[0])
1955            Tmax = max(rd.smallangledata[0])
1956            valuesdict = {
1957                'wtFactor':1.0,
1958                'Dummy':False,
1959                'ranId':ran.randint(0,sys.maxint),
1960                }
1961            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1962            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1963            self.PatternTree.SetItemPyData(
1964                self.PatternTree.AppendItem(Id,text='Comments'),
1965                rd.comments)
1966            self.PatternTree.SetItemPyData(
1967                self.PatternTree.AppendItem(Id,text='Limits'),
1968                [(Tmin,Tmax),[Tmin,Tmax]])
1969            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1970            self.PatternTree.SetItemPyData(
1971                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1972                [Iparm1,Iparm2])
1973            self.PatternTree.SetItemPyData(
1974                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1975            self.PatternTree.SetItemPyData(
1976                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1977                rd.Sample)
1978            self.PatternTree.SetItemPyData(
1979                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1980            newHistList.append(HistName)
1981        else:
1982            self.EnablePlot = True
1983            self.PatternTree.Expand(Id)
1984            self.PatternTree.SelectItem(Id)
1985           
1986        if not newHistList: return # somehow, no new histograms
1987        return # success
1988
1989    def OnMacroRecordStatus(self,event,setvalue=None):
1990        '''Called when the record macro menu item is used which toggles the
1991        value. Alternately a value to be set can be provided. Note that this
1992        routine is made more complex because on the Mac there are lots of menu
1993        items (listed in self.MacroStatusList) and this loops over all of them.
1994        '''
1995        nextvalue = log.ShowLogStatus() != True
1996        if setvalue is not None:
1997            nextvalue = setvalue
1998        if nextvalue:
1999            log.LogOn()
2000            set2 = True
2001        else:
2002            log.LogOff()
2003            set2 = False
2004        for menuitem in self.MacroStatusList:
2005            menuitem.Check(set2)
2006
2007    def _init_Macro(self):
2008        '''Define the items in the macro menu.
2009        '''
2010        menu = self.MacroMenu
2011        item = menu.Append(
2012                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
2013                kind=wx.ITEM_CHECK,text='Record actions')
2014        self.MacroStatusList.append(item)
2015        item.Check(log.ShowLogStatus())
2016        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
2017
2018        # this may only be of value for development work
2019        item = menu.Append(
2020            help='Show logged commands', id=wx.ID_ANY,
2021            kind=wx.ITEM_NORMAL,text='Show log')
2022        def OnShowLog(event):
2023            print 70*'='
2024            print 'List of logged actions'
2025            for i,line in enumerate(log.G2logList):
2026                if line: print i,line
2027            print 70*'='
2028        self.Bind(wx.EVT_MENU, OnShowLog, item)
2029
2030        item = menu.Append(
2031            help='Clear logged commands', id=wx.ID_ANY,
2032            kind=wx.ITEM_NORMAL,text='Clear log')
2033        def OnClearLog(event): log.G2logList=[None]
2034        self.Bind(wx.EVT_MENU, OnClearLog, item)
2035       
2036        item = menu.Append(
2037            help='Save logged commands to file', id=wx.ID_ANY,
2038            kind=wx.ITEM_NORMAL,text='Save log')
2039        def OnSaveLog(event):
2040            import cPickle
2041            defnam = os.path.splitext(
2042                os.path.split(self.GSASprojectfile)[1]
2043                )[0]+'.gcmd'
2044            dlg = wx.FileDialog(self,
2045                'Choose an file to save past actions', '.', defnam, 
2046                'GSAS-II cmd output (*.gcmd)|*.gcmd',
2047                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2048            dlg.CenterOnParent()
2049            try:
2050                if dlg.ShowModal() == wx.ID_OK:
2051                    filename = dlg.GetPath()
2052                    # make sure extension is correct
2053                    filename = os.path.splitext(filename)[0]+'.gcmd'
2054                else:
2055                    filename = None
2056            finally:
2057                dlg.Destroy()
2058            if filename:
2059                fp = open(filename,'wb')
2060                fp.write(str(len(log.G2logList)-1)+'\n')
2061                for item in log.G2logList:
2062                    if item: cPickle.dump(item,fp)
2063                fp.close()
2064        self.Bind(wx.EVT_MENU, OnSaveLog, item)
2065
2066        item = menu.Append(
2067            help='Load logged commands from file', id=wx.ID_ANY,
2068            kind=wx.ITEM_NORMAL,text='Load log')
2069        def OnLoadLog(event):
2070            # this appends. Perhaps we should ask to clear?
2071            import cPickle
2072            defnam = os.path.splitext(
2073                os.path.split(self.GSASprojectfile)[1]
2074                )[0]+'.gcmd'
2075            dlg = wx.FileDialog(self,
2076                'Choose an file to read saved actions', '.', defnam, 
2077                'GSAS-II cmd output (*.gcmd)|*.gcmd',
2078                wx.OPEN)
2079            dlg.CenterOnParent()
2080            try:
2081                if dlg.ShowModal() == wx.ID_OK:
2082                    filename = dlg.GetPath()
2083                    # make sure extension is correct
2084                    filename = os.path.splitext(filename)[0]+'.gcmd'
2085                else:
2086                    filename = None
2087            finally:
2088                dlg.Destroy()
2089            if filename and os.path.exists(filename):
2090                fp = open(filename,'rb')
2091                lines = fp.readline()
2092                for i in range(int(lines)):
2093                    log.G2logList.append(cPickle.load(fp))
2094                fp.close()
2095        self.Bind(wx.EVT_MENU, OnLoadLog, item)
2096
2097        item = menu.Append(
2098            help='Replay saved commands', id=wx.ID_ANY,
2099            kind=wx.ITEM_NORMAL,text='Replay log')
2100        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
2101
2102    def _init_Exports(self,menu):
2103        '''Find exporter routines and add them into menus
2104        '''
2105        # set up the top-level menus
2106        projectmenu = wx.Menu()
2107        item = menu.AppendMenu(
2108            wx.ID_ANY, 'Entire project as',
2109            projectmenu, help='Export entire project')
2110
2111        phasemenu = wx.Menu()
2112        item = menu.AppendMenu(
2113            wx.ID_ANY, 'Phase as',
2114            phasemenu, help='Export phase or sometimes phases')
2115
2116        powdermenu = wx.Menu()
2117        item = menu.AppendMenu(
2118            wx.ID_ANY, 'Powder data as',
2119            powdermenu, help='Export powder diffraction histogram(s)')
2120
2121        singlemenu = wx.Menu()
2122        item = menu.AppendMenu(
2123            wx.ID_ANY, 'Single crystal data as',
2124            singlemenu, help='Export single crystal histogram(s)')
2125
2126        imagemenu = wx.Menu()
2127        item = menu.AppendMenu(
2128            wx.ID_ANY, 'Image data as',
2129            imagemenu, help='Export powder image(s) data')
2130
2131        mapmenu = wx.Menu()
2132        item = menu.AppendMenu(
2133            wx.ID_ANY, 'Maps as',
2134            mapmenu, help='Export density map(s)')
2135
2136        # pdfmenu = wx.Menu()
2137        # item = menu.AppendMenu(
2138        #     wx.ID_ANY, 'PDFs as',
2139        #     pdfmenu, help='Export pair distribution function(s)')
2140
2141        # find all the exporter files
2142        if not self.exporterlist: # this only needs to be done once
2143            pathlist = sys.path
2144            filelist = []
2145            for path in pathlist:
2146                for filename in glob.iglob(os.path.join(path,"G2export*.py")):
2147                    filelist.append(filename)   
2148            filelist = sorted(list(set(filelist))) # remove duplicates
2149            # go through the routines and import them, saving objects that
2150            # have export routines (method Exporter)
2151            for filename in filelist:
2152                path,rootname = os.path.split(filename)
2153                pkg = os.path.splitext(rootname)[0]
2154#                try:
2155                fp = None
2156                fp, fppath,desc = imp.find_module(pkg,[path,])
2157                pkg = imp.load_module(pkg,fp,fppath,desc)
2158                for clss in inspect.getmembers(pkg): # find classes defined in package
2159                    if clss[0].startswith('_'): continue
2160                    if inspect.isclass(clss[1]):
2161                        # check if we have the required methods
2162                        for m in 'Exporter','loadParmDict':
2163                            if not hasattr(clss[1],m): break
2164                            if not callable(getattr(clss[1],m)): break
2165                        else:
2166                            exporter = clss[1](self) # create an export instance
2167                            self.exporterlist.append(exporter)
2168#                except AttributeError:
2169#                    print 'Import_'+errprefix+': Attribute Error'+str(filename)
2170#                    pass
2171#                except ImportError:
2172#                    print 'Import_'+errprefix+': Error importing file'+str(filename)
2173#                    pass
2174                if fp: fp.close()
2175        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
2176        for obj in self.exporterlist:
2177            #print 'exporter',obj
2178            for typ in obj.exporttype:
2179                if typ == "project":
2180                    submenu = projectmenu
2181                elif typ == "phase":
2182                    submenu = phasemenu
2183                elif typ == "powder":
2184                    submenu = powdermenu
2185                elif typ == "single":
2186                    submenu = singlemenu
2187                elif typ == "image":
2188                    submenu = imagemenu
2189                elif typ == "map":
2190                    submenu = mapmenu
2191                # elif typ == "pdf":
2192                #     submenu = pdfmenu
2193                else:
2194                    print("Error, unknown type in "+str(obj))
2195                    break
2196                item = submenu.Append(
2197                    wx.ID_ANY,
2198                    help=obj.longFormatName,
2199                    kind=wx.ITEM_NORMAL,
2200                    text=obj.formatName)
2201                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
2202                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
2203        item = imagemenu.Append(wx.ID_ANY,
2204                        help='Export image controls and masks for multiple images',
2205                        kind=wx.ITEM_NORMAL,
2206                        text='Multiple image controls and masks')
2207        self.Bind(wx.EVT_MENU, self.OnSaveMultipleImg, id=item.GetId())
2208        #code to debug an Exporter. hard-code the routine below, to allow a reload before use
2209        # def DebugExport(event):
2210        #      print 'start reload'
2211        #      reload(G2IO)
2212        #      import G2export_pwdr as dev
2213        #      reload(dev)
2214        #      dev.ExportPowderFXYE(self).Exporter(event)
2215        # item = menu.Append(
2216        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2217        #     help="debug exporter",text="test Export FXYE")
2218        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
2219        # # #self.ExportLookup[item.GetId()] = 'image'
2220        # self.ExportLookup[item.GetId()] = 'powder'
2221
2222    def _Add_ExportMenuItems(self,parent):
2223        # item = parent.Append(
2224        #     help='Select PWDR item to enable',id=wx.ID_ANY,
2225        #     kind=wx.ITEM_NORMAL,
2226        #     text='Export Powder Patterns...')
2227        # self.ExportPattern.append(item)
2228        # item.Enable(False)
2229        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
2230
2231        item = parent.Append(
2232            help='',id=wx.ID_ANY,
2233            kind=wx.ITEM_NORMAL,
2234            text='Export All Peak Lists...')
2235        self.ExportPeakList.append(item)
2236        item.Enable(True)
2237        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
2238
2239        item = parent.Append(
2240            help='',id=wx.ID_ANY,
2241            kind=wx.ITEM_NORMAL,
2242            text='Export HKLs...')
2243        self.ExportHKL.append(item)
2244        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
2245
2246        item = parent.Append(
2247            help='Select PDF item to enable',
2248            id=wx.ID_ANY,
2249            kind=wx.ITEM_NORMAL,
2250            text='Export PDF...')
2251        self.ExportPDF.append(item)
2252        item.Enable(False)
2253        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
2254
2255    def FillMainMenu(self,menubar,addhelp=True):
2256        '''Define contents of the main GSAS-II menu for the (main) data tree window.
2257        For the mac, this is also called for the data item windows as well so that
2258        the main menu items are data menu as well.
2259        '''
2260        File = wx.Menu(title='')
2261        menubar.Append(menu=File, title='&File')
2262        self._Add_FileMenuItems(File)
2263        Data = wx.Menu(title='')
2264        menubar.Append(menu=Data, title='Data')
2265        self._Add_DataMenuItems(Data)
2266        Calculate = wx.Menu(title='')       
2267        menubar.Append(menu=Calculate, title='&Calculate')
2268        self._Add_CalculateMenuItems(Calculate)
2269        Import = wx.Menu(title='')       
2270        menubar.Append(menu=Import, title='Import')
2271        self._Add_ImportMenu_Image(Import)
2272        self._Add_ImportMenu_Phase(Import)
2273        self._Add_ImportMenu_powder(Import)
2274        self._Add_ImportMenu_Sfact(Import)
2275        self._Add_ImportMenu_smallangle(Import)
2276
2277        #======================================================================
2278        # Code to help develop/debug an importer, much is hard-coded below
2279        # but module is reloaded before each use, allowing faster testing
2280        # def DebugImport(event):
2281        #     print 'start reload'
2282        #     import G2phase_ISO as dev
2283        #     reload(dev)
2284        #     rd = dev.ISODISTORTPhaseReader()
2285        #     self.ImportMenuId[event.GetId()] = rd
2286        #     self.OnImportPhase(event)
2287            # or ----------------------------------------------------------------------
2288            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
2289            # special debug code
2290            # or ----------------------------------------------------------------------
2291            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
2292            # fp = open(filename,'Ur')
2293            # if not rd.ContentsValidator(fp):
2294            #     print 'not validated'
2295            #     # make a list of used phase ranId's
2296            # phaseRIdList = []
2297            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2298            # if sub:
2299            #     item, cookie = self.PatternTree.GetFirstChild(sub)
2300            #     while item:
2301            #         phaseName = self.PatternTree.GetItemText(item)
2302            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2303            #         if ranId: phaseRIdList.append(ranId)
2304            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2305            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2306            #     print 'read OK'
2307        # item = Import.Append(
2308        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2309        #     help="debug importer",text="test importer")
2310        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2311        #======================================================================
2312        self.ExportMenu = wx.Menu(title='')
2313        menubar.Append(menu=self.ExportMenu, title='Export')
2314        self._init_Exports(self.ExportMenu)
2315        self._Add_ExportMenuItems(self.ExportMenu)
2316        if GSASIIpath.GetConfigValue('Enable_logging'):
2317            self.MacroMenu = wx.Menu(title='')
2318            menubar.Append(menu=self.MacroMenu, title='Macro')
2319            self._init_Macro()
2320        if addhelp:
2321            HelpMenu=G2G.MyHelp(self,includeTree=True,
2322                morehelpitems=[('&Tutorials','Tutorials'),])
2323            menubar.Append(menu=HelpMenu,title='&Help')
2324           
2325    def _init_ctrls(self, parent):
2326        wx.Frame.__init__(self, name='GSASII', parent=parent,
2327            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2328        clientSize = wx.ClientDisplayRect()
2329        Size = self.GetSize()
2330        xPos = clientSize[2]-Size[0]
2331        self.SetPosition(wx.Point(xPos,clientSize[1]))
2332        self._init_Imports()
2333        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2334        self.MakePDF = []
2335        self.Refine = []
2336        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2337        #self.ExportPattern = []
2338        self.ExportPeakList = []
2339        self.ExportHKL = []
2340        self.ExportPDF = []
2341        self.ExportPhase = []
2342        self.ExportCIF = []
2343        #
2344        self.GSASIIMenu = wx.MenuBar()
2345        # create a list of all dataframe menus (appended in PrefillDataMenu)
2346        self.dataMenuBars = [self.GSASIIMenu]
2347        self.MacroStatusList = []
2348        self.FillMainMenu(self.GSASIIMenu)
2349        self.SetMenuBar(self.GSASIIMenu)
2350        self.Bind(wx.EVT_SIZE, self.OnSize)
2351        self.Status = self.CreateStatusBar()
2352        self.mainPanel = wx.Panel(self,-1)
2353       
2354        wxID_PATTERNTREE = wx.NewId()
2355        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2356        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2357            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2358        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,self.OnDataTreeSelChanged)
2359        self.PatternTree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK,self.OnDataTreeSelChanged)
2360        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2361            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2362        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2363            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2364        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2365            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2366        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2367            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2368        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2369            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2370        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2371            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2372        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2373        self.root = self.PatternTree.root
2374        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2375            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2376        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame,G2frame=self)
2377        plotFrame.Show()
2378       
2379        self.dataDisplay = None
2380       
2381    def __init__(self, parent):
2382        self.ExportLookup = {}
2383        self.exporterlist = []
2384        self._init_ctrls(parent)
2385        self.Image = wx.Image(
2386            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2387            wx.BITMAP_TYPE_ICO)
2388        if "wxMSW" in wx.PlatformInfo:
2389            img = self.Image.Scale(16, 16).ConvertToBitmap()
2390        elif "wxGTK" in wx.PlatformInfo:
2391            img = self.Image.Scale(22, 22).ConvertToBitmap()
2392        else:
2393            img = self.Image.ConvertToBitmap()
2394        self.SetIcon(wx.IconFromBitmap(img))
2395        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2396        # various defaults
2397        self.oldFocus = None
2398        self.GSASprojectfile = ''
2399        self.undofile = ''
2400        self.TreeItemDelete = False
2401        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2402        self.Weight = False
2403        self.IfPlot = False
2404        self.DDShowAll = False
2405        self.atmSel = ''
2406        self.PatternId = 0
2407        self.PickId = 0
2408        self.PickIdText = None
2409        self.PeakTable = []
2410        self.LimitsTable = []
2411        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2412        self.HKL = []
2413        self.Lines = []
2414        self.itemPicked = None
2415        self.dataFrame = None
2416        self.Interpolate = 'nearest'
2417        self.ContourColor = 'Paired'
2418        self.VcovColor = 'RdYlGn'
2419        self.RamaColor = 'Blues'
2420        self.Projection = 'equal area'
2421        self.logPlot = False
2422        self.plusPlot = True
2423        self.sqPlot = False
2424        self.ErrorBars = False
2425        self.Contour = False
2426        self.Legend = False
2427        self.SinglePlot = True
2428        self.Waterfall = False
2429        self.selections= None
2430        self.PDFselections = None
2431        self.SubBack = False
2432        self.seqReverse = False
2433        self.seqLines = True #draw lines between points
2434        self.plotView = 0
2435        self.Image = 0
2436        self.oldImagefile = '' # the name of the last image file read
2437        self.oldImageTag = None # the name of the tag for multi-image files
2438        self.PauseIntegration = False
2439        self.ImageZ = []
2440        self.Integrate = 0
2441        self.imageDefault = {}
2442        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2443        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2444        self.autoIntFrame = None
2445        self.IntegratedList = [] # list of already integrated IMG tree items
2446        self.Sngl = False
2447        self.ifGetRing = False
2448        self.MaskKey = ''           #trigger for making image masks
2449        self.MskDelete = False      #trigger for mask delete
2450        self.StrainKey = ''         #ditto for new strain d-zeros
2451        self.EnablePlot = True
2452        self.hist = ''              # selected histogram in Phase/Data tab
2453        self.dirname = os.path.abspath(os.path.expanduser('~'))       #start in the users home directory by default; may be meaningless
2454        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2455        self.LastImportDir = None # last-used directory where an import was done
2456        self.LastGPXdir = None    # directory where a GPX file was last read
2457        self.LastExportDir = None  # the last directory used for exports, if any.
2458        self.dataDisplayPhaseText = ''
2459        self.lastTreeSetting = []
2460        self.ExpandingAll = False
2461               
2462        arg = sys.argv
2463        if len(arg) > 1 and arg[1]:
2464            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2465            self.dirname = os.path.abspath(os.path.dirname(arg[1]))
2466            if self.dirname: os.chdir(self.dirname)
2467            try:
2468                self.StartProject()         #open the file if possible
2469                return
2470            except Exception:
2471                print 'Error opening or reading file',arg[1]
2472                import traceback
2473                print traceback.format_exc()
2474               
2475        if GSASIIpath.GetConfigValue('Starting_directory'):
2476            try:
2477                pth = GSASIIpath.GetConfigValue('Starting_directory')
2478                pth = os.path.expanduser(pth) 
2479                os.chdir(pth)
2480                self.LastGPXdir = pth
2481            except:
2482                print('Ignoring Config Starting_directory value: '+
2483                      GSASIIpath.GetConfigValue('Starting_directory'))
2484
2485    def GetTreeItemsList(self,item):
2486        return self.PatternTree._getTreeItemsList(item)
2487
2488    def OnSize(self,event):
2489        'Called to make PatternTree fill mainPanel'
2490        w,h = self.GetClientSizeTuple()
2491        self.mainPanel.SetSize(wx.Size(w,h))
2492        self.PatternTree.SetSize(wx.Size(w,h))
2493                       
2494    def OnDataTreeSelChanged(self, event):
2495        '''Called when a data tree item is selected'''
2496        if self.TreeItemDelete:
2497            self.TreeItemDelete = False
2498        else:
2499            if self.ExpandingAll:
2500                #if GSASIIpath.GetConfigValue('debug'): print('Skipping Tree selection due to ExpandAll')
2501                return
2502            pltNum = self.G2plotNB.nb.GetSelection()
2503            if pltNum >= 0:                         #to avoid the startup with no plot!
2504                self.G2plotNB.nb.GetPage(pltNum)
2505            item = event.GetItem()
2506            wx.CallAfter(G2gd.SelectDataTreeItem,self,item)
2507            if self.oldFocus: # Why do this?
2508                self.oldFocus.SetFocus()
2509       
2510    def OnPatternTreeItemCollapsed(self, event):
2511        'Called when a tree item is collapsed - all children will be collapsed'
2512        self.PatternTree.CollapseAllChildren(event.GetItem())
2513
2514    def OnPatternTreeItemExpanded(self, event):
2515        'Called when a tree item is expanded'
2516        self.OnDataTreeSelChanged(event)
2517        event.Skip()
2518       
2519    def OnPatternTreeItemDelete(self, event):
2520        'Called when a tree item is deleted -- not sure what this does'
2521        self.TreeItemDelete = True
2522
2523    def OnPatternTreeItemActivated(self, event):
2524        'Called when a tree item is activated'
2525        event.Skip()
2526       
2527    def OnPatternTreeBeginRDrag(self,event):
2528        event.Allow()
2529        self.BeginDragId = event.GetItem()
2530        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2531        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2532        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2533        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2534        while item:     #G2 data tree has no sub children under a child of a tree item
2535            name = self.PatternTree.GetItemText(item)
2536            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2537            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2538       
2539    def OnPatternTreeEndDrag(self,event):
2540        event.Allow()
2541        self.EndDragId = event.GetItem()
2542        try:
2543            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2544        except:
2545            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2546            NewParent = self.root
2547        if self.ParentId != NewParent:
2548            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2549        else:
2550            Name,Item = self.DragData[0]
2551            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2552            self.PatternTree.SetItemPyData(NewId,Item)
2553            for name,item in self.DragData[1:]:     #loop over children
2554                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2555                self.PatternTree.SetItemPyData(Id,item)
2556            self.PatternTree.Delete(self.BeginDragId)
2557            G2gd.SelectDataTreeItem(self,NewId)
2558       
2559    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2560        'Allows stepping through the tree with the up/down arrow keys'
2561        self.oldFocus = wx.Window.FindFocus()
2562        keyevt = event.GetKeyEvent()
2563        key = event.GetKeyCode()
2564        item = self.PatternTree.GetSelection()
2565        if type(item) is int: return # is this the toplevel in tree?
2566        name = self.PatternTree.GetItemText(item)
2567        parent = self.PatternTree.GetItemParent(item)
2568        if key == wx.WXK_UP:
2569            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2570                if type(parent) is int: return # is this the toplevel in tree?
2571                prev = self.PatternTree.GetPrevSibling(parent)
2572                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2573                if NewId:
2574                    self.PatternTree.Collapse(parent)
2575                    self.PatternTree.Expand(prev)
2576                    self.oldFocus = wx.Window.FindFocus()
2577                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2578                else:
2579                    wx.CallAfter(self.PatternTree.SelectItem,item)
2580            else:   
2581                self.PatternTree.GetPrevSibling(item)
2582                self.PatternTree.SelectItem(item)
2583        elif key == wx.WXK_DOWN:
2584            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2585                next = self.PatternTree.GetNextSibling(parent)
2586                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2587                if NewId:
2588                    self.PatternTree.Collapse(parent)
2589                    self.PatternTree.Expand(next)
2590                    self.oldFocus = wx.Window.FindFocus()
2591                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2592                else:
2593                    wx.CallAfter(self.PatternTree.SelectItem,item)
2594            else:   
2595                self.PatternTree.GetNextSibling(item)
2596                self.PatternTree.SelectItem(item)
2597               
2598    def OnReadPowderPeaks(self,event):
2599        'Bound to menu Data/Read Powder Peaks'
2600        self.CheckNotebook()
2601        pth = G2G.GetImportPath(self)
2602        if not pth: pth = '.'
2603        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2604            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2605        try:
2606            if dlg.ShowModal() == wx.ID_OK:
2607                self.HKL = []
2608                self.powderfile = dlg.GetPath()
2609                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2610                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2611                data = ['PKS',wave,0.0]
2612                names = ['Type','Lam','Zero'] 
2613                codes = [0,0,0]
2614                inst = [G2IO.makeInstDict(names,data,codes),{}]
2615                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2616                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2617                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2618                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2619                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2620                self.PatternTree.Expand(Id)
2621                self.PatternTree.SelectItem(Id)
2622                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2623        finally:
2624            dlg.Destroy()
2625                       
2626    def OnImageRead(self,event):
2627        '''Called to read in an image in any known format. *** Depreciated. ***
2628        '''
2629        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2630
2631    def CheckNotebook(self):
2632        '''Make sure the data tree has the minimally expected controls.
2633        '''
2634        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2635            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2636            self.PatternTree.SetItemPyData(sub,[''])
2637        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2638            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2639            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2640        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2641            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2642            self.PatternTree.SetItemPyData(sub,{})
2643        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2644            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2645            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2646        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2647            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2648            self.PatternTree.SetItemPyData(sub,{})
2649        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2650            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2651            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2652                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2653               
2654    class CopyDialog(wx.Dialog):
2655        '''Creates a dialog for copying control settings between
2656        data tree items'''
2657        def __init__(self,parent,title,text,data):
2658            wx.Dialog.__init__(self,parent,-1,title, 
2659                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2660            self.data = data
2661            panel = wx.Panel(self)
2662            mainSizer = wx.BoxSizer(wx.VERTICAL)
2663            topLabl = wx.StaticText(panel,-1,text)
2664            mainSizer.Add((10,10),1)
2665            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2666            mainSizer.Add((10,10),1)
2667            ncols = len(data)/40+1
2668            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2669            for id,item in enumerate(self.data):
2670                ckbox = wx.CheckBox(panel,id,item[1])
2671                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2672                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2673            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2674            OkBtn = wx.Button(panel,-1,"Ok")
2675            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2676            cancelBtn = wx.Button(panel,-1,"Cancel")
2677            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2678            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2679            btnSizer.Add((20,20),1)
2680            btnSizer.Add(OkBtn)
2681            btnSizer.Add((20,20),1)
2682            btnSizer.Add(cancelBtn)
2683            btnSizer.Add((20,20),1)
2684           
2685            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2686            panel.SetSizer(mainSizer)
2687            panel.Fit()
2688            self.Fit()
2689       
2690        def OnCopyChange(self,event):
2691            id = event.GetId()
2692            self.data[id][0] = self.FindWindowById(id).GetValue()       
2693           
2694        def OnOk(self,event):
2695            parent = self.GetParent()
2696            parent.Raise()
2697            self.EndModal(wx.ID_OK)             
2698           
2699        def OnCancel(self,event):
2700            parent = self.GetParent()
2701            parent.Raise()
2702            self.EndModal(wx.ID_CANCEL)             
2703           
2704        def GetData(self):
2705            return self.data
2706       
2707    class SumDialog(wx.Dialog):
2708        '''Allows user to supply scale factor(s) when summing data -
2709        TODO: CAN WE PREVIEW RESULT HERE?'''
2710        def __init__(self,parent,title,text,dataType,data,dataList):
2711            wx.Dialog.__init__(self,parent,-1,title,size=(400,250),
2712                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
2713            self.plotFrame = wx.Frame(self,-1,'Sum Plots',size=wx.Size(700,600), \
2714                style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2715            self.G2plotNB = G2plt.G2PlotNoteBook(self.plotFrame,G2frame=self)
2716            self.data = data
2717            self.dataList = dataList
2718            self.dataType = dataType
2719            size = (450,350)
2720            panel = wxscroll.ScrolledPanel(self, wx.ID_ANY,size=size,
2721                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
2722            mainSizer = wx.BoxSizer(wx.VERTICAL)
2723            topLabl = wx.StaticText(panel,-1,text)
2724            mainSizer.Add((10,10),1)
2725            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2726            mainSizer.Add((10,10),1)
2727            self.dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2728            for id,item in enumerate(self.data[:-1]):
2729                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(300,20))
2730                name.SetEditable(False)
2731#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2732                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2733                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2734                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2735                self.dataGridSizer.Add(scale,0,wx.LEFT,10)
2736                self.dataGridSizer.Add(name,0,wx.RIGHT,10)
2737            if self.dataType:
2738                self.dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+self.dataType),0, \
2739                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2740                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(300,20),style=wx.TE_PROCESS_ENTER)
2741                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2742                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2743                self.dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2744                self.dataGridSizer.Add(wx.StaticText(panel,label='All scales value: '),0,  \
2745                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2746#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2747                allScale = wx.TextCtrl(panel,value='',style=wx.TE_PROCESS_ENTER)
2748                allScale.Bind(wx.EVT_TEXT_ENTER,self.OnAllScale)
2749                allScale.Bind(wx.EVT_KILL_FOCUS,self.OnAllScale)
2750                self.dataGridSizer.Add(allScale,0,WACV)
2751            mainSizer.Add(self.dataGridSizer,0,wx.EXPAND)
2752            OkBtn = wx.Button(panel,-1,"Ok")
2753            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2754            cancelBtn = wx.Button(panel,-1,"Cancel")
2755            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2756            btnSizer = wx.FlexGridSizer(0,3,10,20)
2757            if self.dataType =='PWDR':
2758                TestBtn = wx.Button(panel,-1,"Test")
2759                TestBtn.Bind(wx.EVT_BUTTON, self.OnTest)
2760                btnSizer.Add(TestBtn)
2761            btnSizer.Add(OkBtn)
2762            btnSizer.Add(cancelBtn)
2763           
2764            panel.SetSizer(mainSizer)
2765            panel.SetAutoLayout(1)
2766            panel.SetupScrolling()
2767            mainSizer.Add((10,10),1)
2768            mainSizer.Add(btnSizer,0,wx.CENTER)
2769            panel.SetSizer(mainSizer)
2770            panel.Fit()
2771            self.Fit()
2772
2773        def OnScaleChange(self,event):
2774            event.Skip()
2775            id = event.GetId()
2776            value = self.FindWindowById(id).GetValue()
2777            try:
2778                self.data[id][0] = float(value)
2779                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2780            except ValueError:
2781                if value and '-' not in value[0]:
2782                    print 'bad input - numbers only'
2783                    self.FindWindowById(id).SetValue('0.000')
2784                   
2785        def OnAllScale(self,event):
2786            event.Skip()
2787            id = event.GetId()
2788            try:
2789                scale = float(self.FindWindowById(id).GetValue())
2790                self.FindWindowById(id).SetValue('%.3f'%(scale))
2791                entries = self.dataGridSizer.GetChildren()
2792                for i,item in enumerate(self.data[:-1]):
2793                    item[0] = scale
2794                    entries[2*i].GetWindow().SetValue('%.3f'%(scale))
2795                 
2796            except ValueError:
2797                print 'bad input - numbers only'
2798                self.FindWindowById(id).SetValue('')
2799                   
2800           
2801        def OnNameChange(self,event):
2802            event.Skip()
2803            self.data[-1] = self.name.GetValue()
2804           
2805        def OnTest(self,event):
2806            lenX = 0
2807            Xminmax = [0,0]
2808            XY = []
2809            Xsum = []
2810            Ysum = []
2811            Vsum = []
2812            result = self.data
2813            for i,item in enumerate(result[:-1]):
2814                scale,name = item
2815                data = self.dataList[i]
2816                if scale:
2817                    x,y,w,yc,yb,yd = data   #numpy arrays!
2818                    XY.append([x,scale*y])
2819                    v = 1./w
2820                    if lenX:
2821                        if lenX != len(x):
2822                            self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2823                                '\nExpected:'+str(lenX)+ \
2824                                '\nFound:   '+str(len(x))+'\nfor '+name)
2825                            self.OnCancel(event)
2826                    else:
2827                        lenX = len(x)
2828                    if Xminmax[1]:
2829                        if Xminmax != [x[0],x[-1]]:
2830                            self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2831                                '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2832                                '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2833                            self.OnCancel(event)
2834                        else:
2835                            for j,yi in enumerate(y):
2836                                 Ysum[j] += scale*yi
2837                                 Vsum[j] += abs(scale)*v[j]
2838                    else:
2839                        Xminmax = [x[0],x[-1]]
2840                        Xsum = x
2841                        Ysum = scale*y
2842                        Vsum = abs(scale*v)
2843            Wsum = 1./np.array(Vsum)
2844            YCsum = np.zeros(lenX)
2845            YBsum = np.zeros(lenX)
2846            YDsum = np.zeros(lenX)
2847            XY.append([Xsum,Ysum])
2848            self.result = [Xsum,Ysum,Wsum,YCsum,YBsum,YDsum]
2849            # N.B. PlotXY expects the first arg to point to G2frame. In this case, we
2850            # create a duplicate (temporary) Plot notebook window that is a child of the
2851            # modal SumDialog dialog (self). This nicely gets deleted when the dialog is destroyed,
2852            # but the plot window is not fully functional, at least on the Mac.
2853            G2plt.PlotXY(self,XY,lines=True,Title='Sum:'+self.data[-1],labelY='Intensity',)
2854            self.plotFrame.Show()
2855                       
2856        def OnOk(self,event):
2857            if self.dataType == 'PWDR': self.OnTest(event)
2858            parent = self.GetParent()
2859            parent.Raise()
2860            self.EndModal(wx.ID_OK)             
2861           
2862        def OnCancel(self,event):
2863            parent = self.GetParent()
2864            parent.Raise()
2865            self.EndModal(wx.ID_CANCEL)             
2866           
2867        def GetData(self):
2868            if self.dataType == 'PWDR':
2869                return self.data,self.result
2870            else:
2871                return self.data
2872                       
2873    def OnPwdrSum(self,event):
2874        'Sum together powder data(?)'
2875        TextList = []
2876        DataList = []
2877        Names = []
2878        Inst = None
2879        Comments = ['Sum equals: \n']
2880        if self.PatternTree.GetCount():
2881            item, cookie = self.PatternTree.GetFirstChild(self.root)
2882            while item:
2883                name = self.PatternTree.GetItemText(item)
2884                Names.append(name)
2885                if 'PWDR' in name:
2886                    TextList.append([0.0,name])
2887                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2888                    if not Inst:
2889                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2890                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2891            if len(TextList) < 2:
2892                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2893                return
2894            TextList.append('default_sum_name')               
2895            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList,DataList)
2896            try:
2897                if dlg.ShowModal() == wx.ID_OK:
2898                    result,sumData = dlg.GetData()
2899                    Xsum,Ysum,Wsum,YCsum,YBsum,YDsum = sumData
2900                    Xminmax = [Xsum[0],Xsum[-1]]
2901                    outname = 'PWDR '+result[-1]
2902                    Id = 0
2903                    if outname in Names:
2904                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2905                        try:
2906                            if dlg2.ShowModal() == wx.ID_OK:
2907                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2908                                self.PatternTree.Delete(Id)
2909                        finally:
2910                            dlg2.Destroy()
2911                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2912                    if Id:
2913                        Sample = G2pdG.SetDefaultSample()
2914                        Ymin = np.min(Ysum)
2915                        Ymax = np.max(Ysum)
2916                        valuesdict = {
2917                            'wtFactor':1.0,
2918                            'Dummy':False,
2919                            'ranId':ran.randint(0,sys.maxint),
2920                            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
2921                            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
2922                            }
2923                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2924                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2925                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2926                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2927                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2928                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2929                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2930                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2931                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2932                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2933                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2934                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2935                        self.PatternTree.SelectItem(Id)
2936                        self.PatternTree.Expand(Id)
2937            finally:
2938                dlg.Destroy()
2939
2940    def OnImageSum(self,event):
2941        'Sum together image data'
2942        TextList = []
2943        DataList = []
2944        IdList = []
2945        Names = []
2946        Comments = ['Sum equals: \n']
2947        if self.PatternTree.GetCount():
2948            item, cookie = self.PatternTree.GetFirstChild(self.root)
2949            while item:
2950                name = self.PatternTree.GetItemText(item)
2951                Names.append(name)
2952                if 'IMG' in name:
2953                    TextList.append([0.0,name])
2954                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2955                    IdList.append(item)
2956                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2957                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2958            if len(TextList) < 2:
2959                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2960                return
2961            TextList.append('default_sum_name')               
2962            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList,DataList)
2963            try:
2964                if dlg.ShowModal() == wx.ID_OK:
2965                    imSize = 0
2966                    result = dlg.GetData()
2967                    First = True
2968                    Found = False
2969                    for i,item in enumerate(result[:-1]):
2970                        scale,name = item
2971                        if scale:
2972                            Found = True                               
2973                            Comments.append("%10.3f %s" % (scale,' * '+name))
2974                            Npix,imagefile,imagetag = DataList[i]
2975                            imagefile = G2IO.GetCheckImageFile(self,IdList[i])[1]
2976                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2977                            if First:
2978                                newImage = np.zeros_like(image)
2979                                First = False
2980                            if imSize:
2981                                if imSize != Npix:
2982                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2983                                        '\nExpected:'+str(imSize)+ \
2984                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2985                                    return
2986                                newImage = newImage+scale*image
2987                            else:
2988                                imSize = Npix
2989                                newImage = newImage+scale*image
2990                            del(image)
2991                    if not Found:
2992                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2993                        return
2994                       
2995                       
2996                    newImage = np.array(newImage,dtype=np.int32)                       
2997                    outname = 'IMG '+result[-1]
2998                    Id = 0
2999                    if outname in Names:
3000                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
3001                        try:
3002                            if dlg2.ShowModal() == wx.ID_OK:
3003                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
3004                        finally:
3005                            dlg2.Destroy()
3006                    else:
3007                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
3008                    if Id:
3009                        pth = G2G.GetExportPath(self)
3010                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth,outname.split('IMG ')[1], 
3011                            'G2img files (*.G2img)|*.G2img', 
3012                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
3013                        if dlg.ShowModal() == wx.ID_OK:
3014                            newimagefile = dlg.GetPath()
3015                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
3016                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
3017                            Imax = np.amax(newImage)
3018                            Imin = np.amin(newImage)
3019                            newImage = []
3020                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
3021                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
3022                        del(newImage)
3023                        if self.imageDefault:
3024                            Data = copy.copy(self.imageDefault)
3025                        Data['formatName'] = 'GSAS-II image'
3026                        Data['showLines'] = True
3027                        Data['ring'] = []
3028                        Data['rings'] = []
3029                        Data['cutoff'] = 10
3030                        Data['pixLimit'] = 20
3031                        Data['ellipses'] = []
3032                        Data['calibrant'] = ''
3033                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
3034                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
3035                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
3036                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
3037                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
3038                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
3039                        self.PatternTree.SelectItem(Id)
3040                        self.PatternTree.Expand(Id)
3041                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
3042                        self.Image = self.PickId
3043            finally:
3044                dlg.Destroy()
3045                     
3046    def OnAddPhase(self,event):
3047        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
3048        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3049            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
3050        else:
3051            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3052        PhaseName = ''
3053        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
3054            style=wx.OK)
3055        if dlg.ShowModal() == wx.ID_OK:
3056            PhaseName = dlg.GetValue()
3057        dlg.Destroy()
3058        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
3059        E,SGData = G2spc.SpcGroup('P 1')
3060        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
3061        G2gd.SelectDataTreeItem(self,sub) #bring up new phase General tab
3062       
3063    def OnDeletePhase(self,event):
3064        'Delete a phase from the tree. Called by Data/Delete Phase menu'
3065        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
3066        if self.dataFrame:
3067            self.dataFrame.Clear() 
3068        TextList = []
3069        DelList = []
3070        DelItemList = []
3071        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3072            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3073        else:
3074            return
3075        if sub:
3076            item, cookie = self.PatternTree.GetFirstChild(sub)
3077            while item:
3078                TextList.append(self.PatternTree.GetItemText(item))
3079                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
3080            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
3081            try:
3082                if dlg.ShowModal() == wx.ID_OK:
3083                    result = dlg.GetSelections()
3084                    for i in result: DelList.append([i,TextList[i]])
3085                    item, cookie = self.PatternTree.GetFirstChild(sub)
3086                    i = 0
3087                    while item:
3088                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
3089                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3090                        i += 1
3091                    for item in DelItemList:
3092                        name = self.PatternTree.GetItemText(item)
3093                        self.PatternTree.Delete(item)
3094                        self.G2plotNB.Delete(name)
3095                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3096                    while item:
3097                        name = self.PatternTree.GetItemText(item)
3098                        if 'PWDR' in name:
3099                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
3100                            refList = self.PatternTree.GetItemPyData(Id)
3101                            if len(refList):
3102                                for i,item in DelList:
3103                                    if item in refList:
3104                                        del(refList[item])
3105#                            self.PatternTree.SetItemPyData(Id,refList)
3106                        elif 'HKLF' in name:
3107                            data = self.PatternTree.GetItemPyData(item)
3108                            data[0] = {}
3109#                            self.PatternTree.SetItemPyData(item,data)
3110                           
3111                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3112            finally:
3113                dlg.Destroy()
3114               
3115    def OnRenameData(self,event):
3116        'Renames an existing phase. Called by Data/Rename Phase menu'
3117        name = self.PatternTree.GetItemText(self.PickId)     
3118        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
3119            if 'Bank' in name:
3120                names = name.split('Bank')
3121                names[1] = ' Bank'+names[1]
3122            elif 'Azm' in name:
3123                names = name.split('Azm')
3124                names[1] = ' Azm'+names[1]
3125            else:
3126                names = [name,'']
3127            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
3128            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
3129                defaultValue=names[0][names[0].index(' ')+1:])
3130            try:
3131                if dlg.ShowModal() == wx.ID_OK:
3132                    name = dataType+dlg.GetValue()+names[1]
3133                    self.PatternTree.SetItemText(self.PickId,name)
3134            finally:
3135                dlg.Destroy()
3136       
3137    def GetFileList(self,fileType,skip=None):        #potentially useful?
3138        'Appears unused. Note routine of same name in GSASIIpwdGUI'
3139        fileList = []
3140        Source = ''
3141        id, cookie = self.PatternTree.GetFirstChild(self.root)
3142        while id:
3143            name = self.PatternTree.GetItemText(id)
3144            if fileType in name:
3145                if id == skip:
3146                    Source = name
3147                else:
3148                    fileList.append([False,name,id])
3149            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3150        if skip:
3151            return fileList,Source
3152        else:
3153            return fileList
3154           
3155    def OnDataDelete(self, event):
3156        '''Delete one or more histograms from data tree. Called by the
3157        Data/DeleteData menu
3158        '''
3159        TextList = []
3160        DelList = []
3161        DelItemList = []
3162        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
3163        PDFnames = []
3164        if self.PatternTree.GetCount():
3165            item, cookie = self.PatternTree.GetFirstChild(self.root)
3166            while item:
3167                name = self.PatternTree.GetItemText(item)
3168                if name not in ['Notebook','Controls','Covariance','Constraints',
3169                    'Restraints','Phases','Rigid bodies'] and 'Sequential' not in name:
3170                    if 'PWDR' in name: nItems['PWDR'] += 1
3171                    if 'SASD' in name: nItems['SASD'] += 1
3172                    if 'IMG' in name:  nItems['IMG'] += 1
3173                    if 'HKLF' in name: nItems['HKLF'] += 1
3174                    if 'PDF' in name:
3175                        PDFnames.append(name)
3176                        nItems['PDF'] += 1
3177                    TextList.append(name)
3178                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3179            for pdfName in PDFnames:
3180                TextList.remove('PWDR'+pdfName[4:])
3181            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
3182            try:
3183                if dlg.ShowModal() == wx.ID_OK:
3184                    result = dlg.GetSelections()
3185                    for i in result: DelList.append(TextList[i])
3186                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3187                    while item:
3188                        itemName = self.PatternTree.GetItemText(item)
3189                        if itemName in DelList:
3190                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
3191                            elif 'SASD' in itemName: nItems['SASD'] -= 1
3192                            elif 'IMG' in itemName: nItems['IMG'] -= 1
3193                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
3194                            elif 'PDF' in itemName: nItems['PDF'] -= 1
3195                            DelItemList.append(item)
3196                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3197                    for item in DelItemList:
3198                        self.PatternTree.Delete(item)
3199                    self.PickId = 0
3200                    self.PickIdText = None
3201                    self.PatternId = 0
3202                    if nItems['PWDR']:
3203                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3204                    else:
3205                        self.G2plotNB.Delete('Powder Patterns')
3206                    if not nItems['IMG']:
3207                        self.G2plotNB.Delete('2D Powder Image')
3208                    if not nItems['HKLF']:
3209                        self.G2plotNB.Delete('Structure Factors')
3210                        if '3D Structure Factors' in self.G2plotNB.plotList:
3211                            self.G2plotNB.Delete('3D Structure Factors')
3212            finally:
3213                dlg.Destroy()
3214
3215    def OnFileOpen(self, event, filename=None):
3216        '''Gets a GSAS-II .gpx project file in response to the
3217        File/Open Project menu button
3218        '''
3219        result = wx.ID_OK
3220        self.EnablePlot = False
3221        if self.PatternTree.GetChildrenCount(self.root,False):
3222            if self.dataFrame:
3223                self.dataFrame.Clear() 
3224            dlg = wx.MessageDialog(
3225                self,
3226                'Do you want to overwrite the current project? '+
3227                'Any unsaved changes in current project will be lost. Press OK to continue.',
3228                'Overwrite?',  wx.OK | wx.CANCEL)
3229            try:
3230                result = dlg.ShowModal()
3231                if result == wx.ID_OK:
3232                    self.PatternTree.DeleteChildren(self.root)
3233                    self.GSASprojectfile = ''
3234                    self.HKL = []
3235                    if self.G2plotNB.plotList:
3236                        self.G2plotNB.clear()
3237            finally:
3238                dlg.Destroy()
3239        if result != wx.ID_OK: return
3240
3241        if not filename:
3242            if self.dataDisplay: self.dataDisplay.Destroy()
3243            if self.LastGPXdir:
3244                pth = self.LastGPXdir
3245            else:
3246                pth = '.'
3247            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3248                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3249            try:
3250                if dlg.ShowModal() != wx.ID_OK: return
3251                self.GSASprojectfile = dlg.GetPath()
3252                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3253                self.dirname = dlg.GetDirectory()
3254            finally:
3255                dlg.Destroy()
3256        else:
3257            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3258            self.dirname = os.path.split(filename)[0]
3259
3260        try:
3261            self.StartProject()         #open the file if possible
3262        except:
3263            print '\nError opening file ',filename
3264            import traceback
3265            print traceback.format_exc()
3266       
3267    def StartProject(self):
3268        '''Opens a GSAS-II project file & selects the 1st available data set to
3269        display (PWDR, HKLF or SASD)
3270        '''
3271       
3272        Id = 0
3273        phaseId = None
3274        G2IO.ProjFileOpen(self)
3275        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3276        self.PatternTree.Expand(self.root)
3277        self.HKL = []
3278        item, cookie = self.PatternTree.GetFirstChild(self.root)
3279        while item and not Id:
3280            name = self.PatternTree.GetItemText(item)
3281            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3282                Id = item
3283            elif name == "Phases":
3284                phaseId = item
3285            elif name == 'Controls':
3286                data = self.PatternTree.GetItemPyData(item)
3287                if data:
3288                    for item in self.Refine: item.Enable(True)
3289                    self.EnableSeqRefineMenu()
3290            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3291        if phaseId: # show all phases
3292            self.PatternTree.Expand(phaseId)
3293        if Id:
3294            self.EnablePlot = True
3295            self.PatternTree.SelectItem(Id)
3296            self.PatternTree.Expand(Id)
3297        elif phaseId:
3298            self.PatternTree.SelectItem(phaseId)
3299        self.CheckNotebook()
3300        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3301        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3302        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3303        self.LastGPXdir = pth
3304
3305    def OnFileClose(self, event):
3306        '''Clears the data tree in response to the
3307        File/New Project menu button. User is given option to save
3308        the project.
3309        '''
3310        if self.dataFrame:
3311            self.dataFrame.Clear()
3312            self.dataFrame.SetLabel('GSAS-II data display') 
3313        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3314        try:
3315            result = dlg.ShowModal()
3316            if result == wx.ID_OK:
3317                self.OnFileSaveMenu(event)
3318            if result != wx.ID_CANCEL:
3319                self.GSASprojectfile = ''
3320                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3321                self.PatternTree.DeleteChildren(self.root)
3322                if self.HKL: self.HKL = []
3323                if self.G2plotNB.plotList:
3324                    self.G2plotNB.clear()
3325        finally:
3326            dlg.Destroy()
3327
3328    def OnFileSave(self, event):
3329        '''Save the current project in response to the
3330        File/Save Project menu button
3331        '''
3332       
3333        if self.GSASprojectfile: 
3334            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3335            self.CheckNotebook()
3336            G2IO.ProjFileSave(self)
3337        else:
3338            self.OnFileSaveas(event)
3339
3340    def OnFileSaveas(self, event):
3341        '''Save the current project in response to the
3342        File/Save as menu button
3343        '''
3344        if GSASIIpath.GetConfigValue('Starting_directory'):
3345            pth = GSASIIpath.GetConfigValue('Starting_directory')
3346            pth = os.path.expanduser(pth) 
3347        elif self.LastGPXdir:
3348            pth = self.LastGPXdir
3349        else:
3350            pth = '.'
3351        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3352            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3353        try:
3354            if dlg.ShowModal() == wx.ID_OK:
3355                self.GSASprojectfile = dlg.GetPath()
3356                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3357                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3358                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3359                self.CheckNotebook()
3360                G2IO.ProjFileSave(self)
3361                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3362        finally:
3363            dlg.Destroy()
3364           
3365    def ExpandAll(self,event):
3366        '''Expand all tree items or those of a single type
3367        '''
3368        txt = self.GetMenuBar().GetLabel(event.Id)
3369        if txt == 'all':
3370            self.ExpandingAll = True
3371            try:
3372                self.PatternTree.ExpandAll()
3373            finally:
3374                self.ExpandingAll = False
3375        else:
3376            self.ExpandingAll = True
3377            try:
3378                item, cookie = self.PatternTree.GetFirstChild(self.root)
3379                while item:
3380                    name = self.PatternTree.GetItemText(item)
3381                    if name.startswith(txt+' '): self.PatternTree.Expand(item)
3382                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3383            finally:
3384                self.ExpandingAll = False
3385
3386    def MoveTreeItems(self,event):
3387        '''Move tree items of a single type to the end of the tree
3388        '''
3389        txt = self.GetMenuBar().GetLabel(event.Id)
3390        # make a list of items to copy
3391        copyList = []
3392        item, cookie = self.PatternTree.GetFirstChild(self.root)
3393        while item:
3394            if self.PatternTree.GetItemText(item).startswith(txt+' '):
3395                copyList.append(item)
3396            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3397       
3398        self.ExpandingAll = True
3399        try:
3400            for item in copyList:
3401                name = self.PatternTree.GetItemText(item)
3402                newId = self.PatternTree.AppendItem(self.root,name)
3403                self.PatternTree.SetItemPyData(newId,self.PatternTree.GetItemPyData(item))
3404                chld, chldcookie = self.PatternTree.GetFirstChild(item)
3405                while chld:
3406                    chname = self.PatternTree.GetItemText(chld)
3407                    newCh = self.PatternTree.AppendItem(newId,chname)
3408                    self.PatternTree.SetItemPyData(newCh,self.PatternTree.GetItemPyData(chld))
3409                    chld, chldcookie = self.PatternTree.GetNextChild(item, chldcookie)
3410                self.PatternTree.Delete(item)
3411        finally:
3412            self.ExpandingAll = False
3413        G2gd.SelectDataTreeItem(self,self.root)
3414           
3415    def ExitMain(self, event):
3416        '''Called if the main window is closed'''
3417        if self.G2plotNB:
3418            self.G2plotNB.Destroy()
3419        if self.dataFrame:
3420            self.dataFrame.Clear() 
3421            self.dataFrame.Destroy()
3422        if self.undofile:
3423            os.remove(self.undofile)
3424        sys.exit()
3425       
3426    def OnFileExit(self, event):
3427        '''Called in response to the File/Quit menu button'''
3428        if self.G2plotNB:
3429            self.G2plotNB.Destroy()
3430        if self.dataFrame:
3431            self.dataFrame.Clear() 
3432            self.dataFrame.Destroy()
3433        self.Close()
3434       
3435    def OnExportPeakList(self,event):
3436        nptand = lambda x: np.tan(x*math.pi/180.)
3437        pth = G2G.GetExportPath(self)
3438        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3439            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3440        try:
3441            if dlg.ShowModal() == wx.ID_OK:
3442                self.peaklistfile = dlg.GetPath()
3443                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3444                file = open(self.peaklistfile,'w')               
3445                item, cookie = self.PatternTree.GetFirstChild(self.root)
3446                while item:
3447                    name = self.PatternTree.GetItemText(item)
3448                    if 'PWDR' in name:
3449                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3450                        wave = 0.0
3451                        while item2:
3452                            name2 = self.PatternTree.GetItemText(item2)
3453                            if name2 == 'Instrument Parameters':
3454                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3455                                Type = Inst['Type'][0]
3456                                if 'T' not in Type:
3457                                    wave = G2mth.getWave(Inst)
3458                            elif name2 == 'Peak List':
3459                                pkdata = self.PatternTree.GetItemPyData(item2)
3460                                peaks = pkdata['peaks']
3461                                sigDict = pkdata['sigDict']
3462                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3463                        file.write("#%s \n" % (name+' Peak List'))
3464                        if wave:
3465                            file.write('#wavelength = %10.6f\n'%(wave))
3466                        if 'T' in Type:
3467                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3468                        else:
3469                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3470                        for ip,peak in enumerate(peaks):
3471                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3472                            if 'T' in Type:  #TOF - more cols
3473                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3474                                for name in esds.keys():
3475                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3476                                sig = np.sqrt(peak[8])
3477                                gam = peak[10]
3478                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3479                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3480                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3481                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3482                            else:               #CW
3483                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3484                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3485                                for name in esds.keys():
3486                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3487                                sig = np.sqrt(peak[4]) #var -> sig
3488                                gam = peak[6]
3489                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3490                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3491                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3492                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3493                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3494                file.close()
3495        finally:
3496            dlg.Destroy()
3497       
3498    def OnExportHKL(self,event):
3499        pth = G2G.GetExportPath(self)
3500        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3501            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3502        try:
3503            if dlg.ShowModal() == wx.ID_OK:
3504                self.peaklistfile = dlg.GetPath()
3505                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3506                file = open(self.peaklistfile,'w')               
3507                item, cookie = self.PatternTree.GetFirstChild(self.root)
3508                while item:
3509                    name = self.PatternTree.GetItemText(item)
3510                    if 'PWDR' in name:
3511                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3512                        while item2:
3513                            name2 = self.PatternTree.GetItemText(item2)
3514                            if name2 == 'Reflection Lists':
3515                                data = self.PatternTree.GetItemPyData(item2)
3516                                phases = data.keys()
3517                                for phase in phases:
3518                                    peaks = data[phase]
3519                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3520                                    if 'T' in peaks.get('Type','PXC'):
3521                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3522                                    else:               
3523                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3524                                    for peak in peaks['RefList']:
3525                                        if 'T' in peaks.get('Type','PXC'):
3526                                            sig = np.sqrt(peak[6])
3527                                            gam = peak[7]
3528                                            FWHM = G2pwd.getgamFW(gam,sig)
3529                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3530                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3531                                        else:
3532                                            sig = np.sqrt(peak[6])
3533                                            gam = peak[7]
3534                                            FWHM = G2pwd.getgamFW(gam,sig)
3535                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3536                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3537                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3538                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3539                file.close()
3540        finally:
3541            dlg.Destroy()
3542       
3543    def OnExportPDF(self,event):
3544        #need S(Q) and G(R) to be saved here - probably best from selection?
3545        names = G2pdG.GetFileList(self,'PDF')
3546        exports = []
3547        if names:
3548            od = {'label_1':'Export I(Q)','value_1':False,'label_2':'Export S(Q)','value_2':False,
3549                  'label_3':'Export F(Q)','value_3':False,'label_4':'Export G(R)','value_4':True,
3550                  'label_5':'Make G(R) for pdfGUI','value_5':False}
3551            dlg = G2G.G2MultiChoiceDialog(self,'Select','PDF patterns to export',names,extraOpts=od)
3552            if dlg.ShowModal() == wx.ID_OK:
3553                sel = dlg.GetSelections()
3554                for x in sel:
3555                    exports.append(names[x])
3556            dlg.Destroy()
3557        if exports:
3558            PDFsaves = [od['value_1'],od['value_2'],od['value_3'],od['value_4'],od['value_5']]
3559            G2IO.PDFSave(self,exports,PDFsaves)
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 OnShowLSParms(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.