source: trunk/GSASII.py @ 2416

Last change on this file since 2416 was 2416, checked in by vondreele, 5 years ago

fix issues with summing images & powder patterns - SumDialog? is now a scrolled window & image sum files now opened correctly after sum is finished

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