source: trunk/GSASII.py @ 2735

Last change on this file since 2735 was 2735, checked in by vondreele, 7 years ago

set default flight path = 20. in case FPATH0 missing from TOF Iparm file.
fix problem editing flight path
modify SetPhaseWindow? to take Size as optional variable & make sizer an optional variable
Use it for all table only phase windows in place of set SizePosLeft? - fixes issues of windows jumping about after Refine.
for initial LeBail? refinement exclude all hap variables (size, mustrain, etc.)

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