source: trunk/GSASII.py @ 2659

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

make lab data (2 x-ray wavelengths) instrument default 'Bragg-Brentano', all others 'Debye-Scherrer'
refactor PDF stuff to show PDF Controls & (new) PDF Peaks on G2 tree (removing I(Q)...).
Old gpx files with I(Q)... updated automatically to new scheme
Add new tree item for PDF Peaks - does nothing yet.
Fix FWHM calc for TOF so bins/FWHM on peak fitting make sense.

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