source: trunk/GSASII.py @ 2658

Last change on this file since 2658 was 2658, checked in by toby, 6 years ago

fix problem reporting histogram with Unicode chars

  • 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.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2017-01-23 19:34:00 +0000 (Mon, 23 Jan 2017) $
6# $Author: toby $
7# $Revision: 2658 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2658 2017-01-23 19:34:00Z toby $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import glob
24import imp
25import inspect
26import numpy as np
27import scipy as sp
28import wx
29import wx.lib.scrolledpanel as wxscroll
30try:  # patch for LANG environment var problem on occasional OSX machines
31    import locale
32    locale.getdefaultlocale()
33except ValueError:
34    print('Fixing location (see https://github.com/matplotlib/matplotlib/issues/5420.)')
35    os.environ['LC_ALL'] = 'en_US.UTF-8'
36    locale.getdefaultlocale()
37import matplotlib as mpl
38try:
39    import OpenGL as ogl
40except ImportError:
41    print('*******************************************************')
42    print('PyOpenGL is missing from your python installation')
43    print('     - we will try to install it')
44    print('*******************************************************')
45    def install_with_easyinstall(package):
46        try: 
47            print "trying a system-wide PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],package])
49            return
50        except:
51            pass
52        try: 
53            print "trying a user level PyOpenGl install"
54            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
55            return
56        except:
57            print "Install of '+package+' failed. Please report this information:"
58            import traceback
59            print traceback.format_exc()
60            sys.exit()
61    from setuptools.command import easy_install
62    install_with_easyinstall('PyOpenGl')
63    print('*******************************************************')         
64    print('OpenGL has been installed. Restarting GSAS-II')
65    print('*******************************************************')         
66    loc = os.path.dirname(__file__)
67    import subprocess
68    subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
69    sys.exit()
70   
71# load the GSAS routines
72import GSASIIpath
73GSASIIpath.SetVersionNumber("$Revision: 2658 $")
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                else:
1205                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
1206                    codes = [0,0,0,0,0,0]
1207                data.extend(v)
1208                if 'INS  1PRCF  ' in Iparm:
1209                    v1 = Iparm['INS  1PRCF  '].split()                                                 
1210                    v = Iparm['INS  1PRCF 1'].split()
1211                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1212                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1213                    v = Iparm['INS  1PRCF 2'].split()
1214                    if v1[0] == 3:
1215                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1216                    else:
1217                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file                   
1218                else:
1219                    v1 = Iparm['INS  1PRCF1 '].split()                                                 
1220                    v = Iparm['INS  1PRCF11'].split()
1221                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1222                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1223                    v = Iparm['INS  1PRCF12'].split()
1224                    if v1[0] == 3:
1225                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1226                    else:
1227                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
1228                codes.extend([0,0,0,0,0,0,0])
1229                Iparm1 = G2IO.makeInstDict(names,data,codes)
1230                Iparm1['Source'] = [Irads[irad],Irads[irad]]
1231                Iparm1['Bank'] = [Bank,Bank,0]
1232                return [Iparm1,{}]
1233            elif 'T' in DataType:
1234                names = ['Type','fltPath','2-theta','difC','difA', 'difB','Zero','alpha','beta-0','beta-1',
1235                    'beta-q','sig-0','sig-1','sig-2','sig-q', 'X','Y','Azimuth',]
1236                codes = [0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,]
1237                azm = 0.
1238                if 'INS  1DETAZM' in Iparm:
1239                    azm = float(Iparm['INS  1DETAZM'])
1240                s = Iparm['INS   FPATH1'].split()
1241                fltPath0 = G2IO.sfloat(s[0])
1242                if 'INS  1BNKPAR' not in Iparm:     #bank missing from Iparm file
1243                    return []
1244                s = Iparm['INS  1BNKPAR'].split()
1245                fltPath1 = G2IO.sfloat(s[0])
1246                data.extend([fltPath0+fltPath1,])               #Flight path source-sample-detector
1247                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
1248                s = Iparm['INS  1 ICONS'].split()
1249                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,G2IO.sfloat(s[2])])    #difC,difA,difB,Zero
1250                if 'INS  1PRCF  ' in Iparm:
1251                    s = Iparm['INS  1PRCF  '].split()
1252                    pfType = int(s[0])
1253                    s = Iparm['INS  1PRCF 1'].split()
1254                    if abs(pfType) == 1:
1255                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1256                        s = Iparm['INS  1PRCF 2'].split()
1257                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1258                    elif abs(pfType) in [3,4,5]:
1259                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1260                        if abs(pfType) == 4:
1261                            data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1262                        else:
1263                            s = Iparm['INS  1PRCF 2'].split()
1264                            data.extend([0.0,0.0,G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                       
1265                    elif abs(pfType) == 2:
1266                        data.extend([G2IO.sfloat(s[1]),0.0,1./G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1267                        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                           
1268                else:
1269                    s = Iparm['INS  1PRCF1 '].split()
1270                    pfType = int(s[0])
1271                    s = Iparm['INS  1PRCF11'].split()
1272                    if abs(pfType) == 1:
1273                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1274                        s = Iparm['INS  1PRCF12'].split()
1275                        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
1276                    elif abs(pfType) in [3,4,5]:
1277                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1278                        if abs(pfType) == 4:
1279                            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
1280                        else:
1281                            s = Iparm['INS  1PRCF12'].split()
1282                            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                       
1283                Inst1 = G2IO.makeInstDict(names,data,codes)
1284                Inst1['Bank'] = [Bank,Bank,0]
1285                Inst2 = {}
1286                if pfType < 0:
1287                    Ipab = 'INS  1PAB'+str(-pfType)
1288                    Npab = int(Iparm[Ipab+'  '].strip())
1289                    Inst2['Pdabc'] = []
1290                    for i in range(Npab):
1291                        k = Ipab+str(i+1).rjust(2)
1292                        s = Iparm[k].split()
1293                        Inst2['Pdabc'].append([float(t) for t in s])
1294                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
1295                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
1296                if 'INS  1I ITYP' in Iparm:
1297                    s = Iparm['INS  1I ITYP'].split()
1298                    Ityp = int(s[0])
1299                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
1300                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
1301                    if Ityp in [1,2,4]:
1302                        Inst2['Itype'] = Itypes[Ityp-1]
1303                        Inst2['Tminmax'] = Tminmax
1304                        Icoeff = []
1305                        Iesd = []
1306                        Icovar = []                   
1307                        for i in range(3):
1308                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
1309                            Icoeff += [float(S) for S in s]
1310                            s = Iparm['INS  1IECOF'+str(i+1)].split()
1311                            Iesd += [float(S) for S in s]
1312                        NT = 10
1313                        for i in range(8):
1314                            s = Iparm['INS  1IECOR'+str(i+1)]
1315                            if i == 7:
1316                                NT = 8
1317                            Icovar += [float(s[6*j:6*j+6]) for j in range(NT)]
1318                        Inst2['Icoeff'] = Icoeff
1319                        Inst2['Iesd'] = Iesd
1320                        Inst2['Icovar'] = Icovar
1321                return [Inst1,Inst2]
1322               
1323        def GetDefaultParms(self,rd):
1324            '''Solicits from user a default set of parameters & returns Inst parm dict
1325            param: self: refers to the GSASII main class
1326            param: rd: importer data structure
1327            returns: dict: Instrument parameter dictionary
1328            '''       
1329            sind = lambda x: math.sin(x*math.pi/180.)
1330            tand = lambda x: math.tan(x*math.pi/180.)
1331            import defaultIparms as dI
1332            while True: # loop until we get a choice
1333                choices = []
1334                head = 'Select from default instrument parameters for '+rd.idstring
1335   
1336                for l in dI.defaultIparm_lbl:
1337                    choices.append('Defaults for '+l)
1338                res = rd.BlockSelector(choices,ParentFrame=self,title=head,
1339                    header='Select default inst parms',useCancel=True)
1340                if res is None: return None
1341                rd.instfile = ''
1342                if 'Generic' in choices[res]:
1343                    dlg = G2G.MultiFloatDialog(self,title='Generic TOF detector bank',
1344                        prompts=['Total FP','2-theta',],values=[25.0,150.,],
1345                            limits=[[6.,200.],[5.,175.],],formats=['%6.2f','%6.1f',])
1346                    if dlg.ShowModal() == wx.ID_OK: #strictly empirical approx.
1347                        FP,tth = dlg.GetValues()
1348                        difC = 505.632*FP*sind(tth/2.)
1349                        sig1 = 50.+2.5e-6*(difC/tand(tth/2.))**2
1350                        bet1 = .00226+7.76e+11/difC**4
1351                        rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1352                        Inst = self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)
1353                        Inst[0]['difC'] = [difC,difC,0]
1354                        Inst[0]['sig-1'] = [sig1,sig1,0]
1355                        Inst[0]['beta-1'] = [bet1,bet1,0]
1356                        return Inst    #this is [Inst1,Inst2] a pair of dicts
1357                    dlg.Destroy()
1358                else:
1359                    rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1360                    return self.ReadPowderInstprm(dI.defaultIparms[res],bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1361
1362        # stuff we might need from the reader
1363        filename = rd.powderentry[0]
1364        bank = rd.powderentry[2]
1365        numbanks = rd.numbanks
1366        #1st priority: is there an instrument parameter file matching the current file
1367        # with extension .instprm, .prm, .inst, or .ins? If so read it
1368        basename = os.path.splitext(filename)[0]
1369        for ext in '.prm','.inst','.ins','.instprm':
1370            if self.zipfile:
1371                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1372                    selection=os.path.split(basename + ext)[1],parent=self)
1373                if instfile == None:
1374                    continue
1375            else:
1376                instfile = basename + ext
1377            if not os.path.exists(instfile):
1378                continue
1379            if 'instprm' in instfile:
1380                Lines = self.OpenPowderInstprm(instfile)
1381                instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1382                if 'list' in str(type(instParmList)):
1383                    rd.instfile = instfile
1384                    rd.instmsg = 'GSAS-II file '+instfile
1385                    return instParmList
1386                else:
1387                    #print 'debug: open/read failed',instfile
1388                    pass # fail silently
1389            else:
1390                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1391                if Iparm:
1392                    #print 'debug: success'
1393                    rd.instfile = instfile
1394                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1395                    return SetPowderInstParms(Iparm,rd)
1396                else:
1397                    #print 'debug: open/read failed',instfile
1398                    pass # fail silently
1399
1400        #2nd priority: is there an instrument parameter file defined for the current data set?
1401        # or if this is a read on a set of set of files, use the last one again
1402        #if rd.instparm as found in data file header or (lastdatafile == filename and lastIparmfile):
1403        if rd.instparm or lastIparmfile:
1404            if rd.instparm:
1405                instfile = os.path.join(os.path.split(filename)[0],rd.instparm)
1406            else:
1407                # for multiple reads of one data file, reuse the inst parm file
1408                instfile = lastIparmfile
1409#            if self.zipfile:
1410#                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1411#                    selection=os.path.split(instfile)[1],parent=self)
1412            if instfile != None and os.path.exists(instfile):
1413                #print 'debug: try read',instfile
1414                if 'instprm' in instfile:   #GSAS-II file must have .instprm as extension
1415                    Lines = self.OpenPowderInstprm(instfile)
1416                    if Lines is not None:
1417                        instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)   #this is [Inst1,Inst2] a pair of dicts
1418                else:   #old GSAS style iparm file - could be named anything!
1419                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1420                    if Iparm:
1421                        #print 'debug: success'
1422                        rd.instfile = instfile
1423                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1424                        instParmList = SetPowderInstParms(Iparm,rd)     #this is [Inst1,Inst2] a pair of dicts
1425                if 'list' in str(type(instParmList)):   #record stuff & return stuff
1426                    rd.instfile = instfile
1427                    rd.instmsg = 'GSAS-II file '+instfile
1428                    return instParmList
1429                else:   #bad iparms - try default
1430                    rd.instmsg = instParmList   #an error message
1431                    return GetDefaultParms(self,rd)
1432            else:
1433                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1434                    +str(instfile)+' requested by file '+ filename)
1435        #Finally - ask user for Instrument parametrs file - seems it can't be in a zip file
1436        while True: # loop until we get a file that works or we get a cancel
1437            instfile = ''
1438            pth = G2G.GetImportPath(self)
1439            if not pth: pth = '.'
1440            dlg = wx.FileDialog(self,
1441                'Choose inst. param file for "'+rd.idstring+'" (or Cancel for default)',
1442                pth, '',
1443                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1444                'GSAS-II iparm file (*.instprm)|*.instprm|'
1445                'All files (*.*)|*.*', wx.OPEN)
1446            if os.path.exists(lastIparmfile):
1447                dlg.SetFilename(lastIparmfile)
1448            if dlg.ShowModal() == wx.ID_OK:
1449                instfile = dlg.GetPath()
1450            dlg.Destroy()
1451            if not instfile: 
1452                return GetDefaultParms(self,rd) #on Cancel/break
1453            if 'instprm' in instfile:
1454                Lines = self.OpenPowderInstprm(instfile)
1455                if Lines is not None:
1456                    instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1457                if 'list' in str(type(instParmList)):
1458                    rd.instfile = instfile
1459                    rd.instmsg = 'GSAS-II file '+instfile
1460                    return instParmList
1461                else:
1462                    rd.instmsg = instParmList   #an error message
1463                    return GetDefaultParms(self,rd)
1464            else:
1465                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1466                if Iparm:
1467                    #print 'debug: success with',instfile
1468                    rd.instfile = instfile
1469                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1470                    return SetPowderInstParms(Iparm,rd)
1471                else:
1472                    self.ErrorDialog('Read Error',
1473                                     'Error opening/reading file '+str(instfile))
1474    def EnableRefineCommand(self):
1475        haveData = False
1476        # check for phases connected to histograms
1477        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1478        if not sub: return
1479        item, cookie = self.PatternTree.GetFirstChild(sub)
1480        while item: # loop over phases
1481            data = self.PatternTree.GetItemPyData(item)
1482            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1483            UseList = data['Histograms']
1484            if UseList: haveData = True
1485        if haveData:
1486            self.dataFrame.DataMenu.Enable(G2gd.wxID_DATADELETE,True)
1487            for item in self.Refine: item.Enable(True)
1488        else:
1489            self.dataFrame.DataMenu.Enable(G2gd.wxID_DATADELETE,False)
1490            for item in self.Refine: item.Enable(False)
1491
1492       
1493    def OnImportPowder(self,event):
1494        '''Called in response to an Import/Powder Data/... menu item
1495        to read a powder diffraction data set.
1496        dict self.ImportMenuId is used to look up the specific
1497        reader item associated with the menu item, which will be
1498        None for the last menu item, which is the "guess" option
1499        where all appropriate formats will be tried.
1500
1501        Also reads an instrument parameter file for each dataset.
1502        '''
1503        # get a list of existing histograms
1504        PWDRlist = []
1505        if self.PatternTree.GetCount():
1506            item, cookie = self.PatternTree.GetFirstChild(self.root)
1507            while item:
1508                name = self.PatternTree.GetItemText(item)
1509                if name.startswith('PWDR ') and name not in PWDRlist:
1510                    PWDRlist.append(name)
1511                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1512        # look up which format was requested
1513        reqrdr = self.ImportMenuId.get(event.GetId()) 
1514        rdlist = self.OnImportGeneric(
1515            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1516        if len(rdlist) == 0: return
1517        self.CheckNotebook()
1518        Iparm = None
1519        lastIparmfile = ''
1520        lastdatafile = ''
1521        newHistList = []
1522        lastVals = []
1523        self.EnablePlot = False
1524        for rd in rdlist:
1525            if 'Instrument Parameters' in rd.pwdparms:
1526                Iparm1,Iparm2 = rd.pwdparms['Instrument Parameters']
1527            else:
1528                # get instrument parameters for each dataset, unless already set
1529                if lastIparmfile:  # is this histogram like previous?
1530                    if lastVals != (rd.powderdata[0].min(),rd.powderdata[0].max(),len(rd.powderdata[0])):
1531                        lastIparmfile = ''
1532                Iparms = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1533                if not Iparms:  #may have bailed out
1534                    Id = 0
1535                    continue
1536                Iparm1,Iparm2 = Iparms
1537                if rd.repeat_instparm: 
1538                    lastIparmfile = rd.instfile
1539                    lastVals = (rd.powderdata[0].min(),rd.powderdata[0].max(),len(rd.powderdata[0]))
1540                # override any keys in read instrument parameters with ones set in import
1541                for key in Iparm1: 
1542                    if key in rd.instdict:
1543                        Iparm1[key] = rd.instdict[key]
1544            lastdatafile = rd.powderentry[0]
1545            HistName = rd.idstring
1546            HistName = 'PWDR '+HistName
1547            # make new histogram names unique
1548            if HistName in PWDRlist:
1549                dlg = wx.MessageDialog(self,'Skip %s?'%(HistName),'Duplicate data name',wx.YES_NO)
1550                try:
1551                    if dlg.ShowModal() == wx.ID_YES:
1552                        Id = 0
1553                        continue
1554                finally:
1555                    dlg.Destroy()
1556            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1557            print('Read powder data '+HistName+ 
1558                ' from file '+rd.readfilename +
1559                ' (format: '+ rd.formatName + 
1560                '). Inst parameters from '+rd.instmsg)
1561            # data are read, now store them in the tree
1562            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1563            if 'T' in Iparm1['Type'][0]:
1564                if not rd.clockWd and rd.GSAS:
1565                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1566                cw = np.diff(rd.powderdata[0])
1567                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1568                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
1569                    npts = min(len(rd.powderdata[0]),len(rd.powderdata[1]),len(cw))
1570                    rd.powderdata[1] = rd.powderdata[1][:npts]/cw[:npts]
1571                    rd.powderdata[2] = rd.powderdata[2][:npts]*cw[:npts]**2  #1/var=w at this point
1572                else:       #NB: from topas/fullprof type files
1573                    rd.powderdata[1] = rd.powderdata[1][:-1]
1574                    rd.powderdata[2] = rd.powderdata[2][:-1]
1575                if 'Itype' in Iparm2:
1576                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1577                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1578                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1579                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1580                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1581                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1582                    var += WYI*rd.powderdata[1]**2
1583                    var /= YI**2
1584                    rd.powderdata[2] = 1./var
1585                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])
1586                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])
1587                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])
1588            Ymax = np.max(rd.powderdata[1])                 
1589            valuesdict = {
1590                'wtFactor':1.0,
1591                'Dummy':False,
1592                'ranId':ran.randint(0,sys.maxint),
1593                'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
1594                'qPlot':False,'dPlot':False,'sqrtPlot':False
1595                }
1596            # apply user-supplied corrections to powder data
1597            if 'CorrectionCode' in Iparm1:
1598                print('Applying corrections from instprm file')
1599                corr = Iparm1['CorrectionCode'][0]
1600                try:
1601                    exec(corr)
1602                    print('done')
1603                except Exception as err:
1604                    print('error: '+str(err))
1605                    print('with commands -------------------')
1606                    print(str(corr))
1607                    print('---------------------------------')
1608                finally:
1609                    del Iparm1['CorrectionCode']
1610            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1611            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1612            self.PatternTree.SetItemPyData(
1613                self.PatternTree.AppendItem(Id,text='Comments'),
1614                rd.comments)
1615            Tmin = min(rd.powderdata[0])
1616            Tmax = max(rd.powderdata[0])
1617            self.PatternTree.SetItemPyData(
1618                self.PatternTree.AppendItem(Id,text='Limits'),
1619                rd.pwdparms.get('Limits',[(Tmin,Tmax),[Tmin,Tmax]])
1620                )
1621            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1622            self.PatternTree.SetItemPyData(
1623                self.PatternTree.AppendItem(Id,text='Background'),
1624                rd.pwdparms.get('Background',
1625                    [['chebyschev',True,3,1.0,0.0,0.0],{'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1626                    )
1627            self.PatternTree.SetItemPyData(
1628                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1629                [Iparm1,Iparm2])
1630            self.PatternTree.SetItemPyData(
1631                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1632                rd.Sample)
1633            self.PatternTree.SetItemPyData(
1634                self.PatternTree.AppendItem(Id,text='Peak List')
1635                ,{'peaks':[],'sigDict':{}})
1636            self.PatternTree.SetItemPyData(
1637                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1638                [[],[]])
1639            self.PatternTree.SetItemPyData(
1640                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1641                [])
1642            self.PatternTree.SetItemPyData(
1643                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1644                {})
1645            # if any Control values have been set, move them into tree
1646            Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
1647            Controls.update(rd.Controls)
1648            newHistList.append(HistName)
1649            rd.repeat_instparm = False  #clear the iparm reuse flag
1650        else:
1651            self.EnablePlot = True
1652            if Id:
1653                self.PatternTree.Expand(Id)
1654                self.PatternTree.SelectItem(Id)
1655
1656        if not newHistList: return # somehow, no new histograms
1657        # make a list of phase names
1658        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1659        phaseNameList = usedHistograms.keys() # phase names in use
1660        if not phaseNameList: return # no phases yet, nothing to do
1661        header = 'Select phase(s) to link\nto the newly-read data:'
1662        for Name in newHistList:
1663            header += '\n  '+str(Name)
1664
1665        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1666        if not result: return
1667        # connect new phases to histograms
1668        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1669        if not sub:
1670            raise Exception('ERROR -- why are there no phases here?')
1671        item, cookie = self.PatternTree.GetFirstChild(sub)
1672        iph = -1
1673        while item: # loop over (new) phases
1674            iph += 1
1675            data = self.PatternTree.GetItemPyData(item)
1676            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1677            if iph not in result: continue
1678            generalData = data['General']
1679            SGData = generalData['SGData']
1680            UseList = data['Histograms']
1681            NShkl = len(G2spc.MustrainNames(SGData))
1682            NDij = len(G2spc.HStrainNames(SGData))
1683            for histoName in newHistList:
1684                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1685                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1686                refList = self.PatternTree.GetItemPyData(
1687                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1688                refList[generalData['Name']] = []
1689        self.EnableRefineCommand()
1690        return # success
1691
1692    def OnDummyPowder(self,event):
1693        '''Called in response to Import/Powder Data/Simulate menu item
1694        to create a Dummy powder diffraction data set.
1695
1696        Reads an instrument parameter file and then gets input from the user
1697        '''
1698        # get a list of existing histograms
1699        PWDRlist = []
1700        if self.PatternTree.GetCount():
1701            item, cookie = self.PatternTree.GetFirstChild(self.root)
1702            while item:
1703                name = self.PatternTree.GetItemText(item)
1704                if name.startswith('PWDR ') and name not in PWDRlist:
1705                    PWDRlist.append(name)
1706                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1707        # Initialize a base class reader
1708        rd = G2IO.ImportPowderData(
1709            extensionlist=tuple(),
1710            strictExtension=False,
1711            formatName = 'Simulate dataset',
1712            longFormatName = 'Compute a simulated pattern')
1713        rd.powderentry[0] = '' # no filename
1714        # #self.powderentry[1] = pos # bank offset (N/A here)
1715        rd.powderentry[2] = 1 # only one bank
1716        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1717        self.CheckNotebook()
1718        Iparm = None
1719        lastdatafile = ''
1720        self.zipfile = None
1721        # get instrument parameters for it
1722        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, '', lastdatafile)
1723        if 'T' in Iparm1['Type'][0]:
1724            print('TOF simulation not supported yet')
1725            return False
1726        else:
1727            # need to get name, 2theta start, end, step
1728            rd.idstring = ' CW'
1729            if 'X' in Iparm1['Type'][0]:
1730                rd.idstring = 'CW x-ray simulation'
1731            else:
1732                rd.idstring = 'CW neutron simulation'
1733            # base initial range on wavelength
1734            wave = Iparm1.get('Lam')
1735            if wave:
1736                wave = wave[0]
1737            else:
1738                wave = Iparm1.get('Lam1')
1739                if wave:
1740                    wave = wave[0]
1741        N = 0
1742        while (N < 3): # insist on a dataset with a few points
1743            names = ('dataset name', 'start angle', 'end angle', 'step size')
1744            if not wave or wave < 1.0:
1745                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1746            else:
1747                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1748            dlg = G2G.ScrolledMultiEditor(
1749                self,[inp] * len(inp),range(len(inp)),names,
1750                header='Enter simulation name and range',
1751                minvals=(None,0.001,0.001,0.0001),
1752                maxvals=(None,180.,180.,.1),
1753                sizevals=((225,-1),)
1754                )
1755            dlg.CenterOnParent()
1756            if dlg.ShowModal() == wx.ID_OK:
1757                if inp[1] > inp[2]:
1758                    end,start,step = inp[1:]
1759                else:               
1760                    start,end,step = inp[1:]
1761                step = abs(step)
1762            else:
1763                return False
1764            N = int((end-start)/step)+1
1765            x = np.linspace(start,end,N,True)
1766            N = len(x)
1767        rd.powderdata = [
1768            np.array(x), # x-axis values
1769            np.zeros_like(x), # powder pattern intensities
1770            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1771            np.zeros_like(x), # calc. intensities (zero)
1772            np.zeros_like(x), # calc. background (zero)
1773            np.zeros_like(x), # obs-calc profiles
1774            ]
1775        Tmin = rd.powderdata[0][0]
1776        Tmax = rd.powderdata[0][-1]
1777        # data are read, now store them in the tree
1778        HistName = inp[0]
1779        HistName = 'PWDR '+HistName
1780        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1781        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1782        Ymax = np.max(rd.powderdata[1])
1783        valuesdict = {
1784            'wtFactor':1.0,
1785            'Dummy':True,
1786            'ranId':ran.randint(0,sys.maxint),
1787            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
1788            'qPlot':False,'dPlot':False,'sqrtPlot':False
1789            }
1790        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1791        self.PatternTree.SetItemPyData(
1792            self.PatternTree.AppendItem(Id,text='Comments'),
1793            rd.comments)
1794        self.PatternTree.SetItemPyData(
1795            self.PatternTree.AppendItem(Id,text='Limits'),
1796            [(Tmin,Tmax),[Tmin,Tmax]])
1797        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1798        self.PatternTree.SetItemPyData(
1799            self.PatternTree.AppendItem(Id,text='Background'),
1800            [['chebyschev',True,3,1.0,0.0,0.0],
1801             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1802        self.PatternTree.SetItemPyData(
1803            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1804            [Iparm1,Iparm2])
1805        self.PatternTree.SetItemPyData(
1806            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1807            rd.Sample)
1808        self.PatternTree.SetItemPyData(
1809            self.PatternTree.AppendItem(Id,text='Peak List')
1810            ,{'peaks':[],'sigDict':{}})
1811        self.PatternTree.SetItemPyData(
1812            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1813            [[],[]])
1814        self.PatternTree.SetItemPyData(
1815            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1816            [])
1817        self.PatternTree.SetItemPyData(
1818            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1819            {})
1820        self.PatternTree.Expand(Id)
1821        self.PatternTree.SelectItem(Id)
1822        print('Added simulation powder data '+HistName+ 
1823              ' with parameters from '+str(rd.instmsg))
1824
1825        # make a list of phase names
1826        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1827        phaseNameList = usedHistograms.keys() # phase names in use
1828        if not phaseNameList: return # no phases yet, nothing to do
1829        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1830        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1831        if not result: return
1832        # connect new phases to histograms
1833        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1834        if not sub:
1835            raise Exception('ERROR -- why are there no phases here?')
1836        item, cookie = self.PatternTree.GetFirstChild(sub)
1837        iph = -1
1838        while item: # loop over (new) phases
1839            iph += 1
1840            data = self.PatternTree.GetItemPyData(item)
1841            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1842            if iph not in result: continue
1843            generalData = data['General']
1844            SGData = generalData['SGData']
1845            UseList = data['Histograms']
1846            NShkl = len(G2spc.MustrainNames(SGData))
1847            NDij = len(G2spc.HStrainNames(SGData))
1848            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
1849            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1850            refList = self.PatternTree.GetItemPyData(
1851                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1852            refList[generalData['Name']] = []
1853        self.EnableRefineCommand()
1854        return # success
1855       
1856    def OnPreferences(self,event):
1857        'Edit the GSAS-II configuration variables'
1858        dlg = G2G.SelectConfigSetting(self)
1859        dlg.ShowModal() == wx.ID_OK
1860        dlg.Destroy()
1861
1862    def _Add_ImportMenu_smallangle(self,parent):
1863        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1864        '''
1865        submenu = wx.Menu()
1866        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1867            submenu, help='Import small angle data')
1868        for reader in self.ImportSmallAngleReaderlist:
1869            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1870                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1871            self.ImportMenuId[item.GetId()] = reader
1872            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1873        # item = submenu.Append(wx.ID_ANY,
1874        #     help='Import small angle data, use file to try to determine format',
1875        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1876        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1877
1878    def OnImportSmallAngle(self,event):
1879        '''Called in response to an Import/Small Angle Data/... menu item
1880        to read a small angle diffraction data set.
1881        dict self.ImportMenuId is used to look up the specific
1882        reader item associated with the menu item, which will be
1883        None for the last menu item, which is the "guess" option
1884        where all appropriate formats will be tried.
1885
1886        '''
1887       
1888        def GetSASDIparm(reader):
1889            parm = reader.instdict
1890            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1891                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1892            return Iparm,{}
1893           
1894        # get a list of existing histograms
1895        SASDlist = []
1896        if self.PatternTree.GetCount():
1897            item, cookie = self.PatternTree.GetFirstChild(self.root)
1898            while item:
1899                name = self.PatternTree.GetItemText(item)
1900                if name.startswith('SASD ') and name not in SASDlist:
1901                    SASDlist.append(name)
1902                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1903        # look up which format was requested
1904        reqrdr = self.ImportMenuId.get(event.GetId()) 
1905        rdlist = self.OnImportGeneric(
1906            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1907        if len(rdlist) == 0: return
1908        self.CheckNotebook()
1909        newHistList = []
1910        self.EnablePlot = False
1911        for rd in rdlist:
1912            HistName = rd.idstring
1913            HistName = 'SASD '+HistName
1914            # make new histogram names unique
1915            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1916            print 'Read small angle data '+HistName+ \
1917                ' from file '+self.lastimport
1918            # data are read, now store them in the tree
1919            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1920            Iparm1,Iparm2 = GetSASDIparm(rd)
1921#            if 'T' in Iparm1['Type'][0]:
1922#                if not rd.clockWd and rd.GSAS:
1923#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1924#                cw = np.diff(rd.powderdata[0])
1925#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1926#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1927#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1928#                if 'Itype' in Iparm2:
1929#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1930#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1931#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1932#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1933#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1934#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1935#                    var += WYI*rd.powderdata[1]**2
1936#                    var /= YI**2
1937#                    rd.powderdata[2] = 1./var
1938#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1939#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1940#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1941            Tmin = min(rd.smallangledata[0])
1942            Tmax = max(rd.smallangledata[0])
1943            valuesdict = {
1944                'wtFactor':1.0,
1945                'Dummy':False,
1946                'ranId':ran.randint(0,sys.maxint),
1947                }
1948            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1949            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1950            self.PatternTree.SetItemPyData(
1951                self.PatternTree.AppendItem(Id,text='Comments'),
1952                rd.comments)
1953            self.PatternTree.SetItemPyData(
1954                self.PatternTree.AppendItem(Id,text='Limits'),
1955                [(Tmin,Tmax),[Tmin,Tmax]])
1956            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1957            self.PatternTree.SetItemPyData(
1958                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1959                [Iparm1,Iparm2])
1960            self.PatternTree.SetItemPyData(
1961                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1962            self.PatternTree.SetItemPyData(
1963                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1964                rd.Sample)
1965            self.PatternTree.SetItemPyData(
1966                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1967            newHistList.append(HistName)
1968        else:
1969            self.EnablePlot = True
1970            self.PatternTree.Expand(Id)
1971            self.PatternTree.SelectItem(Id)
1972           
1973        if not newHistList: return # somehow, no new histograms
1974        return # success
1975
1976    def OnMacroRecordStatus(self,event,setvalue=None):
1977        '''Called when the record macro menu item is used which toggles the
1978        value. Alternately a value to be set can be provided. Note that this
1979        routine is made more complex because on the Mac there are lots of menu
1980        items (listed in self.MacroStatusList) and this loops over all of them.
1981        '''
1982        nextvalue = log.ShowLogStatus() != True
1983        if setvalue is not None:
1984            nextvalue = setvalue
1985        if nextvalue:
1986            log.LogOn()
1987            set2 = True
1988        else:
1989            log.LogOff()
1990            set2 = False
1991        for menuitem in self.MacroStatusList:
1992            menuitem.Check(set2)
1993
1994    def _init_Macro(self):
1995        '''Define the items in the macro menu.
1996        '''
1997        menu = self.MacroMenu
1998        item = menu.Append(
1999                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
2000                kind=wx.ITEM_CHECK,text='Record actions')
2001        self.MacroStatusList.append(item)
2002        item.Check(log.ShowLogStatus())
2003        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
2004
2005        # this may only be of value for development work
2006        item = menu.Append(
2007            help='Show logged commands', id=wx.ID_ANY,
2008            kind=wx.ITEM_NORMAL,text='Show log')
2009        def OnShowLog(event):
2010            print 70*'='
2011            print 'List of logged actions'
2012            for i,line in enumerate(log.G2logList):
2013                if line: print i,line
2014            print 70*'='
2015        self.Bind(wx.EVT_MENU, OnShowLog, item)
2016
2017        item = menu.Append(
2018            help='Clear logged commands', id=wx.ID_ANY,
2019            kind=wx.ITEM_NORMAL,text='Clear log')
2020        def OnClearLog(event): log.G2logList=[None]
2021        self.Bind(wx.EVT_MENU, OnClearLog, item)
2022       
2023        item = menu.Append(
2024            help='Save logged commands to file', id=wx.ID_ANY,
2025            kind=wx.ITEM_NORMAL,text='Save log')
2026        def OnSaveLog(event):
2027            import cPickle
2028            defnam = os.path.splitext(
2029                os.path.split(self.GSASprojectfile)[1]
2030                )[0]+'.gcmd'
2031            dlg = wx.FileDialog(self,
2032                'Choose an file to save past actions', '.', defnam, 
2033                'GSAS-II cmd output (*.gcmd)|*.gcmd',
2034                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2035            dlg.CenterOnParent()
2036            try:
2037                if dlg.ShowModal() == wx.ID_OK:
2038                    filename = dlg.GetPath()
2039                    # make sure extension is correct
2040                    filename = os.path.splitext(filename)[0]+'.gcmd'
2041                else:
2042                    filename = None
2043            finally:
2044                dlg.Destroy()
2045            if filename:
2046                fp = open(filename,'wb')
2047                fp.write(str(len(log.G2logList)-1)+'\n')
2048                for item in log.G2logList:
2049                    if item: cPickle.dump(item,fp)
2050                fp.close()
2051        self.Bind(wx.EVT_MENU, OnSaveLog, item)
2052
2053        item = menu.Append(
2054            help='Load logged commands from file', id=wx.ID_ANY,
2055            kind=wx.ITEM_NORMAL,text='Load log')
2056        def OnLoadLog(event):
2057            # this appends. Perhaps we should ask to clear?
2058            import cPickle
2059            defnam = os.path.splitext(
2060                os.path.split(self.GSASprojectfile)[1]
2061                )[0]+'.gcmd'
2062            dlg = wx.FileDialog(self,
2063                'Choose an file to read saved actions', '.', defnam, 
2064                'GSAS-II cmd output (*.gcmd)|*.gcmd',
2065                wx.OPEN)
2066            dlg.CenterOnParent()
2067            try:
2068                if dlg.ShowModal() == wx.ID_OK:
2069                    filename = dlg.GetPath()
2070                    # make sure extension is correct
2071                    filename = os.path.splitext(filename)[0]+'.gcmd'
2072                else:
2073                    filename = None
2074            finally:
2075                dlg.Destroy()
2076            if filename and os.path.exists(filename):
2077                fp = open(filename,'rb')
2078                lines = fp.readline()
2079                for i in range(int(lines)):
2080                    log.G2logList.append(cPickle.load(fp))
2081                fp.close()
2082        self.Bind(wx.EVT_MENU, OnLoadLog, item)
2083
2084        item = menu.Append(
2085            help='Replay saved commands', id=wx.ID_ANY,
2086            kind=wx.ITEM_NORMAL,text='Replay log')
2087        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
2088
2089    def _init_Exports(self,menu):
2090        '''Find exporter routines and add them into menus
2091        '''
2092        # set up the top-level menus
2093        projectmenu = wx.Menu()
2094        item = menu.AppendMenu(
2095            wx.ID_ANY, 'Entire project as',
2096            projectmenu, help='Export entire project')
2097
2098        phasemenu = wx.Menu()
2099        item = menu.AppendMenu(
2100            wx.ID_ANY, 'Phase as',
2101            phasemenu, help='Export phase or sometimes phases')
2102
2103        powdermenu = wx.Menu()
2104        item = menu.AppendMenu(
2105            wx.ID_ANY, 'Powder data as',
2106            powdermenu, help='Export powder diffraction histogram(s)')
2107
2108        singlemenu = wx.Menu()
2109        item = menu.AppendMenu(
2110            wx.ID_ANY, 'Single crystal data as',
2111            singlemenu, help='Export single crystal histogram(s)')
2112
2113        imagemenu = wx.Menu()
2114        item = menu.AppendMenu(
2115            wx.ID_ANY, 'Image data as',
2116            imagemenu, help='Export powder image(s) data')
2117
2118        mapmenu = wx.Menu()
2119        item = menu.AppendMenu(
2120            wx.ID_ANY, 'Maps as',
2121            mapmenu, help='Export density map(s)')
2122
2123        # pdfmenu = wx.Menu()
2124        # item = menu.AppendMenu(
2125        #     wx.ID_ANY, 'PDFs as',
2126        #     pdfmenu, help='Export pair distribution function(s)')
2127
2128        # find all the exporter files
2129        if not self.exporterlist: # this only needs to be done once
2130            pathlist = sys.path
2131            filelist = []
2132            for path in pathlist:
2133                for filename in glob.iglob(os.path.join(path,"G2export*.py")):
2134                    filelist.append(filename)   
2135            filelist = sorted(list(set(filelist))) # remove duplicates
2136            # go through the routines and import them, saving objects that
2137            # have export routines (method Exporter)
2138            for filename in filelist:
2139                path,rootname = os.path.split(filename)
2140                pkg = os.path.splitext(rootname)[0]
2141#                try:
2142                fp = None
2143                fp, fppath,desc = imp.find_module(pkg,[path,])
2144                pkg = imp.load_module(pkg,fp,fppath,desc)
2145                for clss in inspect.getmembers(pkg): # find classes defined in package
2146                    if clss[0].startswith('_'): continue
2147                    if inspect.isclass(clss[1]):
2148                        # check if we have the required methods
2149                        for m in 'Exporter','loadParmDict':
2150                            if not hasattr(clss[1],m): break
2151                            if not callable(getattr(clss[1],m)): break
2152                        else:
2153                            exporter = clss[1](self) # create an export instance
2154                            self.exporterlist.append(exporter)
2155#                except AttributeError:
2156#                    print 'Import_'+errprefix+': Attribute Error'+str(filename)
2157#                    pass
2158#                except ImportError:
2159#                    print 'Import_'+errprefix+': Error importing file'+str(filename)
2160#                    pass
2161                if fp: fp.close()
2162        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
2163        for obj in self.exporterlist:
2164            #print 'exporter',obj
2165            for typ in obj.exporttype:
2166                if typ == "project":
2167                    submenu = projectmenu
2168                elif typ == "phase":
2169                    submenu = phasemenu
2170                elif typ == "powder":
2171                    submenu = powdermenu
2172                elif typ == "single":
2173                    submenu = singlemenu
2174                elif typ == "image":
2175                    submenu = imagemenu
2176                elif typ == "map":
2177                    submenu = mapmenu
2178                # elif typ == "pdf":
2179                #     submenu = pdfmenu
2180                else:
2181                    print("Error, unknown type in "+str(obj))
2182                    break
2183                item = submenu.Append(
2184                    wx.ID_ANY,
2185                    help=obj.longFormatName,
2186                    kind=wx.ITEM_NORMAL,
2187                    text=obj.formatName)
2188                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
2189                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
2190        item = imagemenu.Append(wx.ID_ANY,
2191                        help='Export image controls and masks for multiple images',
2192                        kind=wx.ITEM_NORMAL,
2193                        text='Multiple image controls and masks')
2194        self.Bind(wx.EVT_MENU, self.OnSaveMultipleImg, id=item.GetId())
2195        #code to debug an Exporter. hard-code the routine below, to allow a reload before use
2196        # def DebugExport(event):
2197        #      print 'start reload'
2198        #      reload(G2IO)
2199        #      import G2export_pwdr as dev
2200        #      reload(dev)
2201        #      dev.ExportPowderFXYE(self).Exporter(event)
2202        # item = menu.Append(
2203        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2204        #     help="debug exporter",text="test Export FXYE")
2205        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
2206        # # #self.ExportLookup[item.GetId()] = 'image'
2207        # self.ExportLookup[item.GetId()] = 'powder'
2208
2209    def _Add_ExportMenuItems(self,parent):
2210        # item = parent.Append(
2211        #     help='Select PWDR item to enable',id=wx.ID_ANY,
2212        #     kind=wx.ITEM_NORMAL,
2213        #     text='Export Powder Patterns...')
2214        # self.ExportPattern.append(item)
2215        # item.Enable(False)
2216        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
2217
2218        item = parent.Append(
2219            help='',id=wx.ID_ANY,
2220            kind=wx.ITEM_NORMAL,
2221            text='Export All Peak Lists...')
2222        self.ExportPeakList.append(item)
2223        item.Enable(True)
2224        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
2225
2226        item = parent.Append(
2227            help='',id=wx.ID_ANY,
2228            kind=wx.ITEM_NORMAL,
2229            text='Export HKLs...')
2230        self.ExportHKL.append(item)
2231        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
2232
2233        item = parent.Append(
2234            help='Select PDF item to enable',
2235            id=wx.ID_ANY,
2236            kind=wx.ITEM_NORMAL,
2237            text='Export PDF...')
2238        self.ExportPDF.append(item)
2239        item.Enable(False)
2240        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
2241
2242    def FillMainMenu(self,menubar,addhelp=True):
2243        '''Define contents of the main GSAS-II menu for the (main) data tree window.
2244        For the mac, this is also called for the data item windows as well so that
2245        the main menu items are data menu as well.
2246        '''
2247        File = wx.Menu(title='')
2248        menubar.Append(menu=File, title='&File')
2249        self._Add_FileMenuItems(File)
2250        Data = wx.Menu(title='')
2251        menubar.Append(menu=Data, title='Data')
2252        self._Add_DataMenuItems(Data)
2253        Calculate = wx.Menu(title='')       
2254        menubar.Append(menu=Calculate, title='&Calculate')
2255        self._Add_CalculateMenuItems(Calculate)
2256        Import = wx.Menu(title='')       
2257        menubar.Append(menu=Import, title='Import')
2258        self._Add_ImportMenu_Image(Import)
2259        self._Add_ImportMenu_Phase(Import)
2260        self._Add_ImportMenu_powder(Import)
2261        self._Add_ImportMenu_Sfact(Import)
2262        self._Add_ImportMenu_smallangle(Import)
2263
2264        #======================================================================
2265        # Code to help develop/debug an importer, much is hard-coded below
2266        # but module is reloaded before each use, allowing faster testing
2267        # def DebugImport(event):
2268        #     print 'start reload'
2269        #     import G2phase_ISO as dev
2270        #     reload(dev)
2271        #     rd = dev.ISODISTORTPhaseReader()
2272        #     self.ImportMenuId[event.GetId()] = rd
2273        #     self.OnImportPhase(event)
2274            # or ----------------------------------------------------------------------
2275            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
2276            # special debug code
2277            # or ----------------------------------------------------------------------
2278            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
2279            # fp = open(filename,'Ur')
2280            # if not rd.ContentsValidator(fp):
2281            #     print 'not validated'
2282            #     # make a list of used phase ranId's
2283            # phaseRIdList = []
2284            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2285            # if sub:
2286            #     item, cookie = self.PatternTree.GetFirstChild(sub)
2287            #     while item:
2288            #         phaseName = self.PatternTree.GetItemText(item)
2289            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2290            #         if ranId: phaseRIdList.append(ranId)
2291            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2292            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2293            #     print 'read OK'
2294        # item = Import.Append(
2295        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2296        #     help="debug importer",text="test importer")
2297        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2298        #======================================================================
2299        self.ExportMenu = wx.Menu(title='')
2300        menubar.Append(menu=self.ExportMenu, title='Export')
2301        self._init_Exports(self.ExportMenu)
2302        self._Add_ExportMenuItems(self.ExportMenu)
2303        if GSASIIpath.GetConfigValue('Enable_logging'):
2304            self.MacroMenu = wx.Menu(title='')
2305            menubar.Append(menu=self.MacroMenu, title='Macro')
2306            self._init_Macro()
2307        if addhelp:
2308            HelpMenu=G2G.MyHelp(self,includeTree=True,
2309                morehelpitems=[('&Tutorials','Tutorials'),])
2310            menubar.Append(menu=HelpMenu,title='&Help')
2311           
2312    def _init_ctrls(self, parent):
2313        wx.Frame.__init__(self, name='GSASII', parent=parent,
2314            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2315        clientSize = wx.ClientDisplayRect()
2316        Size = self.GetSize()
2317        xPos = clientSize[2]-Size[0]
2318        self.SetPosition(wx.Point(xPos,clientSize[1]))
2319        self._init_Imports()
2320        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2321        self.MakePDF = []
2322        self.Refine = []
2323        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2324        #self.ExportPattern = []
2325        self.ExportPeakList = []
2326        self.ExportHKL = []
2327        self.ExportPDF = []
2328        self.ExportPhase = []
2329        self.ExportCIF = []
2330        #
2331        self.GSASIIMenu = wx.MenuBar()
2332        # create a list of all dataframe menus (appended in PrefillDataMenu)
2333        self.dataMenuBars = [self.GSASIIMenu]
2334        self.MacroStatusList = []
2335        self.FillMainMenu(self.GSASIIMenu)
2336        self.SetMenuBar(self.GSASIIMenu)
2337        self.Bind(wx.EVT_SIZE, self.OnSize)
2338        self.Status = self.CreateStatusBar()
2339        self.mainPanel = wx.Panel(self,-1)
2340       
2341        wxID_PATTERNTREE = wx.NewId()
2342        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2343        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2344            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2345        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,self.OnDataTreeSelChanged)
2346        self.PatternTree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK,self.OnDataTreeSelChanged)
2347        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2348            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2349        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2350            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2351        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2352            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2353        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2354            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2355        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2356            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2357        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2358            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2359        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2360        self.root = self.PatternTree.root
2361        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2362            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2363        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame,G2frame=self)
2364        plotFrame.Show()
2365       
2366        self.dataDisplay = None
2367       
2368    def __init__(self, parent):
2369        self.ExportLookup = {}
2370        self.exporterlist = []
2371        self._init_ctrls(parent)
2372        self.Image = wx.Image(
2373            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2374            wx.BITMAP_TYPE_ICO)
2375        if "wxMSW" in wx.PlatformInfo:
2376            img = self.Image.Scale(16, 16).ConvertToBitmap()
2377        elif "wxGTK" in wx.PlatformInfo:
2378            img = self.Image.Scale(22, 22).ConvertToBitmap()
2379        else:
2380            img = self.Image.ConvertToBitmap()
2381        self.SetIcon(wx.IconFromBitmap(img))
2382        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2383        # various defaults
2384        self.oldFocus = None
2385        self.GSASprojectfile = ''
2386        self.undofile = ''
2387        self.TreeItemDelete = False
2388        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2389        self.Weight = False
2390        self.IfPlot = False
2391        self.DDShowAll = False
2392        self.atmSel = ''
2393        self.PatternId = 0
2394        self.PickId = 0
2395        self.PickIdText = None
2396        self.PeakTable = []
2397        self.LimitsTable = []
2398        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2399        self.HKL = []
2400        self.Lines = []
2401        self.itemPicked = None
2402        self.dataFrame = None
2403        self.Interpolate = 'nearest'
2404        self.ContourColor = 'Paired'
2405        self.VcovColor = 'RdYlGn'
2406        self.RamaColor = 'Blues'
2407        self.Projection = 'equal area'
2408        self.logPlot = False
2409        self.plusPlot = True
2410        self.sqPlot = False
2411        self.ErrorBars = False
2412        self.Contour = False
2413        self.Legend = False
2414        self.SinglePlot = True
2415        self.Waterfall = False
2416        self.selections= None
2417        self.PDFselections = None
2418        self.SubBack = False
2419        self.seqReverse = False
2420        self.seqLines = True #draw lines between points
2421        self.plotView = 0
2422        self.Image = 0
2423        self.oldImagefile = '' # the name of the last image file read
2424        self.oldImageTag = None # the name of the tag for multi-image files
2425        self.PauseIntegration = False
2426        self.ImageZ = []
2427        self.Integrate = 0
2428        self.imageDefault = {}
2429        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2430        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2431        self.autoIntFrame = None
2432        self.IntegratedList = [] # list of already integrated IMG tree items
2433        self.Sngl = False
2434        self.ifGetRing = False
2435        self.MaskKey = ''           #trigger for making image masks
2436        self.MskDelete = False      #trigger for mask delete
2437        self.StrainKey = ''         #ditto for new strain d-zeros
2438        self.EnablePlot = True
2439        self.hist = ''              # selected histogram in Phase/Data tab
2440        self.dirname = os.path.abspath(os.path.expanduser('~'))       #start in the users home directory by default; may be meaningless
2441        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2442        self.LastImportDir = None # last-used directory where an import was done
2443        self.LastGPXdir = None    # directory where a GPX file was last read
2444        self.LastExportDir = None  # the last directory used for exports, if any.
2445        self.dataDisplayPhaseText = ''
2446        self.lastTreeSetting = []
2447        self.ExpandingAll = False
2448               
2449        arg = sys.argv
2450        if len(arg) > 1 and arg[1]:
2451            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2452            self.dirname = os.path.abspath(os.path.dirname(arg[1]))
2453            if self.dirname: os.chdir(self.dirname)
2454            try:
2455                self.StartProject()         #open the file if possible
2456                return
2457            except Exception:
2458                print 'Error opening or reading file',arg[1]
2459                import traceback
2460                print traceback.format_exc()
2461               
2462        if GSASIIpath.GetConfigValue('Starting_directory'):
2463            try:
2464                pth = GSASIIpath.GetConfigValue('Starting_directory')
2465                pth = os.path.expanduser(pth) 
2466                os.chdir(pth)
2467                self.LastGPXdir = pth
2468            except:
2469                print('Ignoring Config Starting_directory value: '+
2470                      GSASIIpath.GetConfigValue('Starting_directory'))
2471
2472    def GetTreeItemsList(self,item):
2473        return self.PatternTree._getTreeItemsList(item)
2474
2475    def OnSize(self,event):
2476        'Called to make PatternTree fill mainPanel'
2477        w,h = self.GetClientSizeTuple()
2478        self.mainPanel.SetSize(wx.Size(w,h))
2479        self.PatternTree.SetSize(wx.Size(w,h))
2480                       
2481    def OnDataTreeSelChanged(self, event):
2482        '''Called when a data tree item is selected'''
2483        if self.TreeItemDelete:
2484            self.TreeItemDelete = False
2485        else:
2486            if self.ExpandingAll:
2487                #if GSASIIpath.GetConfigValue('debug'): print('Skipping Tree selection due to ExpandAll')
2488                return
2489            pltNum = self.G2plotNB.nb.GetSelection()
2490            if pltNum >= 0:                         #to avoid the startup with no plot!
2491                self.G2plotNB.nb.GetPage(pltNum)
2492            item = event.GetItem()
2493            wx.CallAfter(G2gd.SelectDataTreeItem,self,item)
2494            if self.oldFocus: # Why do this?
2495                self.oldFocus.SetFocus()
2496       
2497    def OnPatternTreeItemCollapsed(self, event):
2498        'Called when a tree item is collapsed - all children will be collapsed'
2499        self.PatternTree.CollapseAllChildren(event.GetItem())
2500
2501    def OnPatternTreeItemExpanded(self, event):
2502        'Called when a tree item is expanded'
2503        self.OnDataTreeSelChanged(event)
2504        event.Skip()
2505       
2506    def OnPatternTreeItemDelete(self, event):
2507        'Called when a tree item is deleted -- not sure what this does'
2508        self.TreeItemDelete = True
2509
2510    def OnPatternTreeItemActivated(self, event):
2511        'Called when a tree item is activated'
2512        event.Skip()
2513       
2514    def OnPatternTreeBeginRDrag(self,event):
2515        event.Allow()
2516        self.BeginDragId = event.GetItem()
2517        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2518        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2519        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2520        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2521        while item:     #G2 data tree has no sub children under a child of a tree item
2522            name = self.PatternTree.GetItemText(item)
2523            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2524            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2525       
2526    def OnPatternTreeEndDrag(self,event):
2527        event.Allow()
2528        self.EndDragId = event.GetItem()
2529        try:
2530            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2531        except:
2532            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2533            NewParent = self.root
2534        if self.ParentId != NewParent:
2535            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2536        else:
2537            Name,Item = self.DragData[0]
2538            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2539            self.PatternTree.SetItemPyData(NewId,Item)
2540            for name,item in self.DragData[1:]:     #loop over children
2541                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2542                self.PatternTree.SetItemPyData(Id,item)
2543            self.PatternTree.Delete(self.BeginDragId)
2544            G2gd.SelectDataTreeItem(self,NewId)
2545       
2546    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2547        'Allows stepping through the tree with the up/down arrow keys'
2548        self.oldFocus = wx.Window.FindFocus()
2549        keyevt = event.GetKeyEvent()
2550        key = event.GetKeyCode()
2551        item = self.PatternTree.GetSelection()
2552        if type(item) is int: return # is this the toplevel in tree?
2553        name = self.PatternTree.GetItemText(item)
2554        parent = self.PatternTree.GetItemParent(item)
2555        if key == wx.WXK_UP:
2556            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2557                if type(parent) is int: return # is this the toplevel in tree?
2558                prev = self.PatternTree.GetPrevSibling(parent)
2559                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2560                if NewId:
2561                    self.PatternTree.Collapse(parent)
2562                    self.PatternTree.Expand(prev)
2563                    self.oldFocus = wx.Window.FindFocus()
2564                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2565                else:
2566                    wx.CallAfter(self.PatternTree.SelectItem,item)
2567            else:   
2568                self.PatternTree.GetPrevSibling(item)
2569                self.PatternTree.SelectItem(item)
2570        elif key == wx.WXK_DOWN:
2571            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2572                next = self.PatternTree.GetNextSibling(parent)
2573                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2574                if NewId:
2575                    self.PatternTree.Collapse(parent)
2576                    self.PatternTree.Expand(next)
2577                    self.oldFocus = wx.Window.FindFocus()
2578                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2579                else:
2580                    wx.CallAfter(self.PatternTree.SelectItem,item)
2581            else:   
2582                self.PatternTree.GetNextSibling(item)
2583                self.PatternTree.SelectItem(item)
2584               
2585    def OnReadPowderPeaks(self,event):
2586        'Bound to menu Data/Read Powder Peaks'
2587        self.CheckNotebook()
2588        pth = G2G.GetImportPath(self)
2589        if not pth: pth = '.'
2590        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2591            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2592        try:
2593            if dlg.ShowModal() == wx.ID_OK:
2594                self.HKL = []
2595                self.powderfile = dlg.GetPath()
2596                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2597                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2598                data = ['PKS',wave,0.0]
2599                names = ['Type','Lam','Zero'] 
2600                codes = [0,0,0]
2601                inst = [G2IO.makeInstDict(names,data,codes),{}]
2602                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2603                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2604                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2605                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2606                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2607                self.PatternTree.Expand(Id)
2608                self.PatternTree.SelectItem(Id)
2609                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2610        finally:
2611            dlg.Destroy()
2612                       
2613    def OnImageRead(self,event):
2614        '''Called to read in an image in any known format. *** Depreciated. ***
2615        '''
2616        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2617
2618    def CheckNotebook(self):
2619        '''Make sure the data tree has the minimally expected controls.
2620        '''
2621        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2622            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2623            self.PatternTree.SetItemPyData(sub,[''])
2624        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2625            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2626            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2627        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2628            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2629            self.PatternTree.SetItemPyData(sub,{})
2630        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2631            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2632            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2633        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2634            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2635            self.PatternTree.SetItemPyData(sub,{})
2636        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2637            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2638            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2639                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2640               
2641    class CopyDialog(wx.Dialog):
2642        '''Creates a dialog for copying control settings between
2643        data tree items'''
2644        def __init__(self,parent,title,text,data):
2645            wx.Dialog.__init__(self,parent,-1,title, 
2646                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2647            self.data = data
2648            panel = wx.Panel(self)
2649            mainSizer = wx.BoxSizer(wx.VERTICAL)
2650            topLabl = wx.StaticText(panel,-1,text)
2651            mainSizer.Add((10,10),1)
2652            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2653            mainSizer.Add((10,10),1)
2654            ncols = len(data)/40+1
2655            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2656            for id,item in enumerate(self.data):
2657                ckbox = wx.CheckBox(panel,id,item[1])
2658                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2659                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2660            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2661            OkBtn = wx.Button(panel,-1,"Ok")
2662            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2663            cancelBtn = wx.Button(panel,-1,"Cancel")
2664            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2665            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2666            btnSizer.Add((20,20),1)
2667            btnSizer.Add(OkBtn)
2668            btnSizer.Add((20,20),1)
2669            btnSizer.Add(cancelBtn)
2670            btnSizer.Add((20,20),1)
2671           
2672            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2673            panel.SetSizer(mainSizer)
2674            panel.Fit()
2675            self.Fit()
2676       
2677        def OnCopyChange(self,event):
2678            id = event.GetId()
2679            self.data[id][0] = self.FindWindowById(id).GetValue()       
2680           
2681        def OnOk(self,event):
2682            parent = self.GetParent()
2683            parent.Raise()
2684            self.EndModal(wx.ID_OK)             
2685           
2686        def OnCancel(self,event):
2687            parent = self.GetParent()
2688            parent.Raise()
2689            self.EndModal(wx.ID_CANCEL)             
2690           
2691        def GetData(self):
2692            return self.data
2693       
2694    class SumDialog(wx.Dialog):
2695        '''Allows user to supply scale factor(s) when summing data -
2696        TODO: CAN WE PREVIEW RESULT HERE?'''
2697        def __init__(self,parent,title,text,dataType,data,dataList):
2698            wx.Dialog.__init__(self,parent,-1,title,size=(400,250),
2699                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
2700            self.plotFrame = wx.Frame(self,-1,'Sum Plots',size=wx.Size(700,600), \
2701                style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2702            self.G2plotNB = G2plt.G2PlotNoteBook(self.plotFrame,G2frame=self)
2703            self.data = data
2704            self.dataList = dataList
2705            self.dataType = dataType
2706            size = (400,250)
2707            panel = wxscroll.ScrolledPanel(self, wx.ID_ANY,size=size,
2708                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
2709            mainSizer = wx.BoxSizer(wx.VERTICAL)
2710            topLabl = wx.StaticText(panel,-1,text)
2711            mainSizer.Add((10,10),1)
2712            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2713            mainSizer.Add((10,10),1)
2714            self.dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2715            for id,item in enumerate(self.data[:-1]):
2716                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(300,20))
2717                name.SetEditable(False)
2718#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2719                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2720                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2721                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2722                self.dataGridSizer.Add(scale,0,wx.LEFT,10)
2723                self.dataGridSizer.Add(name,0,wx.RIGHT,10)
2724            if self.dataType:
2725                self.dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+self.dataType),0, \
2726                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2727                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(300,20),style=wx.TE_PROCESS_ENTER)
2728                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2729                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2730                self.dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2731                self.dataGridSizer.Add(wx.StaticText(panel,label='All scales value: '),0,  \
2732                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2733#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2734                allScale = wx.TextCtrl(panel,value='',style=wx.TE_PROCESS_ENTER)
2735                allScale.Bind(wx.EVT_TEXT_ENTER,self.OnAllScale)
2736                allScale.Bind(wx.EVT_KILL_FOCUS,self.OnAllScale)
2737                self.dataGridSizer.Add(allScale,0,WACV)
2738            mainSizer.Add(self.dataGridSizer,0,wx.EXPAND)
2739            TestBtn = wx.Button(panel,-1,"Test")
2740            TestBtn.Bind(wx.EVT_BUTTON, self.OnTest)
2741            OkBtn = wx.Button(panel,-1,"Ok")
2742            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2743            cancelBtn = wx.Button(panel,-1,"Cancel")
2744            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2745            btnSizer = wx.FlexGridSizer(0,3,10,20)
2746            if self.dataType =='PWDR':  btnSizer.Add(TestBtn)
2747            btnSizer.Add(OkBtn)
2748            btnSizer.Add(cancelBtn)
2749           
2750            panel.SetSizer(mainSizer)
2751            panel.SetAutoLayout(1)
2752            panel.SetupScrolling()
2753            mainSizer.Add((10,10),1)
2754            mainSizer.Add(btnSizer,0,wx.CENTER)
2755            panel.SetSizer(mainSizer)
2756            panel.Fit()
2757            self.Fit()
2758
2759        def OnScaleChange(self,event):
2760            event.Skip()
2761            id = event.GetId()
2762            value = self.FindWindowById(id).GetValue()
2763            try:
2764                self.data[id][0] = float(value)
2765                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2766            except ValueError:
2767                if value and '-' not in value[0]:
2768                    print 'bad input - numbers only'
2769                    self.FindWindowById(id).SetValue('0.000')
2770                   
2771        def OnAllScale(self,event):
2772            event.Skip()
2773            id = event.GetId()
2774            try:
2775                scale = float(self.FindWindowById(id).GetValue())
2776                self.FindWindowById(id).SetValue('%.3f'%(scale))
2777                entries = self.dataGridSizer.GetChildren()
2778                for i,item in enumerate(self.data[:-1]):
2779                    item[0] = scale
2780                    entries[2*i].GetWindow().SetValue('%.3f'%(scale))
2781                 
2782            except ValueError:
2783                print 'bad input - numbers only'
2784                self.FindWindowById(id).SetValue('')
2785                   
2786           
2787        def OnNameChange(self,event):
2788            event.Skip()
2789            self.data[-1] = self.name.GetValue()
2790           
2791        def OnTest(self,event):
2792            lenX = 0
2793            Xminmax = [0,0]
2794            XY = []
2795            Xsum = []
2796            Ysum = []
2797            Vsum = []
2798            result = self.data
2799            for i,item in enumerate(result[:-1]):
2800                scale,name = item
2801                data = self.dataList[i]
2802                if scale:
2803                    x,y,w,yc,yb,yd = data   #numpy arrays!
2804                    XY.append([x,scale*y])
2805                    v = 1./w
2806                    if lenX:
2807                        if lenX != len(x):
2808                            self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2809                                '\nExpected:'+str(lenX)+ \
2810                                '\nFound:   '+str(len(x))+'\nfor '+name)
2811                            self.OnCancel(event)
2812                    else:
2813                        lenX = len(x)
2814                    if Xminmax[1]:
2815                        if Xminmax != [x[0],x[-1]]:
2816                            self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2817                                '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2818                                '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2819                            self.OnCancel(event)
2820                        else:
2821                            for j,yi in enumerate(y):
2822                                 Ysum[j] += scale*yi
2823                                 Vsum[j] += abs(scale)*v[j]
2824                    else:
2825                        Xminmax = [x[0],x[-1]]
2826                        Xsum = x
2827                        Ysum = scale*y
2828                        Vsum = abs(scale*v)
2829            Wsum = 1./np.array(Vsum)
2830            YCsum = np.zeros(lenX)
2831            YBsum = np.zeros(lenX)
2832            YDsum = np.zeros(lenX)
2833            XY.append([Xsum,Ysum])
2834            self.result = [Xsum,Ysum,Wsum,YCsum,YBsum,YDsum]
2835            # N.B. PlotXY expects the first arg to point to G2frame. In this case, we
2836            # create a duplicate (temporary) Plot notebook window that is a child of the
2837            # modal SumDialog dialog (self). This nicely gets deleted when the dialog is destroyed,
2838            # but the plot window is not fully functional, at least on the Mac.
2839            G2plt.PlotXY(self,XY,lines=True,Title='Sum:'+self.data[-1],labelY='Intensity',)
2840            self.plotFrame.Show()
2841                       
2842        def OnOk(self,event):
2843            if self.dataType == 'PWDR': self.OnTest(event)
2844            parent = self.GetParent()
2845            parent.Raise()
2846            self.EndModal(wx.ID_OK)             
2847           
2848        def OnCancel(self,event):
2849            parent = self.GetParent()
2850            parent.Raise()
2851            self.EndModal(wx.ID_CANCEL)             
2852           
2853        def GetData(self):
2854            if self.dataType == 'PWDR':
2855                return self.data,self.result
2856            else:
2857                return self.data
2858                       
2859    def OnPwdrSum(self,event):
2860        'Sum together powder data(?)'
2861        TextList = []
2862        DataList = []
2863        Names = []
2864        Inst = None
2865        Comments = ['Sum equals: \n']
2866        if self.PatternTree.GetCount():
2867            item, cookie = self.PatternTree.GetFirstChild(self.root)
2868            while item:
2869                name = self.PatternTree.GetItemText(item)
2870                Names.append(name)
2871                if 'PWDR' in name:
2872                    TextList.append([0.0,name])
2873                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2874                    if not Inst:
2875                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2876                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2877            if len(TextList) < 2:
2878                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2879                return
2880            TextList.append('default_sum_name')               
2881            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList,DataList)
2882            try:
2883                if dlg.ShowModal() == wx.ID_OK:
2884                    result,sumData = dlg.GetData()
2885                    Xsum,Ysum,Wsum,YCsum,YBsum,YDsum = sumData
2886                    Xminmax = [Xsum[0],Xsum[-1]]
2887                    outname = 'PWDR '+result[-1]
2888                    Id = 0
2889                    if outname in Names:
2890                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2891                        try:
2892                            if dlg2.ShowModal() == wx.ID_OK:
2893                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2894                                self.PatternTree.Delete(Id)
2895                        finally:
2896                            dlg2.Destroy()
2897                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2898                    if Id:
2899                        Sample = G2pdG.SetDefaultSample()
2900                        Ymax = np.max(Ysum)
2901                        valuesdict = {
2902                            'wtFactor':1.0,
2903                            'Dummy':False,
2904                            'ranId':ran.randint(0,sys.maxint),
2905                            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
2906                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2907                            }
2908                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2909                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2910                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2911                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2912                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2913                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2914                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2915                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2916                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2917                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2918                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2919                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2920                        self.PatternTree.SelectItem(Id)
2921                        self.PatternTree.Expand(Id)
2922            finally:
2923                dlg.Destroy()
2924
2925    def OnImageSum(self,event):
2926        'Sum together image data'
2927        TextList = []
2928        DataList = []
2929        Names = []
2930        Comments = ['Sum equals: \n']
2931        if self.PatternTree.GetCount():
2932            item, cookie = self.PatternTree.GetFirstChild(self.root)
2933            while item:
2934                name = self.PatternTree.GetItemText(item)
2935                Names.append(name)
2936                if 'IMG' in name:
2937                    TextList.append([0.0,name])
2938                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2939                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2940                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2941            if len(TextList) < 2:
2942                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2943                return
2944            TextList.append('default_sum_name')               
2945            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList,DataList)
2946            try:
2947                if dlg.ShowModal() == wx.ID_OK:
2948                    imSize = 0
2949                    result = dlg.GetData()
2950                    First = True
2951                    Found = False
2952                    for i,item in enumerate(result[:-1]):
2953                        scale,name = item
2954                        if scale:
2955                            Found = True                               
2956                            Comments.append("%10.3f %s" % (scale,' * '+name))
2957                            Npix,imagefile,imagetag = DataList[i]
2958                            imagefile = G2IO.CheckImageFile(self,imagefile)
2959                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2960                            if First:
2961                                newImage = np.zeros_like(image)
2962                                First = False
2963                            if imSize:
2964                                if imSize != Npix:
2965                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2966                                        '\nExpected:'+str(imSize)+ \
2967                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2968                                    return
2969                                newImage = newImage+scale*image
2970                            else:
2971                                imSize = Npix
2972                                newImage = newImage+scale*image
2973                            del(image)
2974                    if not Found:
2975                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2976                        return
2977                       
2978                       
2979                    newImage = np.array(newImage,dtype=np.int32)                       
2980                    outname = 'IMG '+result[-1]
2981                    Id = 0
2982                    if outname in Names:
2983                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2984                        try:
2985                            if dlg2.ShowModal() == wx.ID_OK:
2986                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2987                        finally:
2988                            dlg2.Destroy()
2989                    else:
2990                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2991                    if Id:
2992                        pth = G2G.GetExportPath(self)
2993                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth,outname.split('IMG ')[1], 
2994                            'G2img files (*.G2img)|*.G2img', 
2995                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
2996                        if dlg.ShowModal() == wx.ID_OK:
2997                            newimagefile = dlg.GetPath()
2998                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2999                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
3000                            Imax = np.amax(newImage)
3001                            Imin = np.amin(newImage)
3002                            newImage = []
3003                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
3004                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
3005                        del(newImage)
3006                        if self.imageDefault:
3007                            Data = copy.copy(self.imageDefault)
3008                        Data['formatName'] = 'GSAS-II image'
3009                        Data['showLines'] = True
3010                        Data['ring'] = []
3011                        Data['rings'] = []
3012                        Data['cutoff'] = 10
3013                        Data['pixLimit'] = 20
3014                        Data['ellipses'] = []
3015                        Data['calibrant'] = ''
3016                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
3017                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
3018                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
3019                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
3020                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
3021                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
3022                        self.PatternTree.SelectItem(Id)
3023                        self.PatternTree.Expand(Id)
3024                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
3025                        self.Image = self.PickId
3026            finally:
3027                dlg.Destroy()
3028                     
3029    def OnAddPhase(self,event):
3030        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
3031        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3032            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
3033        else:
3034            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3035        PhaseName = ''
3036        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
3037            style=wx.OK)
3038        if dlg.ShowModal() == wx.ID_OK:
3039            PhaseName = dlg.GetValue()
3040        dlg.Destroy()
3041        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
3042        E,SGData = G2spc.SpcGroup('P 1')
3043        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
3044        G2gd.SelectDataTreeItem(self,sub) #bring up new phase General tab
3045       
3046    def OnDeletePhase(self,event):
3047        'Delete a phase from the tree. Called by Data/Delete Phase menu'
3048        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
3049        if self.dataFrame:
3050            self.dataFrame.Clear() 
3051        TextList = []
3052        DelList = []
3053        DelItemList = []
3054        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3055            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3056        else:
3057            return
3058        if sub:
3059            item, cookie = self.PatternTree.GetFirstChild(sub)
3060            while item:
3061                TextList.append(self.PatternTree.GetItemText(item))
3062                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
3063            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
3064            try:
3065                if dlg.ShowModal() == wx.ID_OK:
3066                    result = dlg.GetSelections()
3067                    for i in result: DelList.append([i,TextList[i]])
3068                    item, cookie = self.PatternTree.GetFirstChild(sub)
3069                    i = 0
3070                    while item:
3071                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
3072                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3073                        i += 1
3074                    for item in DelItemList:
3075                        name = self.PatternTree.GetItemText(item)
3076                        self.PatternTree.Delete(item)
3077                        self.G2plotNB.Delete(name)
3078                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3079                    while item:
3080                        name = self.PatternTree.GetItemText(item)
3081                        if 'PWDR' in name:
3082                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
3083                            refList = self.PatternTree.GetItemPyData(Id)
3084                            if len(refList):
3085                                for i,item in DelList:
3086                                    if item in refList:
3087                                        del(refList[item])
3088#                            self.PatternTree.SetItemPyData(Id,refList)
3089                        elif 'HKLF' in name:
3090                            data = self.PatternTree.GetItemPyData(item)
3091                            data[0] = {}
3092#                            self.PatternTree.SetItemPyData(item,data)
3093                           
3094                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3095            finally:
3096                dlg.Destroy()
3097               
3098    def OnRenameData(self,event):
3099        'Renames an existing phase. Called by Data/Rename Phase menu'
3100        name = self.PatternTree.GetItemText(self.PickId)     
3101        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
3102            if 'Bank' in name:
3103                names = name.split('Bank')
3104                names[1] = ' Bank'+names[1]
3105            elif 'Azm' in name:
3106                names = name.split('Azm')
3107                names[1] = ' Azm'+names[1]
3108            else:
3109                names = [name,'']
3110            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
3111            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
3112                defaultValue=names[0][names[0].index(' ')+1:])
3113            try:
3114                if dlg.ShowModal() == wx.ID_OK:
3115                    name = dataType+dlg.GetValue()+names[1]
3116                    self.PatternTree.SetItemText(self.PickId,name)
3117            finally:
3118                dlg.Destroy()
3119       
3120    def GetFileList(self,fileType,skip=None):        #potentially useful?
3121        'Appears unused. Note routine of same name in GSASIIpwdGUI'
3122        fileList = []
3123        Source = ''
3124        id, cookie = self.PatternTree.GetFirstChild(self.root)
3125        while id:
3126            name = self.PatternTree.GetItemText(id)
3127            if fileType in name:
3128                if id == skip:
3129                    Source = name
3130                else:
3131                    fileList.append([False,name,id])
3132            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3133        if skip:
3134            return fileList,Source
3135        else:
3136            return fileList
3137           
3138    def OnDataDelete(self, event):
3139        '''Delete one or more histograms from data tree. Called by the
3140        Data/DeleteData menu
3141        '''
3142        TextList = []
3143        DelList = []
3144        DelItemList = []
3145        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
3146        if self.PatternTree.GetCount():
3147            item, cookie = self.PatternTree.GetFirstChild(self.root)
3148            while item:
3149                name = self.PatternTree.GetItemText(item)
3150                if name not in ['Notebook','Controls','Covariance','Constraints',
3151                    'Restraints','Phases','Rigid bodies'] and 'Sequential' not in name:
3152                    if 'PWDR' in name: nItems['PWDR'] += 1
3153                    if 'SASD' in name: nItems['SASD'] += 1
3154                    if 'IMG' in name:  nItems['IMG'] += 1
3155                    if 'HKLF' in name: nItems['HKLF'] += 1
3156                    if 'PDF' in name:  nItems['PDF'] += 1
3157                    TextList.append(name)
3158                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3159            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
3160            try:
3161                if dlg.ShowModal() == wx.ID_OK:
3162                    result = dlg.GetSelections()
3163                    for i in result: DelList.append(TextList[i])
3164                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3165                    while item:
3166                        itemName = self.PatternTree.GetItemText(item)
3167                        if itemName in DelList:
3168                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
3169                            elif 'SASD' in itemName: nItems['SASD'] -= 1
3170                            elif 'IMG' in itemName: nItems['IMG'] -= 1
3171                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
3172                            elif 'PDF' in itemName: nItems['PDF'] -= 1
3173                            DelItemList.append(item)
3174                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3175                    for item in DelItemList:
3176                        self.PatternTree.Delete(item)
3177                    self.PickId = 0
3178                    self.PickIdText = None
3179                    self.PatternId = 0
3180                    if nItems['PWDR']:
3181                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3182                    else:
3183                        self.G2plotNB.Delete('Powder Patterns')
3184                    if not nItems['IMG']:
3185                        self.G2plotNB.Delete('2D Powder Image')
3186                    if not nItems['HKLF']:
3187                        self.G2plotNB.Delete('Structure Factors')
3188                        if '3D Structure Factors' in self.G2plotNB.plotList:
3189                            self.G2plotNB.Delete('3D Structure Factors')
3190            finally:
3191                dlg.Destroy()
3192
3193    def OnFileOpen(self, event, filename=None):
3194        '''Gets a GSAS-II .gpx project file in response to the
3195        File/Open Project menu button
3196        '''
3197        result = wx.ID_OK
3198        self.EnablePlot = False
3199        if self.PatternTree.GetChildrenCount(self.root,False):
3200            if self.dataFrame:
3201                self.dataFrame.Clear() 
3202            dlg = wx.MessageDialog(
3203                self,
3204                'Do you want to overwrite the current project? '+
3205                'Any unsaved changes in current project will be lost. Press OK to continue.',
3206                'Overwrite?',  wx.OK | wx.CANCEL)
3207            try:
3208                result = dlg.ShowModal()
3209                if result == wx.ID_OK:
3210                    self.PatternTree.DeleteChildren(self.root)
3211                    self.GSASprojectfile = ''
3212                    self.HKL = []
3213                    if self.G2plotNB.plotList:
3214                        self.G2plotNB.clear()
3215            finally:
3216                dlg.Destroy()
3217        if result != wx.ID_OK: return
3218
3219        if not filename:
3220            if self.dataDisplay: self.dataDisplay.Destroy()
3221            if self.LastGPXdir:
3222                pth = self.LastGPXdir
3223            else:
3224                pth = '.'
3225            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3226                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3227            try:
3228                if dlg.ShowModal() != wx.ID_OK: return
3229                self.GSASprojectfile = dlg.GetPath()
3230                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3231                self.dirname = dlg.GetDirectory()
3232            finally:
3233                dlg.Destroy()
3234        else:
3235            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3236            self.dirname = os.path.split(filename)[0]
3237
3238        try:
3239            self.StartProject()         #open the file if possible
3240        except:
3241            print '\nError opening file ',filename
3242            import traceback
3243            print traceback.format_exc()
3244       
3245    def StartProject(self):
3246        '''Opens a GSAS-II project file & selects the 1st available data set to
3247        display (PWDR, HKLF or SASD)
3248        '''
3249       
3250        Id = 0
3251        phaseId = None
3252        G2IO.ProjFileOpen(self)
3253        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3254        self.PatternTree.Expand(self.root)
3255        self.HKL = []
3256        item, cookie = self.PatternTree.GetFirstChild(self.root)
3257        while item and not Id:
3258            name = self.PatternTree.GetItemText(item)
3259            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3260                Id = item
3261            elif name == "Phases":
3262                phaseId = item
3263            elif name == 'Controls':
3264                data = self.PatternTree.GetItemPyData(item)
3265                if data:
3266                    for item in self.Refine: item.Enable(True)
3267                    self.EnableSeqRefineMenu()
3268            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3269        if phaseId: # show all phases
3270            self.PatternTree.Expand(phaseId)
3271        if Id:
3272            self.EnablePlot = True
3273            self.PatternTree.SelectItem(Id)
3274            self.PatternTree.Expand(Id)
3275        elif phaseId:
3276            self.PatternTree.SelectItem(phaseId)
3277        self.CheckNotebook()
3278        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3279        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3280        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3281        self.LastGPXdir = pth
3282
3283    def OnFileClose(self, event):
3284        '''Clears the data tree in response to the
3285        File/New Project menu button. User is given option to save
3286        the project.
3287        '''
3288        if self.dataFrame:
3289            self.dataFrame.Clear()
3290            self.dataFrame.SetLabel('GSAS-II data display') 
3291        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3292        try:
3293            result = dlg.ShowModal()
3294            if result == wx.ID_OK:
3295                self.OnFileSaveMenu(event)
3296            if result != wx.ID_CANCEL:
3297                self.GSASprojectfile = ''
3298                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3299                self.PatternTree.DeleteChildren(self.root)
3300                if self.HKL: self.HKL = []
3301                if self.G2plotNB.plotList:
3302                    self.G2plotNB.clear()
3303        finally:
3304            dlg.Destroy()
3305
3306    def OnFileSave(self, event):
3307        '''Save the current project in response to the
3308        File/Save Project menu button
3309        '''
3310       
3311        if self.GSASprojectfile: 
3312            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3313            self.CheckNotebook()
3314            G2IO.ProjFileSave(self)
3315        else:
3316            self.OnFileSaveas(event)
3317
3318    def OnFileSaveas(self, event):
3319        '''Save the current project in response to the
3320        File/Save as menu button
3321        '''
3322        if GSASIIpath.GetConfigValue('Starting_directory'):
3323            pth = GSASIIpath.GetConfigValue('Starting_directory')
3324            pth = os.path.expanduser(pth) 
3325        elif self.LastGPXdir:
3326            pth = self.LastGPXdir
3327        else:
3328            pth = '.'
3329        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3330            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3331        try:
3332            if dlg.ShowModal() == wx.ID_OK:
3333                self.GSASprojectfile = dlg.GetPath()
3334                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3335                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3336                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3337                self.CheckNotebook()
3338                G2IO.ProjFileSave(self)
3339                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3340        finally:
3341            dlg.Destroy()
3342           
3343    def ExpandAll(self,event):
3344        '''Expand all tree items or those of a single type
3345        '''
3346        txt = self.GetMenuBar().GetLabel(event.Id)
3347        if txt == 'all':
3348            self.ExpandingAll = True
3349            try:
3350                self.PatternTree.ExpandAll()
3351            finally:
3352                self.ExpandingAll = False
3353        else:
3354            self.ExpandingAll = True
3355            try:
3356                item, cookie = self.PatternTree.GetFirstChild(self.root)
3357                while item:
3358                    name = self.PatternTree.GetItemText(item)
3359                    if name.startswith(txt+' '): self.PatternTree.Expand(item)
3360                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3361            finally:
3362                self.ExpandingAll = False
3363
3364    def MoveTreeItems(self,event):
3365        '''Move tree items of a single type to the end of the tree
3366        '''
3367        txt = self.GetMenuBar().GetLabel(event.Id)
3368        # make a list of items to copy
3369        copyList = []
3370        item, cookie = self.PatternTree.GetFirstChild(self.root)
3371        while item:
3372            if self.PatternTree.GetItemText(item).startswith(txt+' '):
3373                copyList.append(item)
3374            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3375       
3376        self.ExpandingAll = True
3377        try:
3378            for item in copyList:
3379                name = self.PatternTree.GetItemText(item)
3380                newId = self.PatternTree.AppendItem(self.root,name)
3381                self.PatternTree.SetItemPyData(newId,self.PatternTree.GetItemPyData(item))
3382                chld, chldcookie = self.PatternTree.GetFirstChild(item)
3383                while chld:
3384                    chname = self.PatternTree.GetItemText(chld)
3385                    newCh = self.PatternTree.AppendItem(newId,chname)
3386                    self.PatternTree.SetItemPyData(newCh,self.PatternTree.GetItemPyData(chld))
3387                    chld, chldcookie = self.PatternTree.GetNextChild(item, chldcookie)
3388                self.PatternTree.Delete(item)
3389        finally:
3390            self.ExpandingAll = False
3391        G2gd.SelectDataTreeItem(self,self.root)
3392           
3393    def ExitMain(self, event):
3394        '''Called if the main window is closed'''
3395        if self.G2plotNB:
3396            self.G2plotNB.Destroy()
3397        if self.dataFrame:
3398            self.dataFrame.Clear() 
3399            self.dataFrame.Destroy()
3400        if self.undofile:
3401            os.remove(self.undofile)
3402        sys.exit()
3403       
3404    def OnFileExit(self, event):
3405        '''Called in response to the File/Quit menu button'''
3406        if self.G2plotNB:
3407            self.G2plotNB.Destroy()
3408        if self.dataFrame:
3409            self.dataFrame.Clear() 
3410            self.dataFrame.Destroy()
3411        self.Close()
3412       
3413    def OnExportPeakList(self,event):
3414        nptand = lambda x: np.tan(x*math.pi/180.)
3415        pth = G2G.GetExportPath(self)
3416        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3417            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3418        try:
3419            if dlg.ShowModal() == wx.ID_OK:
3420                self.peaklistfile = dlg.GetPath()
3421                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3422                file = open(self.peaklistfile,'w')               
3423                item, cookie = self.PatternTree.GetFirstChild(self.root)
3424                while item:
3425                    name = self.PatternTree.GetItemText(item)
3426                    if 'PWDR' in name:
3427                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3428                        wave = 0.0
3429                        while item2:
3430                            name2 = self.PatternTree.GetItemText(item2)
3431                            if name2 == 'Instrument Parameters':
3432                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3433                                Type = Inst['Type'][0]
3434                                if 'T' not in Type:
3435                                    wave = G2mth.getWave(Inst)
3436                            elif name2 == 'Peak List':
3437                                pkdata = self.PatternTree.GetItemPyData(item2)
3438                                peaks = pkdata['peaks']
3439                                sigDict = pkdata['sigDict']
3440                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3441                        file.write("#%s \n" % (name+' Peak List'))
3442                        if wave:
3443                            file.write('#wavelength = %10.6f\n'%(wave))
3444                        if 'T' in Type:
3445                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3446                        else:
3447                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3448                        for ip,peak in enumerate(peaks):
3449                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3450                            if 'T' in Type:  #TOF - more cols
3451                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3452                                for name in esds.keys():
3453                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3454                                sig = np.sqrt(peak[8])
3455                                gam = peak[10]
3456                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3457                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3458                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3459                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3460                            else:               #CW
3461                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3462                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3463                                for name in esds.keys():
3464                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3465                                sig = np.sqrt(peak[4]) #var -> sig
3466                                gam = peak[6]
3467                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3468                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3469                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3470                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3471                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3472                file.close()
3473        finally:
3474            dlg.Destroy()
3475       
3476    def OnExportHKL(self,event):
3477        pth = G2G.GetExportPath(self)
3478        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3479            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3480        try:
3481            if dlg.ShowModal() == wx.ID_OK:
3482                self.peaklistfile = dlg.GetPath()
3483                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3484                file = open(self.peaklistfile,'w')               
3485                item, cookie = self.PatternTree.GetFirstChild(self.root)
3486                while item:
3487                    name = self.PatternTree.GetItemText(item)
3488                    if 'PWDR' in name:
3489                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3490                        while item2:
3491                            name2 = self.PatternTree.GetItemText(item2)
3492                            if name2 == 'Reflection Lists':
3493                                data = self.PatternTree.GetItemPyData(item2)
3494                                phases = data.keys()
3495                                for phase in phases:
3496                                    peaks = data[phase]
3497                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3498                                    if 'T' in peaks.get('Type','PXC'):
3499                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3500                                    else:               
3501                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3502                                    for peak in peaks['RefList']:
3503                                        if 'T' in peaks.get('Type','PXC'):
3504                                            sig = np.sqrt(peak[6])
3505                                            gam = peak[7]
3506                                            FWHM = G2pwd.getgamFW(gam,sig)
3507                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3508                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3509                                        else:
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.5f %10.5f %10.3f \n" % \
3514                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3515                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3516                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3517                file.close()
3518        finally:
3519            dlg.Destroy()
3520       
3521    def OnExportPDF(self,event):
3522        #need S(Q) and G(R) to be saved here - probably best from selection?
3523        names = ['All']
3524        exports = []
3525        item, cookie = self.PatternTree.GetFirstChild(self.root)
3526        while item:
3527            name = self.PatternTree.GetItemText(item)
3528            if 'PDF' in name:
3529                names.append(name)
3530            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3531        if names:
3532            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3533            if dlg.ShowModal() == wx.ID_OK:
3534                sel = dlg.GetSelections()
3535                if sel[0] == 0:
3536                    exports = names[1:]
3537                else:
3538                    for x in sel:
3539                        exports.append(names[x])
3540            dlg.Destroy()
3541        if exports:
3542            G2IO.PDFSave(self,exports)
3543       
3544    def OnMakePDFs(self,event):
3545        '''Sets up PDF data structure filled with defaults; if found chemical formula is inserted
3546        so a default PDF can be made.
3547        '''
3548        sind = lambda x: math.sin(x*math.pi/180.)
3549        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3550        tof2q = lambda t,C:2.0*math.pi*C/t
3551        TextList = []
3552        ElLists = []
3553        Qlimits = []
3554        Names = []
3555        if self.PatternTree.GetCount():
3556            id, cookie = self.PatternTree.GetFirstChild(self.root)
3557            while id:
3558                name = self.PatternTree.GetItemText(id)
3559                Names.append(name)
3560                if 'PWDR' in name:
3561                    TextList.append(name)
3562                    Comments = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Comments'))
3563                    Parms = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Instrument Parameters'))[0]
3564                    fullLimits = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Limits'))[0]
3565                    if 'C' in Parms['Type'][0]:
3566                        wave = G2mth.getWave(Parms)
3567                        qMax = tth2q(fullLimits[1],wave)
3568                    else:   #'T'of
3569                        qMax = tof2q(fullLimits[0],Parms['difC'][1])
3570                    Qlimits.append([0.9*qMax,qMax])
3571                    ElList = {}
3572                    for item in Comments:           #grab chemical formula from Comments
3573                        if 'formula' in item[:15].lower():
3574                            formula = item.split('=')[1].split()
3575                            elems = formula[::2]
3576                            nums = formula[1::2]
3577                            formula = zip(elems,nums)
3578                            for [elem,num] in formula:
3579                                ElData = G2elem.GetElInfo(elem,Parms)
3580                                ElData['FormulaNo'] = float(num)
3581                                ElList[elem] = ElData
3582                    ElLists.append(ElList)
3583                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3584            if len(TextList) < 1:
3585                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3586                return
3587            dlg = G2G.G2MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3588            try:
3589                if dlg.ShowModal() == wx.ID_OK:
3590                    for i in dlg.GetSelections():
3591                        item = TextList[i]
3592                        ElList = ElLists[i]
3593                        PWDRname = item[4:]
3594                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3595                        Data = {
3596                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3597                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3598                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3599                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':ElList,
3600                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3601                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':Qlimits[i],
3602                            'Lorch':False,'BackRatio':0.0,'Rmax':100.,'noRing':False,'IofQmin':1.0}
3603                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3604                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3605                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3606                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3607                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3608                for item in self.ExportPDF: item.Enable(True)
3609            finally:
3610                dlg.Destroy()
3611               
3612    def GetPWDRdatafromTree(self,PWDRname):
3613        ''' Returns powder data from GSASII tree
3614
3615        :param str PWDRname: a powder histogram name as obtained from
3616          :meth:`GSASIIstruct.GetHistogramNames`
3617
3618        :returns: PWDRdata = powder data dictionary with
3619          Powder data arrays, Limits, Instrument Parameters,
3620          Sample Parameters           
3621        '''
3622        PWDRdata = {}
3623        try:
3624            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3625        except ValueError:
3626            PWDRdata['wtFactor'] = 1.0
3627        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3628        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3629        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3630        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3631        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3632        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3633        if 'ranId' not in PWDRdata:  # patch, add a random Id
3634            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3635        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3636            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3637        return PWDRdata
3638
3639    def GetHKLFdatafromTree(self,HKLFname):
3640        ''' Returns single crystal data from GSASII tree
3641
3642        :param str HKLFname: a single crystal histogram name as obtained
3643          from
3644          :meth:`GSASIIstruct.GetHistogramNames`
3645
3646        :returns: HKLFdata = single crystal data list of reflections
3647
3648        '''
3649        HKLFdata = {}
3650        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3651#        try:
3652#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3653#        except ValueError:
3654#            HKLFdata['wtFactor'] = 1.0
3655        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3656        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3657        return HKLFdata
3658       
3659    def GetPhaseData(self):
3660        '''Returns a dict with defined phases.
3661        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3662        get same info from GPX file.
3663        '''
3664        phaseData = {}
3665        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3666            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3667        else:
3668            print 'no phases found in GetPhaseData'
3669            sub = None
3670        if sub:
3671            item, cookie = self.PatternTree.GetFirstChild(sub)
3672            while item:
3673                phaseName = self.PatternTree.GetItemText(item)
3674                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3675                if 'ranId' not in phaseData[phaseName]:
3676                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3677                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3678        return phaseData
3679
3680    def GetPhaseInfofromTree(self):
3681        '''Get the phase names and their rId values,
3682        also the histograms used in each phase.
3683
3684        :returns: (phaseRIdList, usedHistograms) where
3685
3686          * phaseRIdList is a list of random Id values for each phase
3687          * usedHistograms is a dict where the keys are the phase names
3688            and the values for each key are a list of the histogram names
3689            used in each phase.
3690        '''
3691        phaseRIdList = []
3692        usedHistograms = {}
3693        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3694        if sub:
3695            item, cookie = self.PatternTree.GetFirstChild(sub)
3696            while item:
3697                phaseName = self.PatternTree.GetItemText(item)
3698                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3699                if ranId: phaseRIdList.append(ranId)
3700                data = self.PatternTree.GetItemPyData(item)
3701                UseList = data['Histograms']
3702                usedHistograms[phaseName] = UseList.keys()
3703                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3704        return phaseRIdList,usedHistograms
3705
3706    def GetPhaseNames(self):
3707        '''Returns a list of defined phases.
3708        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3709        get same info from GPX file.
3710        '''
3711        phaseNames = []
3712        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3713            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3714        else:
3715            print 'no phases found in GetPhaseNames'
3716            sub = None
3717        if sub:
3718            item, cookie = self.PatternTree.GetFirstChild(sub)
3719            while item:
3720                phase = self.PatternTree.GetItemText(item)
3721                phaseNames.append(phase)
3722                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3723        return phaseNames
3724   
3725    def GetHistogramNames(self,hType):
3726        """ Returns a list of histogram names found in the GSASII data tree
3727        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3728        get same info from GPX file.
3729       
3730        :param str hType: list of histogram types
3731        :return: list of histogram names
3732       
3733        """
3734        HistogramNames = []
3735        if self.PatternTree.GetCount():
3736            item, cookie = self.PatternTree.GetFirstChild(self.root)
3737            while item:
3738                name = self.PatternTree.GetItemText(item)
3739                if name[:4] in hType:
3740                    HistogramNames.append(name)       
3741                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3742
3743        return HistogramNames
3744                   
3745    def GetUsedHistogramsAndPhasesfromTree(self):
3746        ''' Returns all histograms that are found in any phase
3747        and any phase that uses a histogram.
3748        This also assigns numbers to used phases and histograms by the
3749        order they appear in the file.
3750        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3751        get same info from GPX file.
3752
3753        :returns: (Histograms,Phases)
3754
3755            * Histograms = dictionary of histograms as {name:data,...}
3756            * Phases = dictionary of phases that use histograms
3757        '''
3758        Histograms = {}
3759        Phases = {}
3760        phaseNames = self.GetPhaseNames()
3761        phaseData = self.GetPhaseData()
3762        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3763
3764        for phase in phaseData:
3765            Phase = phaseData[phase]
3766            pId = phaseNames.index(phase)
3767            Phase['pId'] = pId
3768            if Phase['Histograms']:
3769                if phase not in Phases:
3770                    Phases[phase] = Phase
3771                for hist in Phase['Histograms']:
3772                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3773                        Phase['Histograms'][hist]['Use'] = True         
3774                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3775                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3776                        if item:
3777                            if 'PWDR' in hist[:4]: 
3778                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3779                            elif 'HKLF' in hist[:4]:
3780                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3781                            hId = histoList.index(hist)
3782                            Histograms[hist]['hId'] = hId
3783                        else: # would happen if a referenced histogram were renamed or deleted
3784                            print('For phase "'+phase+
3785                                  '" unresolved reference to histogram "'+hist+'"')
3786        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3787        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3788        return Histograms,Phases
3789       
3790    def MakeLSParmDict(self):
3791        '''Load all parameters used for computation from the tree into a
3792        dict of paired values [value, refine flag]. Note that this is
3793        different than the parmDict used in the refinement, which only has
3794        values.
3795
3796        Note that similar things are done in
3797        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3798        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3799        a GPX file).
3800
3801        :returns: (parmDict,varyList) where:
3802
3803         * parmDict is a dict with values and refinement flags
3804           for each parameter and
3805         * varyList is a list of variables (refined parameters).
3806        '''
3807        parmDict = {}
3808        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3809        for phase in Phases:
3810            if 'pId' not in Phases[phase]:
3811                self.ErrorDialog('View parameter error','You must run least squares at least once')
3812                raise Exception,'No pId for phase '+str(phase)
3813        rigidbodyDict = self.PatternTree.GetItemPyData(   
3814            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3815        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3816        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3817        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3818        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3819        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3820        varyList = rbVary+phaseVary+hapVary+histVary
3821        parmDict.update(rbDict)
3822        parmDict.update(phaseDict)
3823        parmDict.update(hapDict)
3824        parmDict.update(histDict)
3825        for parm in parmDict:
3826            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3827                'Omega','Chi','Phi','nDebye','nPeaks']:
3828                parmDict[parm] = [parmDict[parm],'-']
3829            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3830                parmDict[parm] = [parmDict[parm],'-']
3831            elif parm in varyList:
3832                parmDict[parm] = [parmDict[parm],'T']
3833            else:
3834                parmDict[parm] = [parmDict[parm],'F']
3835        # for i in parmDict: print i,'\t',parmDict[i]
3836        # fl = open('parmDict.dat','wb')
3837        # import cPickle
3838        # cPickle.dump(parmDict,fl,1)
3839        # fl.close()
3840        return parmDict,varyList
3841
3842    def ShowLSParms(self,event):
3843        '''Displays a window showing all parameters in the refinement.
3844        Called from the Calculate/View LS Parms menu.
3845        '''
3846        parmDict,varyList = self.MakeLSParmDict()
3847        parmValDict = {}
3848        for i in parmDict:
3849            parmValDict[i] = parmDict[i][0]
3850           
3851        reqVaryList = tuple(varyList) # save requested variables
3852        try:
3853            # process constraints
3854            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3855            Constraints = self.PatternTree.GetItemPyData(sub)
3856            constList = []
3857            for item in Constraints:
3858                if item.startswith('_'): continue
3859                constList += Constraints[item]
3860            G2mv.InitVars()
3861            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3862            groups,parmlist = G2mv.GroupConstraints(constrDict)
3863            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3864            G2mv.Map2Dict(parmValDict,varyList)
3865        except:
3866            pass
3867        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3868        dlg.ShowModal()
3869        dlg.Destroy()
3870
3871    def OnRefine(self,event):
3872        '''Perform a refinement.
3873        Called from the Calculate/Refine menu.
3874        '''
3875        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3876        if Id:
3877            dlg = wx.MessageDialog(
3878                self,
3879                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3880                'Remove sequential results?',wx.OK|wx.CANCEL)
3881            if dlg.ShowModal() == wx.ID_OK:
3882                self.PatternTree.Delete(Id)
3883                dlg.Destroy()
3884            else:
3885                dlg.Destroy()
3886                return
3887        self.OnFileSave(event)
3888        # check that constraints are OK here
3889        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3890        if errmsg:
3891            self.ErrorDialog('Refinement error',errmsg)
3892            return
3893        if warnmsg:
3894            print('Conflict between refinment flag settings and constraints:\n'+
3895                warnmsg+'\nRefinement not possible')
3896            self.ErrorDialog('Refinement Flag Error',
3897                'Conflict between refinement flag settings and constraints:\n'+
3898                warnmsg+'\nRefinement not possible')
3899            return
3900        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3901            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3902            parent=self)
3903        Size = dlg.GetSize()
3904        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3905            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3906        dlg.CenterOnParent()
3907        Rw = 100.00
3908        self.SaveTreeSetting()
3909        self.PatternTree.SaveExposedItems()       
3910        try:
3911            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)    #Msg is Rvals dict if Ok=True
3912        finally:
3913            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3914            dlg.Destroy()
3915            wx.Yield()
3916        if OK:
3917            Rw = Msg['Rwp']
3918            lamMax = Msg.get('lamMax',0.001)
3919            text = 'Load new result?'
3920            if lamMax >= 10.:
3921                text += '\nWARNING: Steepest descents dominates;'+   \
3922                ' minimum may not have been reached\nor result may be false minimum.'+  \
3923                ' You should reconsider your parameter suite'
3924            dlg2 = wx.MessageDialog(self,text,'Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3925            try:
3926                if dlg2.ShowModal() == wx.ID_OK:
3927                    self.PatternTree.DeleteChildren(self.root)
3928                    self.HKL = []
3929                    G2IO.ProjFileOpen(self,False)
3930                    self.PatternTree.RestoreExposedItems()       
3931                    self.ResetPlots()
3932            finally:
3933                dlg2.Destroy()
3934        else:
3935            self.ErrorDialog('Refinement error',Msg)
3936       
3937    def SaveTreeSetting(self):
3938        'Save the last tree setting'
3939        oldId =  self.PatternTree.GetSelection()        #retain current selection
3940        oldPath = self.GetTreeItemsList(oldId)
3941        self.lastTreeSetting = oldPath
3942        # note that for reasons unclear, it does not seem necessary to reload the Atoms tab
3943        #parentName = ''
3944        #tabId = None
3945        # parentId = self.PatternTree.GetItemParent(oldId)
3946        # if parentId:
3947        #     parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3948        #     if 'Phases' in parentName:
3949        #         tabId = self.dataDisplay.GetSelection()
3950        #self.lastTreeSetting = oldPath,tabId
3951        #GSASIIpath.IPyBreak()
3952       
3953    def TestResetPlot(self,event):
3954        '''Debug code to test cleaning up plots after a refinement'''
3955        #for i in range(self.G2plotNB.nb.GetPageCount()):
3956        #    [self.G2plotNB.nb.GetPageText(i)
3957        # save current tree item and (if needed) atoms tab
3958        self.SaveTreeSetting()
3959        self.ResetPlots()
3960       
3961    def ResetPlots(self):
3962        '''This reloads the current tree item, often drawing a plot. It refreshes any plots
3963        that have registered a refresh routine (see G2plotNB.RegisterRedrawRoutine)
3964        and deletes all plots that have not been refreshed and
3965        require one (see G2plotNB.SetNoDelete).
3966        '''
3967        lastRaisedPlotTab = self.G2plotNB.lastRaisedPlotTab # save the last page saved
3968        #print 'lastRaisedPlotTab=',lastRaisedPlotTab
3969        self.G2plotNB.lastRaisedPlotTab = None
3970        # mark displayed plots as invalid
3971        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
3972            frame.plotInvalid = True
3973        # reload current tree item, triggering the routine to redraw the data window and possibly a plot
3974        #oldPath,tabId = self.lastTreeSetting
3975        oldPath = self.lastTreeSetting
3976        Id = self.root
3977        for txt in oldPath:
3978            Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3979        self.PickIdText = None  #force reload of page
3980        if Id:
3981            self.PickId = Id
3982            self.PatternTree.SelectItem(Id)
3983        # update other self-updating plots
3984        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
3985            if frame.plotInvalid and frame.replotFunction:
3986                frame.replotFunction(*frame.replotArgs,**frame.replotKWargs)
3987        # delete any remaining plots that are still invalid and need a refresh
3988        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
3989            if frame.plotInvalid and frame.plotRequiresRedraw:
3990                self.G2plotNB.Delete(lbl)
3991        # put the previously last-raised tab on top, if present. If not, use the one corresponding to
3992        # the last tree item to be selected
3993        wx.CallAfter(self.G2plotNB.RaiseLastPage,lastRaisedPlotTab,self.G2plotNB.lastRaisedPlotTab)
3994       
3995    def OnSeqRefine(self,event):
3996        '''Perform a sequential refinement.
3997        Called from the Calculate/Sequential refine menu.
3998        '''
3999        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4000        if not Id:
4001            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
4002            self.PatternTree.SetItemPyData(Id,{})           
4003        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
4004        if not Controls.get('Seq Data'):
4005            print('Error: a sequential refinement has not been set up')
4006            return
4007        Controls['ShowCell'] = True
4008        self.OnFileSave(event)
4009        # check that constraints are OK here
4010        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
4011        if errmsg:
4012            self.ErrorDialog('Refinement error',errmsg)
4013            return
4014        if warnmsg:
4015            print('Conflict between refinment flag settings and constraints:\n'+
4016                  warnmsg+'\nRefinement not possible')
4017            self.ErrorDialog('Refinement Flag Error',
4018                             'Conflict between refinment flag settings and constraints:\n'+
4019                             warnmsg+'\nRefinement not possible')
4020            return
4021        self.PatternTree.SaveExposedItems()       
4022        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
4023            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
4024            parent=self)           
4025        Size = dlg.GetSize()
4026        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
4027            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
4028        dlg.CenterOnParent()
4029        try:
4030            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)     #Msg is Rvals dict if Ok=True
4031        finally:
4032            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
4033            dlg.Destroy()
4034            wx.Yield()
4035        if OK:
4036            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
4037            try:
4038                if dlg.ShowModal() == wx.ID_OK:
4039                    self.PickIdText = None  #force reload of PickId contents
4040                    self.PatternTree.DeleteChildren(self.root)
4041                    if len(self.HKL): self.HKL = []
4042                    if self.G2plotNB.plotList:
4043                        self.G2plotNB.clear()
4044                    G2IO.ProjFileOpen(self,False)
4045                    self.PatternTree.RestoreExposedItems()
4046                    self.ResetPlots()
4047                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4048                    self.PatternTree.SelectItem(Id)
4049            finally:
4050                dlg.Destroy()
4051        else:
4052            self.ErrorDialog('Sequential refinement error',Msg)
4053       
4054    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
4055        'Display an error message'
4056        result = None
4057        if parent is None:
4058            dlg = wx.MessageDialog(self, message, title,  wtype)
4059        else:
4060            dlg = wx.MessageDialog(parent, message, title,  wtype)
4061            dlg.CenterOnParent() # not working on Mac
4062        try:
4063            result = dlg.ShowModal()
4064        finally:
4065            dlg.Destroy()
4066        return result
4067   
4068    def OnSaveMultipleImg(self,event):
4069        '''Select and save multiple image parameter and mask files
4070        '''
4071        G2IO.SaveMultipleImg(self)
4072       
4073class GSASIImain(wx.App):
4074    '''Defines a wxApp for GSAS-II
4075
4076    Creates a wx frame (self.main) which contains the display of the
4077    data tree.
4078    '''
4079    def OnInit(self):
4080        '''Called automatically when the app is created.'''
4081        import platform
4082        if '2.7' not in sys.version[:5]:
4083            dlg = wx.MessageDialog(None, 
4084                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
4085                'Python version error',  wx.OK)
4086            try:
4087                dlg.ShowModal()
4088            finally:
4089                dlg.Destroy()
4090            sys.exit()
4091        self.main = GSASII(None)
4092        self.main.Show()
4093        self.SetTopWindow(self.main)
4094        # save the current package versions
4095        self.main.PackageVersions = []
4096        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
4097        for p in (wx,mpl,np,sp,ogl):
4098            self.main.PackageVersions.append([p.__name__,p.__version__])
4099        try:
4100            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
4101        except:
4102            try:
4103                from PIL import PILLOW_VERSION
4104                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
4105            except:
4106                pass
4107        self.main.PackageVersions.append(['Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
4108       
4109        return True
4110    # def MacOpenFile(self, filename):
4111    #     '''Called on Mac every time a file is dropped on the app when it is running,
4112    #     treat this like a File/Open project menu action.
4113    #     Should be ignored on other platforms
4114    #     '''
4115    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
4116    #     print 'MacOpen',filename
4117    #     if os.path.splitext(filename)[1] == '.py': return
4118    #     # end PATCH
4119    #     self.main.OnFileOpen(None,filename)
4120    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
4121       
4122def main():
4123    '''Start up the GSAS-II application'''
4124    #application = GSASIImain() # don't redirect output, someday we
4125    # may want to do this if we can
4126    application = GSASIImain(0)
4127    if GSASIIpath.GetConfigValue('wxInspector'):
4128        import wx.lib.inspection as wxeye
4129        wxeye.InspectionTool().Show()
4130
4131    #application.main.OnRefine(None)
4132    application.MainLoop()
4133   
4134if __name__ == '__main__':
4135    # print versions
4136    print "Python module versions loaded:"
4137    print "  Python:     ",sys.version.split()[0]
4138    print "  wx:         ",wx.__version__
4139    print "  matplotlib: ",mpl.__version__
4140    print "  numpy:      ",np.__version__
4141    print "  scipy:      ",sp.__version__
4142    print "  OpenGL:     ",ogl.__version__
4143    try:
4144        from PIL import Image
4145        try:
4146            from PIL import PILLOW_VERSION
4147            version = PILLOW_VERSION
4148        except:
4149            version = Image.VERSION
4150        print "  PIL.Image:  ",version
4151    except ImportError:
4152        try:
4153            import Image
4154            print "Image (PIL):",Image.VERSION
4155        except ImportError:
4156            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
4157    import platform
4158    print "  Platform:   ",sys.platform,platform.architecture()[0],platform.machine()
4159    try:
4160        import mkl
4161        print "  Max threads:",mkl.get_max_threads()
4162    except:
4163        pass
4164    #print "wxPython description",wx.PlatformInfo
4165    print "This is GSAS-II revision "+str(GSASIIpath.GetVersionNumber())+'\n'
4166    GSASIIpath.InvokeDebugOpts()
4167    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.