source: trunk/GSASII.py @ 2645

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

improve PDF opt; add PDF opt to Compute All PDFs; rename to Setup PDFs

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