source: trunk/GSASII.py @ 2643

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

labeling on Data items

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