source: trunk/GSASII.py @ 2431

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

fix problem with wxPython 3.x with changing text entry focus. Needed a event.Skip() in every TextCtrl? event handler. Hope I got them all...

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