source: trunk/GSASII.py @ 2420

Last change on this file since 2420 was 2420, checked in by vondreele, 6 years ago

add "All scales value" to SumDialog?

  • 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-11 19:43:03 +0000 (Thu, 11 Aug 2016) $
6# $Author: vondreele $
7# $Revision: 2420 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2420 2016-08-11 19:43:03Z 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: 2420 $")
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=[
2256                           ('&Tutorials','Tutorials'), 
2257                           ])
2258        menubar.Append(menu=HelpMenu,title='&Help')
2259           
2260    def _init_ctrls(self, parent):
2261        wx.Frame.__init__(self, name='GSASII', parent=parent,
2262            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2263        clientSize = wx.ClientDisplayRect()
2264        Size = self.GetSize()
2265        xPos = clientSize[2]-Size[0]
2266        self.SetPosition(wx.Point(xPos,clientSize[1]))
2267        self._init_Imports()
2268        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2269        self.MakePDF = []
2270        self.Refine = []
2271        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2272        #self.ExportPattern = []
2273        self.ExportPeakList = []
2274        self.ExportHKL = []
2275        self.ExportPDF = []
2276        self.ExportPhase = []
2277        self.ExportCIF = []
2278        #
2279        self.GSASIIMenu = wx.MenuBar()
2280        # create a list of all dataframe menus (appended in PrefillDataMenu)
2281        self.dataMenuBars = [self.GSASIIMenu]
2282        self.MacroStatusList = []
2283        self.FillMainMenu(self.GSASIIMenu)
2284        self.SetMenuBar(self.GSASIIMenu)
2285        self.Bind(wx.EVT_SIZE, self.OnSize)
2286        self.Status = self.CreateStatusBar()
2287        self.mainPanel = wx.Panel(self,-1)
2288       
2289        wxID_PATTERNTREE = wx.NewId()
2290        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2291        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2292            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2293        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnPatternTreeSelChanged)
2294        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2295            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2296        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2297            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2298        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2299            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2300        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2301            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2302        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2303            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2304        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2305            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2306        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2307        self.root = self.PatternTree.root
2308        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2309            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2310        #self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
2311        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame,G2frame=self)
2312        plotFrame.Show()
2313       
2314        self.dataDisplay = None
2315       
2316    def __init__(self, parent):
2317        self.ExportLookup = {}
2318        self._init_ctrls(parent)
2319        self.Image = wx.Image(
2320            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2321            wx.BITMAP_TYPE_ICO)
2322        if "wxMSW" in wx.PlatformInfo:
2323            img = self.Image.Scale(16, 16).ConvertToBitmap()
2324        elif "wxGTK" in wx.PlatformInfo:
2325            img = self.Image.Scale(22, 22).ConvertToBitmap()
2326        else:
2327            img = self.Image.ConvertToBitmap()
2328        self.SetIcon(wx.IconFromBitmap(img))
2329        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2330        # various defaults
2331        self.oldFocus = None
2332        self.GSASprojectfile = ''
2333        self.undofile = ''
2334        self.TreeItemDelete = False
2335        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2336        self.Weight = False
2337        self.IfPlot = False
2338        self.DDShowAll = False
2339        self.atmSel = ''
2340        self.PatternId = 0
2341        self.PickId = 0
2342        self.PickIdText = None
2343        self.PeakTable = []
2344        self.LimitsTable = []
2345        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2346        self.HKL = []
2347        self.Lines = []
2348        self.itemPicked = None
2349        self.dataFrame = None
2350        self.Interpolate = 'nearest'
2351        self.ContourColor = 'Paired'
2352        self.VcovColor = 'RdYlGn'
2353        self.RamaColor = 'Blues'
2354        self.Projection = 'equal area'
2355        self.logPlot = False
2356        self.plusPlot = True
2357        self.sqPlot = False
2358        self.ErrorBars = False
2359        self.Contour = False
2360        self.Legend = False
2361        self.SinglePlot = True
2362        self.SubBack = False
2363        self.seqReverse = False
2364        self.seqLines = True #draw lines between points
2365        self.plotView = 0
2366        self.Image = 0
2367        self.oldImagefile = '' # the name of the last image file read
2368        self.oldImageTag = None # the name of the tag for multi-image files
2369        self.PauseIntegration = False
2370        self.ImageZ = []
2371        self.Integrate = 0
2372        self.imageDefault = {}
2373        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2374        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2375        self.autoIntFrame = None
2376        self.IntegratedList = [] # list of already integrated IMG tree items
2377        self.Sngl = False
2378        self.ifGetRing = False
2379        self.MaskKey = ''           #trigger for making image masks
2380        self.StrainKey = ''         #ditto for new strain d-zeros
2381        self.EnablePlot = True
2382        self.hist = ''              # selected histogram in Phase/Data tab
2383        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
2384        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2385        self.LastImportDir = None # last-used directory where an import was done
2386        self.LastGPXdir = None    # directory where a GPX file was last read
2387        self.LastExportDir = None  # the last directory used for exports, if any.
2388        self.dataDisplayPhaseText = ''
2389       
2390        arg = sys.argv
2391        if len(arg) > 1 and arg[1]:
2392            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2393            self.dirname = os.path.dirname(arg[1])
2394            if self.dirname: os.chdir(self.dirname)
2395            try:
2396                self.StartProject()         #open the file if possible
2397                return
2398            except Exception:
2399                print 'Error opening or reading file',arg[1]
2400                import traceback
2401                print traceback.format_exc()
2402               
2403        if GSASIIpath.GetConfigValue('Starting_directory'):
2404            try:
2405                pth = GSASIIpath.GetConfigValue('Starting_directory')
2406                pth = os.path.expanduser(pth) 
2407                os.chdir(pth)
2408                self.LastGPXdir = pth
2409            except:
2410                print('Ignoring Config Starting_directory value: '+
2411                      GSASIIpath.GetConfigValue('Starting_directory'))
2412
2413    def GetTreeItemsList(self,item):
2414        return self.PatternTree._getTreeItemsList(item)
2415
2416    def OnSize(self,event):
2417        'Called to make PatternTree fill mainPanel'
2418        w,h = self.GetClientSizeTuple()
2419        self.mainPanel.SetSize(wx.Size(w,h))
2420        self.PatternTree.SetSize(wx.Size(w,h))
2421                       
2422    def OnPatternTreeSelChanged(self, event):
2423        '''Called when a data tree item is selected'''
2424        if self.TreeItemDelete:
2425            self.TreeItemDelete = False
2426        else:
2427            pltNum = self.G2plotNB.nb.GetSelection()
2428            if pltNum >= 0:                         #to avoid the startup with no plot!
2429                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2430                pltPlot = pltPage.figure
2431            item = event.GetItem()
2432            G2gd.MovePatternTreeToGrid(self,item)
2433            if self.oldFocus:
2434                self.oldFocus.SetFocus()
2435       
2436    def OnPatternTreeItemCollapsed(self, event):
2437        'Called when a tree item is collapsed - all children will be collapsed'
2438        self.PatternTree.CollapseAllChildren(event.GetItem())
2439
2440    def OnPatternTreeItemExpanded(self, event):
2441        'Called when a tree item is expanded'
2442        self.OnPatternTreeSelChanged(event)
2443        event.Skip()
2444       
2445    def OnPatternTreeItemDelete(self, event):
2446        'Called when a tree item is deleted -- not sure what this does'
2447        self.TreeItemDelete = True
2448
2449    def OnPatternTreeItemActivated(self, event):
2450        'Called when a tree item is activated'
2451        event.Skip()
2452       
2453    def OnPatternTreeBeginRDrag(self,event):
2454        event.Allow()
2455        self.BeginDragId = event.GetItem()
2456        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2457        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2458        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2459        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2460        while item:     #G2 data tree has no sub children under a child of a tree item
2461            name = self.PatternTree.GetItemText(item)
2462            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2463            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2464       
2465    def OnPatternTreeEndDrag(self,event):
2466        event.Allow()
2467        self.EndDragId = event.GetItem()
2468        try:
2469            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2470        except:
2471            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2472            NewParent = self.root
2473        if self.ParentId != NewParent:
2474            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2475        else:
2476            Name,Item = self.DragData[0]
2477            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2478            self.PatternTree.SetItemPyData(NewId,Item)
2479            for name,item in self.DragData[1:]:     #loop over children
2480                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2481                self.PatternTree.SetItemPyData(Id,item)
2482            self.PatternTree.Delete(self.BeginDragId)
2483            G2gd.MovePatternTreeToGrid(self,NewId)
2484       
2485    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2486        'Allows stepping through the tree with the up/down arrow keys'
2487        self.oldFocus = wx.Window.FindFocus()
2488        keyevt = event.GetKeyEvent()
2489        key = event.GetKeyCode()
2490        item = self.PatternTree.GetSelection()
2491        if type(item) is int: return # is this the toplevel in tree?
2492        name = self.PatternTree.GetItemText(item)
2493        parent = self.PatternTree.GetItemParent(item)
2494        if key == wx.WXK_UP:
2495            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2496                if type(parent) is int: return # is this the toplevel in tree?
2497                prev = self.PatternTree.GetPrevSibling(parent)
2498                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2499                if NewId:
2500                    self.PatternTree.Collapse(parent)
2501                    self.PatternTree.Expand(prev)
2502                    self.oldFocus = wx.Window.FindFocus()
2503                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2504                else:
2505                    wx.CallAfter(self.PatternTree.SelectItem,item)
2506            else:   
2507                self.PatternTree.GetPrevSibling(item)
2508                self.PatternTree.SelectItem(item)
2509        elif key == wx.WXK_DOWN:
2510            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2511                next = self.PatternTree.GetNextSibling(parent)
2512                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2513                if NewId:
2514                    self.PatternTree.Collapse(parent)
2515                    self.PatternTree.Expand(next)
2516                    self.oldFocus = wx.Window.FindFocus()
2517                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2518                else:
2519                    wx.CallAfter(self.PatternTree.SelectItem,item)
2520            else:   
2521                self.PatternTree.GetNextSibling(item)
2522                self.PatternTree.SelectItem(item)
2523               
2524    def OnReadPowderPeaks(self,event):
2525        'Bound to menu Data/Read Powder Peaks'
2526        Cuka = 1.54052
2527        self.CheckNotebook()
2528        pth = G2G.GetImportPath(self)
2529        if not pth: pth = '.'
2530        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2531            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2532        try:
2533            if dlg.ShowModal() == wx.ID_OK:
2534                self.HKL = []
2535                self.powderfile = dlg.GetPath()
2536                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2537                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2538                data = ['PKS',wave,0.0]
2539                names = ['Type','Lam','Zero'] 
2540                codes = [0,0,0]
2541                inst = [G2IO.makeInstDict(names,data,codes),{}]
2542                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2543                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2544                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2545                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2546                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2547                self.PatternTree.Expand(Id)
2548                self.PatternTree.SelectItem(Id)
2549                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2550        finally:
2551            dlg.Destroy()
2552                       
2553    def OnImageRead(self,event):
2554        '''Called to read in an image in any known format. *** Depreciated. ***
2555        '''
2556        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2557
2558    def CheckNotebook(self):
2559        '''Make sure the data tree has the minimally expected controls.
2560        '''
2561        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2562            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2563            self.PatternTree.SetItemPyData(sub,[''])
2564        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2565            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2566            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2567        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2568            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2569            self.PatternTree.SetItemPyData(sub,{})
2570        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2571            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2572            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2573        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2574            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2575            self.PatternTree.SetItemPyData(sub,{})
2576        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2577            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2578            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2579                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2580               
2581    class CopyDialog(wx.Dialog):
2582        '''Creates a dialog for copying control settings between
2583        data tree items'''
2584        def __init__(self,parent,title,text,data):
2585            wx.Dialog.__init__(self,parent,-1,title, 
2586                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2587            self.data = data
2588            panel = wx.Panel(self)
2589            mainSizer = wx.BoxSizer(wx.VERTICAL)
2590            topLabl = wx.StaticText(panel,-1,text)
2591            mainSizer.Add((10,10),1)
2592            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2593            mainSizer.Add((10,10),1)
2594            ncols = len(data)/40+1
2595            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2596            for id,item in enumerate(self.data):
2597                ckbox = wx.CheckBox(panel,id,item[1])
2598                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2599                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2600            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2601            OkBtn = wx.Button(panel,-1,"Ok")
2602            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2603            cancelBtn = wx.Button(panel,-1,"Cancel")
2604            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2605            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2606            btnSizer.Add((20,20),1)
2607            btnSizer.Add(OkBtn)
2608            btnSizer.Add((20,20),1)
2609            btnSizer.Add(cancelBtn)
2610            btnSizer.Add((20,20),1)
2611           
2612            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2613            panel.SetSizer(mainSizer)
2614            panel.Fit()
2615            self.Fit()
2616       
2617        def OnCopyChange(self,event):
2618            id = event.GetId()
2619            self.data[id][0] = self.FindWindowById(id).GetValue()       
2620           
2621        def OnOk(self,event):
2622            parent = self.GetParent()
2623            parent.Raise()
2624            self.EndModal(wx.ID_OK)             
2625           
2626        def OnCancel(self,event):
2627            parent = self.GetParent()
2628            parent.Raise()
2629            self.EndModal(wx.ID_CANCEL)             
2630           
2631        def GetData(self):
2632            return self.data
2633       
2634    class SumDialog(wx.Dialog):
2635        'Allows user to supply scale factor(s) when summing data'
2636        def __init__(self,parent,title,text,dataType,data):
2637            wx.Dialog.__init__(self,parent,-1,title,size=(400,250),
2638                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
2639            self.data = data
2640            size = (400,250)
2641            panel = wxscroll.ScrolledPanel(self, wx.ID_ANY,size=size,
2642                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
2643            mainSizer = wx.BoxSizer(wx.VERTICAL)
2644            topLabl = wx.StaticText(panel,-1,text)
2645            mainSizer.Add((10,10),1)
2646            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2647            mainSizer.Add((10,10),1)
2648            self.dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2649            for id,item in enumerate(self.data[:-1]):
2650                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(300,20))
2651                name.SetEditable(False)
2652                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2653                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2654                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2655                self.dataGridSizer.Add(scale,0,wx.LEFT,10)
2656                self.dataGridSizer.Add(name,0,wx.RIGHT,10)
2657            if dataType:
2658                self.dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2659                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2660                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(300,20),style=wx.TE_PROCESS_ENTER)
2661                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2662                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2663                self.dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2664                self.dataGridSizer.Add(wx.StaticText(panel,label='All scales value: '),0,  \
2665                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2666                allScale = wx.TextCtrl(panel,value='',style=wx.TE_PROCESS_ENTER)
2667                allScale.Bind(wx.EVT_TEXT_ENTER,self.OnAllScale)
2668                allScale.Bind(wx.EVT_KILL_FOCUS,self.OnAllScale)
2669                self.dataGridSizer.Add(allScale,0,WACV)
2670            mainSizer.Add(self.dataGridSizer,0,wx.EXPAND)
2671            OkBtn = wx.Button(panel,-1,"Ok")
2672            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2673            cancelBtn = wx.Button(panel,-1,"Cancel")
2674            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2675            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2676            btnSizer.Add((20,20),1)
2677            btnSizer.Add(OkBtn)
2678            btnSizer.Add((20,20),1)
2679            btnSizer.Add(cancelBtn)
2680            btnSizer.Add((20,20),1)
2681           
2682            panel.SetSizer(mainSizer)
2683            panel.SetAutoLayout(1)
2684            panel.SetupScrolling()
2685            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2686            panel.SetSizer(mainSizer)
2687            panel.Fit()
2688            self.Fit()
2689
2690        def OnScaleChange(self,event):
2691            id = event.GetId()
2692            value = self.FindWindowById(id).GetValue()
2693            try:
2694                self.data[id][0] = float(value)
2695                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2696            except ValueError:
2697                if value and '-' not in value[0]:
2698                    print 'bad input - numbers only'
2699                    self.FindWindowById(id).SetValue('0.000')
2700                   
2701        def OnAllScale(self,event):
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            self.data[-1] = self.name.GetValue() 
2718           
2719        def OnOk(self,event):
2720            parent = self.GetParent()
2721            parent.Raise()
2722            self.EndModal(wx.ID_OK)             
2723           
2724        def OnCancel(self,event):
2725            parent = self.GetParent()
2726            parent.Raise()
2727            self.EndModal(wx.ID_CANCEL)             
2728           
2729        def GetData(self):
2730            return self.data
2731                       
2732    def OnPwdrSum(self,event):
2733        'Sum together powder data(?)'
2734        TextList = []
2735        DataList = []
2736        SumList = []
2737        Names = []
2738        Inst = None
2739        SumItemList = []
2740        Comments = ['Sum equals: \n']
2741        if self.PatternTree.GetCount():
2742            item, cookie = self.PatternTree.GetFirstChild(self.root)
2743            while item:
2744                name = self.PatternTree.GetItemText(item)
2745                Names.append(name)
2746                if 'PWDR' in name:
2747                    TextList.append([0.0,name])
2748                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2749                    if not Inst:
2750                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2751                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2752            if len(TextList) < 2:
2753                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2754                return
2755            TextList.append('default_sum_name')               
2756            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2757            try:
2758                if dlg.ShowModal() == wx.ID_OK:
2759                    lenX = 0
2760                    Xminmax = [0,0]
2761                    Xsum = []
2762                    Ysum = []
2763                    Vsum = []
2764                    result = dlg.GetData()
2765                    for i,item in enumerate(result[:-1]):
2766                        scale,name = item
2767                        data = DataList[i]
2768                        if scale:
2769                            Comments.append("%10.3f %s" % (scale,' * '+name))
2770                            x,y,w,yc,yb,yd = data   #numpy arrays!
2771                            v = 1./w
2772                            if lenX:
2773                                if lenX != len(x):
2774                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2775                                        '\nExpected:'+str(lenX)+ \
2776                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2777                                    return
2778                            else:
2779                                lenX = len(x)
2780                            if Xminmax[1]:
2781                                if Xminmax != [x[0],x[-1]]:
2782                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2783                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2784                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2785                                    return
2786                                else:
2787                                    for j,yi in enumerate(y):
2788                                         Ysum[j] += scale*yi
2789                                         Vsum[j] += abs(scale)*v[j]
2790                            else:
2791                                Xminmax = [x[0],x[-1]]
2792                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2793                                for j,yi in enumerate(y):
2794                                    Xsum.append(x[j])
2795                                    Ysum.append(scale*yi)
2796                                    Vsum.append(abs(scale*v[j]))
2797                    Wsum = 1./np.array(Vsum)
2798                    outname = 'PWDR '+result[-1]
2799                    Id = 0
2800                    if outname in Names:
2801                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2802                        try:
2803                            if dlg2.ShowModal() == wx.ID_OK:
2804                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2805                                self.PatternTree.Delete(Id)
2806                        finally:
2807                            dlg2.Destroy()
2808                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2809                    if Id:
2810                        Sample = G2pdG.SetDefaultSample()
2811                        valuesdict = {
2812                            'wtFactor':1.0,
2813                            'Dummy':False,
2814                            'ranId':ran.randint(0,sys.maxint),
2815                            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
2816                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2817                            }
2818                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2819                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2820                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2821                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2822                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2823                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2824                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2825                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2826                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2827                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2828                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2829                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2830                        self.PatternTree.SelectItem(Id)
2831                        self.PatternTree.Expand(Id)
2832            finally:
2833                dlg.Destroy()
2834
2835    def OnImageSum(self,event):
2836        'Sum together image data'
2837        TextList = []
2838        DataList = []
2839        SumList = []
2840        Names = []
2841        Inst = []
2842        SumItemList = []
2843        Comments = ['Sum equals: \n']
2844        if self.PatternTree.GetCount():
2845            item, cookie = self.PatternTree.GetFirstChild(self.root)
2846            while item:
2847                name = self.PatternTree.GetItemText(item)
2848                Names.append(name)
2849                if 'IMG' in name:
2850                    TextList.append([0.0,name])
2851                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2852                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2853                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2854            if len(TextList) < 2:
2855                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2856                return
2857            TextList.append('default_sum_name')               
2858            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2859            try:
2860                if dlg.ShowModal() == wx.ID_OK:
2861                    imSize = 0
2862                    result = dlg.GetData()
2863                    First = True
2864                    Found = False
2865                    for i,item in enumerate(result[:-1]):
2866                        scale,name = item
2867                        if scale:
2868                            Found = True                               
2869                            Comments.append("%10.3f %s" % (scale,' * '+name))
2870                            Npix,imagefile,imagetag = DataList[i]
2871                            imagefile = G2IO.CheckImageFile(self,imagefile)
2872                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2873                            if First:
2874                                newImage = np.zeros_like(image)
2875                                First = False
2876                            if imSize:
2877                                if imSize != Npix:
2878                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2879                                        '\nExpected:'+str(imSize)+ \
2880                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2881                                    return
2882                                newImage = newImage+scale*image
2883                            else:
2884                                imSize = Npix
2885                                newImage = newImage+scale*image
2886                            del(image)
2887                    if not Found:
2888                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2889                        return
2890                       
2891                       
2892                    newImage = np.array(newImage,dtype=np.int32)                       
2893                    outname = 'IMG '+result[-1]
2894                    Id = 0
2895                    if outname in Names:
2896                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2897                        try:
2898                            if dlg2.ShowModal() == wx.ID_OK:
2899                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2900                        finally:
2901                            dlg2.Destroy()
2902                    else:
2903                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2904                    if Id:
2905                        pth = G2G.GetExportPath(self)
2906                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth,outname.split('IMG ')[1], 
2907                            'G2img files (*.G2img)|*.G2img', 
2908                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
2909                        if dlg.ShowModal() == wx.ID_OK:
2910                            newimagefile = dlg.GetPath()
2911                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2912                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2913                            Imax = np.amax(newImage)
2914                            Imin = np.amin(newImage)
2915                            newImage = []
2916                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2917                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2918                        del(newImage)
2919                        if self.imageDefault:
2920                            Data = copy.copy(self.imageDefault)
2921                        Data['formatName'] = 'GSAS-II image'
2922                        Data['showLines'] = True
2923                        Data['ring'] = []
2924                        Data['rings'] = []
2925                        Data['cutoff'] = 10
2926                        Data['pixLimit'] = 20
2927                        Data['ellipses'] = []
2928                        Data['calibrant'] = ''
2929                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2930                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2931                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2932                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2933                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2934                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2935                        self.PatternTree.SelectItem(Id)
2936                        self.PatternTree.Expand(Id)
2937                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2938                        self.Image = self.PickId
2939            finally:
2940                dlg.Destroy()
2941                     
2942    def OnAddPhase(self,event):
2943        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2944        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2945            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2946        else:
2947            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2948        PhaseName = ''
2949        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2950            style=wx.OK)
2951        if dlg.ShowModal() == wx.ID_OK:
2952            PhaseName = dlg.GetValue()
2953        dlg.Destroy()
2954        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2955        E,SGData = G2spc.SpcGroup('P 1')
2956        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2957        G2gd.MovePatternTreeToGrid(self,sub) #bring up new phase General tab
2958       
2959    def OnDeletePhase(self,event):
2960        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2961        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2962        if self.dataFrame:
2963            self.dataFrame.Clear() 
2964        TextList = []
2965        DelList = []
2966        DelItemList = []
2967        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2968            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2969        else:
2970            return
2971        if sub:
2972            item, cookie = self.PatternTree.GetFirstChild(sub)
2973            while item:
2974                TextList.append(self.PatternTree.GetItemText(item))
2975                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2976            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2977            try:
2978                if dlg.ShowModal() == wx.ID_OK:
2979                    result = dlg.GetSelections()
2980                    for i in result: DelList.append([i,TextList[i]])
2981                    item, cookie = self.PatternTree.GetFirstChild(sub)
2982                    i = 0
2983                    while item:
2984                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2985                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2986                        i += 1
2987                    for item in DelItemList:
2988                        name = self.PatternTree.GetItemText(item)
2989                        self.PatternTree.Delete(item)
2990                        self.G2plotNB.Delete(name)
2991                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2992                    while item:
2993                        name = self.PatternTree.GetItemText(item)
2994                        if 'PWDR' in name:
2995                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2996                            refList = self.PatternTree.GetItemPyData(Id)
2997                            if len(refList):
2998                                for i,item in DelList:
2999                                    if item in refList:
3000                                        del(refList[item])
3001                            self.PatternTree.SetItemPyData(Id,refList)
3002                        elif 'HKLF' in name:
3003                            data = self.PatternTree.GetItemPyData(item)
3004                            data[0] = {}
3005                            self.PatternTree.SetItemPyData(item,data)
3006                           
3007                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3008            finally:
3009                dlg.Destroy()
3010               
3011    def OnRenameData(self,event):
3012        'Renames an existing phase. Called by Data/Rename Phase menu'
3013        name = self.PatternTree.GetItemText(self.PickId)     
3014        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
3015            if 'Bank' in name:
3016                names = name.split('Bank')
3017                names[1] = ' Bank'+names[1]
3018            elif 'Azm' in name:
3019                names = name.split('Azm')
3020                names[1] = ' Azm'+names[1]
3021            else:
3022                names = [name,'']
3023            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
3024            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
3025                defaultValue=names[0][names[0].index(' ')+1:])
3026            try:
3027                if dlg.ShowModal() == wx.ID_OK:
3028                    name = dataType+dlg.GetValue()+names[1]
3029                    self.PatternTree.SetItemText(self.PickId,name)
3030            finally:
3031                dlg.Destroy()
3032       
3033    def GetFileList(self,fileType,skip=None):        #potentially useful?
3034        'Appears unused. Note routine of same name in GSASIIpwdGUI'
3035        fileList = []
3036        Source = ''
3037        id, cookie = self.PatternTree.GetFirstChild(self.root)
3038        while id:
3039            name = self.PatternTree.GetItemText(id)
3040            if fileType in name:
3041                if id == skip:
3042                    Source = name
3043                else:
3044                    fileList.append([False,name,id])
3045            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3046        if skip:
3047            return fileList,Source
3048        else:
3049            return fileList
3050           
3051    def OnDataDelete(self, event):
3052        '''Delete one or more histograms from data tree. Called by the
3053        Data/DeleteData menu
3054        '''
3055        TextList = []
3056        DelList = []
3057        DelItemList = []
3058        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
3059        ifPWDR = False
3060        ifSASD = False
3061        ifIMG = False
3062        ifHKLF = False
3063        ifPDF = False
3064        if self.PatternTree.GetCount():
3065            item, cookie = self.PatternTree.GetFirstChild(self.root)
3066            while item:
3067                name = self.PatternTree.GetItemText(item)
3068                if name not in ['Notebook','Controls','Covariance','Constraints',
3069                    'Restraints','Phases','Rigid bodies','Sequential results']:
3070                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
3071                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
3072                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
3073                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
3074                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
3075                    TextList.append(name)
3076                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3077            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
3078            try:
3079                if dlg.ShowModal() == wx.ID_OK:
3080                    result = dlg.GetSelections()
3081                    for i in result: DelList.append(TextList[i])
3082                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3083                    while item:
3084                        itemName = self.PatternTree.GetItemText(item)
3085                        if itemName in DelList:
3086                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
3087                            elif 'SASD' in itemName: nItems['SASD'] -= 1
3088                            elif 'IMG' in itemName: nItems['IMG'] -= 1
3089                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
3090                            elif 'PDF' in itemName: nItems['PDF'] -= 1
3091                            DelItemList.append(item)
3092                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3093                    for item in DelItemList:
3094                        self.PatternTree.Delete(item)
3095                    self.PickId = 0
3096                    self.PickIdText = None
3097                    self.PatternId = 0
3098                    if nItems['PWDR']:
3099                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3100                    else:
3101                        self.G2plotNB.Delete('Powder Patterns')
3102                    if not nItems['IMG']:
3103                        self.G2plotNB.Delete('2D Powder Image')
3104                    if not nItems['HKLF']:
3105                        self.G2plotNB.Delete('Structure Factors')
3106                        if '3D Structure Factors' in self.G2plotNB.plotList:
3107                            self.G2plotNB.Delete('3D Structure Factors')
3108            finally:
3109                dlg.Destroy()
3110
3111    def OnFileOpen(self, event, filename=None):
3112        '''Gets a GSAS-II .gpx project file in response to the
3113        File/Open Project menu button
3114        '''
3115        result = wx.ID_OK
3116        self.EnablePlot = False
3117        if self.PatternTree.GetChildrenCount(self.root,False):
3118            if self.dataFrame:
3119                self.dataFrame.Clear() 
3120            dlg = wx.MessageDialog(
3121                self,
3122                'Do you want to overwrite the current project? '+
3123                'Any unsaved changes in current project will be lost. Press OK to continue.',
3124                'Overwrite?',  wx.OK | wx.CANCEL)
3125            try:
3126                result = dlg.ShowModal()
3127                if result == wx.ID_OK:
3128                    self.PatternTree.DeleteChildren(self.root)
3129                    self.GSASprojectfile = ''
3130                    self.HKL = []
3131                    if self.G2plotNB.plotList:
3132                        self.G2plotNB.clear()
3133            finally:
3134                dlg.Destroy()
3135        if result != wx.ID_OK: return
3136
3137        if not filename:
3138            if self.dataDisplay: self.dataDisplay.Destroy()
3139            if self.LastGPXdir:
3140                pth = self.LastGPXdir
3141            else:
3142                pth = '.'
3143            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3144                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3145            try:
3146                if dlg.ShowModal() != wx.ID_OK: return
3147                self.GSASprojectfile = dlg.GetPath()
3148                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3149                self.dirname = dlg.GetDirectory()
3150            finally:
3151                dlg.Destroy()
3152        else:
3153            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3154            self.dirname = os.path.split(filename)[0]
3155
3156        try:
3157            self.StartProject()         #open the file if possible
3158        except:
3159            print '\nError opening file ',filename
3160            import traceback
3161            print traceback.format_exc()
3162       
3163    def StartProject(self):
3164        '''Opens a GSAS-II project file & selects the 1st available data set to
3165        display (PWDR, HKLF or SASD)
3166        '''
3167       
3168        Id = 0
3169        G2IO.ProjFileOpen(self)
3170        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3171        self.PatternTree.Expand(self.root)
3172        self.HKL = []
3173        item, cookie = self.PatternTree.GetFirstChild(self.root)
3174        while item and not Id:
3175            name = self.PatternTree.GetItemText(item)
3176            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3177                Id = item
3178            elif name == 'Controls':
3179                data = self.PatternTree.GetItemPyData(item)
3180                if data:
3181                    for item in self.Refine: item.Enable(True)
3182                    self.EnableSeqRefineMenu()
3183            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3184        if Id:
3185            self.EnablePlot = True
3186            self.PatternTree.SelectItem(Id)
3187        self.CheckNotebook()
3188        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3189        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3190        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3191        self.LastGPXdir = pth
3192
3193    def OnFileClose(self, event):
3194        '''Clears the data tree in response to the
3195        File/New Project menu button. User is given option to save
3196        the project.
3197        '''
3198        if self.dataFrame:
3199            self.dataFrame.Clear()
3200            self.dataFrame.SetLabel('GSAS-II data display') 
3201        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3202        try:
3203            result = dlg.ShowModal()
3204            if result == wx.ID_OK:
3205                self.OnFileSaveMenu(event)
3206            if result != wx.ID_CANCEL:
3207                self.GSASprojectfile = ''
3208                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3209                self.PatternTree.DeleteChildren(self.root)
3210                if self.HKL: self.HKL = []
3211                if self.G2plotNB.plotList:
3212                    self.G2plotNB.clear()
3213        finally:
3214            dlg.Destroy()
3215
3216    def OnFileSave(self, event):
3217        '''Save the current project in response to the
3218        File/Save Project menu button
3219        '''
3220       
3221        if self.GSASprojectfile: 
3222            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3223            self.CheckNotebook()
3224            G2IO.ProjFileSave(self)
3225        else:
3226            self.OnFileSaveas(event)
3227
3228    def OnFileSaveas(self, event):
3229        '''Save the current project in response to the
3230        File/Save as menu button
3231        '''
3232        if GSASIIpath.GetConfigValue('Starting_directory'):
3233            pth = GSASIIpath.GetConfigValue('Starting_directory')
3234            pth = os.path.expanduser(pth) 
3235        elif self.LastGPXdir:
3236            pth = self.LastGPXdir
3237        else:
3238            pth = '.'
3239        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3240            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3241        try:
3242            if dlg.ShowModal() == wx.ID_OK:
3243                self.GSASprojectfile = dlg.GetPath()
3244                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3245                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3246                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3247                self.CheckNotebook()
3248                G2IO.ProjFileSave(self)
3249                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3250        finally:
3251            dlg.Destroy()
3252
3253    def ExitMain(self, event):
3254        '''Called if the main window is closed'''
3255        if self.G2plotNB:
3256            self.G2plotNB.Destroy()
3257        if self.dataFrame:
3258            self.dataFrame.Clear() 
3259            self.dataFrame.Destroy()
3260        if self.undofile:
3261            os.remove(self.undofile)
3262        sys.exit()
3263       
3264    def OnFileExit(self, event):
3265        '''Called in response to the File/Quit menu button'''
3266        if self.G2plotNB:
3267            self.G2plotNB.Destroy()
3268        if self.dataFrame:
3269            self.dataFrame.Clear() 
3270            self.dataFrame.Destroy()
3271        self.Close()
3272       
3273    def OnExportPeakList(self,event):
3274        nptand = lambda x: np.tan(x*math.pi/180.)
3275        pth = G2G.GetExportPath(self)
3276        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3277            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3278        try:
3279            if dlg.ShowModal() == wx.ID_OK:
3280                self.peaklistfile = dlg.GetPath()
3281                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3282                file = open(self.peaklistfile,'w')               
3283                item, cookie = self.PatternTree.GetFirstChild(self.root)
3284                while item:
3285                    name = self.PatternTree.GetItemText(item)
3286                    if 'PWDR' in name:
3287                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3288                        wave = 0.0
3289                        while item2:
3290                            name2 = self.PatternTree.GetItemText(item2)
3291                            if name2 == 'Instrument Parameters':
3292                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3293                                Type = Inst['Type'][0]
3294                                if 'T' not in Type:
3295                                    wave = G2mth.getWave(Inst)
3296                            elif name2 == 'Peak List':
3297                                pkdata = self.PatternTree.GetItemPyData(item2)
3298                                peaks = pkdata['peaks']
3299                                sigDict = pkdata['sigDict']
3300                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3301                        file.write("#%s \n" % (name+' Peak List'))
3302                        if wave:
3303                            file.write('#wavelength = %10.6f\n'%(wave))
3304                        if 'T' in Type:
3305                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3306                        else:
3307                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3308                        for ip,peak in enumerate(peaks):
3309                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3310                            if 'T' in Type:  #TOF - more cols
3311                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3312                                for name in esds.keys():
3313                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3314                                sig = np.sqrt(peak[8])
3315                                gam = peak[10]
3316                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3317                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3318                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3319                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3320                            else:               #CW
3321                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3322                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3323                                for name in esds.keys():
3324                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3325                                sig = np.sqrt(peak[4]) #var -> sig
3326                                gam = peak[6]
3327                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3328                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3329                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3330                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3331                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3332                file.close()
3333        finally:
3334            dlg.Destroy()
3335       
3336    def OnExportHKL(self,event):
3337        pth = G2G.GetExportPath(self)
3338        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3339            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3340        try:
3341            if dlg.ShowModal() == wx.ID_OK:
3342                self.peaklistfile = dlg.GetPath()
3343                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3344                file = open(self.peaklistfile,'w')               
3345                item, cookie = self.PatternTree.GetFirstChild(self.root)
3346                while item:
3347                    name = self.PatternTree.GetItemText(item)
3348                    if 'PWDR' in name:
3349                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3350                        while item2:
3351                            name2 = self.PatternTree.GetItemText(item2)
3352                            if name2 == 'Reflection Lists':
3353                                data = self.PatternTree.GetItemPyData(item2)
3354                                phases = data.keys()
3355                                for phase in phases:
3356                                    peaks = data[phase]
3357                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3358                                    if 'T' in peaks.get('Type','PXC'):
3359                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3360                                    else:               
3361                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3362                                    for peak in peaks['RefList']:
3363                                        if 'T' in peaks.get('Type','PXC'):
3364                                            sig = np.sqrt(peak[6])
3365                                            gam = peak[7]
3366                                            FWHM = G2pwd.getgamFW(gam,sig)
3367                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3368                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3369                                        else:
3370                                            sig = np.sqrt(peak[6])
3371                                            gam = peak[7]
3372                                            FWHM = G2pwd.getgamFW(gam,sig)
3373                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3374                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3375                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3376                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3377                file.close()
3378        finally:
3379            dlg.Destroy()
3380       
3381    def OnExportPDF(self,event):
3382        #need S(Q) and G(R) to be saved here - probably best from selection?
3383        names = ['All']
3384        exports = []
3385        item, cookie = self.PatternTree.GetFirstChild(self.root)
3386        while item:
3387            name = self.PatternTree.GetItemText(item)
3388            if 'PDF' in name:
3389                names.append(name)
3390            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3391        if names:
3392            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3393            if dlg.ShowModal() == wx.ID_OK:
3394                sel = dlg.GetSelections()
3395                if sel[0] == 0:
3396                    exports = names[1:]
3397                else:
3398                    for x in sel:
3399                        exports.append(names[x])
3400            dlg.Destroy()
3401        if exports:
3402            G2IO.PDFSave(self,exports)
3403       
3404    def OnMakePDFs(self,event):
3405        '''Calculates PDFs
3406        '''
3407        sind = lambda x: math.sin(x*math.pi/180.)
3408        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3409        TextList = []
3410        PDFlist = []
3411        Names = []
3412        if self.PatternTree.GetCount():
3413            id, cookie = self.PatternTree.GetFirstChild(self.root)
3414            while id:
3415                name = self.PatternTree.GetItemText(id)
3416                Names.append(name)
3417                if 'PWDR' in name:
3418                    TextList.append(name)
3419                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3420            if len(TextList) == 1:
3421                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3422                return
3423            dlg = G2G.G2MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3424            try:
3425                if dlg.ShowModal() == wx.ID_OK:
3426                    PDFlist = [TextList[i] for i in dlg.GetSelections()]
3427                    for item in PDFlist:
3428                        PWDRname = item[4:]
3429                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3430                        Data = {
3431                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3432                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3433                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3434                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3435                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3436                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3437                            'Lorch':True,'BackRatio':0.0}
3438                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3439                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3440                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3441                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3442                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3443                for item in self.ExportPDF: item.Enable(True)
3444            finally:
3445                dlg.Destroy()
3446               
3447    def GetPWDRdatafromTree(self,PWDRname):
3448        ''' Returns powder data from GSASII tree
3449
3450        :param str PWDRname: a powder histogram name as obtained from
3451          :meth:`GSASIIstruct.GetHistogramNames`
3452
3453        :returns: PWDRdata = powder data dictionary with
3454          Powder data arrays, Limits, Instrument Parameters,
3455          Sample Parameters           
3456        '''
3457        PWDRdata = {}
3458        try:
3459            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3460        except ValueError:
3461            PWDRdata['wtFactor'] = 1.0
3462        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3463        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3464        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3465        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3466        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3467        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3468        if 'ranId' not in PWDRdata:  # patch, add a random Id
3469            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3470        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3471            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3472        return PWDRdata
3473
3474    def GetHKLFdatafromTree(self,HKLFname):
3475        ''' Returns single crystal data from GSASII tree
3476
3477        :param str HKLFname: a single crystal histogram name as obtained
3478          from
3479          :meth:`GSASIIstruct.GetHistogramNames`
3480
3481        :returns: HKLFdata = single crystal data list of reflections
3482
3483        '''
3484        HKLFdata = {}
3485        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3486#        try:
3487#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3488#        except ValueError:
3489#            HKLFdata['wtFactor'] = 1.0
3490        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3491        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3492        return HKLFdata
3493       
3494    def GetPhaseData(self):
3495        '''Returns a dict with defined phases.
3496        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3497        get same info from GPX file.
3498        '''
3499        phaseData = {}
3500        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3501            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3502        else:
3503            print 'no phases found in GetPhaseData'
3504            sub = None
3505        if sub:
3506            item, cookie = self.PatternTree.GetFirstChild(sub)
3507            while item:
3508                phaseName = self.PatternTree.GetItemText(item)
3509                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3510                if 'ranId' not in phaseData[phaseName]:
3511                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3512                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3513        return phaseData
3514
3515    def GetPhaseInfofromTree(self):
3516        '''Get the phase names and their rId values,
3517        also the histograms used in each phase.
3518
3519        :returns: (phaseRIdList, usedHistograms) where
3520
3521          * phaseRIdList is a list of random Id values for each phase
3522          * usedHistograms is a dict where the keys are the phase names
3523            and the values for each key are a list of the histogram names
3524            used in each phase.
3525        '''
3526        phaseRIdList = []
3527        usedHistograms = {}
3528        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3529        if sub:
3530            item, cookie = self.PatternTree.GetFirstChild(sub)
3531            while item:
3532                phaseName = self.PatternTree.GetItemText(item)
3533                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3534                if ranId: phaseRIdList.append(ranId)
3535                data = self.PatternTree.GetItemPyData(item)
3536                UseList = data['Histograms']
3537                usedHistograms[phaseName] = UseList.keys()
3538                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3539        return phaseRIdList,usedHistograms
3540
3541    def GetPhaseNames(self):
3542        '''Returns a list of defined phases.
3543        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3544        get same info from GPX file.
3545        '''
3546        phaseNames = []
3547        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3548            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3549        else:
3550            print 'no phases found in GetPhaseNames'
3551            sub = None
3552        if sub:
3553            item, cookie = self.PatternTree.GetFirstChild(sub)
3554            while item:
3555                phase = self.PatternTree.GetItemText(item)
3556                phaseNames.append(phase)
3557                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3558        return phaseNames
3559   
3560    def GetHistogramNames(self,hType):
3561        """ Returns a list of histogram names found in the GSASII data tree
3562        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3563        get same info from GPX file.
3564       
3565        :param str hType: list of histogram types
3566        :return: list of histogram names
3567       
3568        """
3569        HistogramNames = []
3570        if self.PatternTree.GetCount():
3571            item, cookie = self.PatternTree.GetFirstChild(self.root)
3572            while item:
3573                name = self.PatternTree.GetItemText(item)
3574                if name[:4] in hType:
3575                    HistogramNames.append(name)       
3576                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3577
3578        return HistogramNames
3579                   
3580    def GetUsedHistogramsAndPhasesfromTree(self):
3581        ''' Returns all histograms that are found in any phase
3582        and any phase that uses a histogram.
3583        This also assigns numbers to used phases and histograms by the
3584        order they appear in the file.
3585        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3586        get same info from GPX file.
3587
3588        :returns: (Histograms,Phases)
3589
3590            * Histograms = dictionary of histograms as {name:data,...}
3591            * Phases = dictionary of phases that use histograms
3592        '''
3593        Histograms = {}
3594        Phases = {}
3595        phaseNames = self.GetPhaseNames()
3596        phaseData = self.GetPhaseData()
3597        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3598
3599        for phase in phaseData:
3600            Phase = phaseData[phase]
3601            pId = phaseNames.index(phase)
3602            Phase['pId'] = pId
3603            if Phase['Histograms']:
3604                if phase not in Phases:
3605                    Phases[phase] = Phase
3606                for hist in Phase['Histograms']:
3607                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3608                        Phase['Histograms'][hist]['Use'] = True         
3609                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3610                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3611                        if item:
3612                            if 'PWDR' in hist[:4]: 
3613                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3614                            elif 'HKLF' in hist[:4]:
3615                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3616                            hId = histoList.index(hist)
3617                            Histograms[hist]['hId'] = hId
3618                        else: # would happen if a referenced histogram were renamed or deleted
3619                            print('For phase "'+str(phase)+
3620                                  '" unresolved reference to histogram "'+str(hist)+'"')
3621        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3622        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3623        return Histograms,Phases
3624       
3625    def MakeLSParmDict(self):
3626        '''Load all parameters used for computation from the tree into a
3627        dict of paired values [value, refine flag]. Note that this is
3628        different than the parmDict used in the refinement, which only has
3629        values.
3630
3631        Note that similar things are done in
3632        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3633        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3634        a GPX file).
3635
3636        :returns: (parmDict,varyList) where:
3637
3638         * parmDict is a dict with values and refinement flags
3639           for each parameter and
3640         * varyList is a list of variables (refined parameters).
3641        '''
3642        parmDict = {}
3643        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3644        for phase in Phases:
3645            if 'pId' not in Phases[phase]:
3646                self.ErrorDialog('View parameter error','You must run least squares at least once')
3647                raise Exception,'No pId for phase '+str(phase)
3648        rigidbodyDict = self.PatternTree.GetItemPyData(   
3649            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3650        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3651        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3652        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3653        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3654        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3655        varyList = rbVary+phaseVary+hapVary+histVary
3656        parmDict.update(rbDict)
3657        parmDict.update(phaseDict)
3658        parmDict.update(hapDict)
3659        parmDict.update(histDict)
3660        for parm in parmDict:
3661            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3662                'Omega','Chi','Phi','nDebye','nPeaks']:
3663                parmDict[parm] = [parmDict[parm],'-']
3664            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3665                parmDict[parm] = [parmDict[parm],'-']
3666            elif parm in varyList:
3667                parmDict[parm] = [parmDict[parm],'T']
3668            else:
3669                parmDict[parm] = [parmDict[parm],'F']
3670        # for i in parmDict: print i,'\t',parmDict[i]
3671        # fl = open('parmDict.dat','wb')
3672        # import cPickle
3673        # cPickle.dump(parmDict,fl,1)
3674        # fl.close()
3675        return parmDict,varyList
3676
3677    def ShowLSParms(self,event):
3678        '''Displays a window showing all parameters in the refinement.
3679        Called from the Calculate/View LS Parms menu.
3680        '''
3681        parmDict,varyList = self.MakeLSParmDict()
3682        parmValDict = {}
3683        for i in parmDict:
3684            parmValDict[i] = parmDict[i][0]
3685           
3686        reqVaryList = tuple(varyList) # save requested variables
3687        try:
3688            # process constraints
3689            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3690            Constraints = self.PatternTree.GetItemPyData(sub)
3691            constList = []
3692            for item in Constraints:
3693                if item.startswith('_'): continue
3694                constList += Constraints[item]
3695            G2mv.InitVars()
3696            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3697            groups,parmlist = G2mv.GroupConstraints(constrDict)
3698            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3699            G2mv.Map2Dict(parmValDict,varyList)
3700        except:
3701            pass
3702        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3703        dlg.ShowModal()
3704        dlg.Destroy()
3705       
3706    def OnRefine(self,event):
3707        '''Perform a refinement.
3708        Called from the Calculate/Refine menu.
3709        '''       
3710        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3711        if Id:
3712            dlg = wx.MessageDialog(
3713                self,
3714                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3715                'Remove sequential results?',wx.OK|wx.CANCEL)
3716            if dlg.ShowModal() == wx.ID_OK:
3717                self.PatternTree.Delete(Id)
3718                dlg.Destroy()
3719            else:
3720                dlg.Destroy()
3721                return
3722        self.OnFileSave(event)
3723        # check that constraints are OK here
3724        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3725        if errmsg:
3726            self.ErrorDialog('Refinement error',errmsg)
3727            return
3728        if warnmsg:
3729            print('Conflict between refinment flag settings and constraints:\n'+
3730                warnmsg+'\nRefinement not possible')
3731            self.ErrorDialog('Refinement Flag Error',
3732                'Conflict between refinement flag settings and constraints:\n'+
3733                warnmsg+'\nRefinement not possible')
3734            return
3735        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3736            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3737            parent=self)
3738        Size = dlg.GetSize()
3739        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3740            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3741        dlg.CenterOnParent()
3742        Rw = 100.00
3743        oldId =  self.PatternTree.GetSelection()        #retain current selection
3744        oldPath = self.GetTreeItemsList(oldId)
3745        parentName = ''
3746        oldName = self.PatternTree.GetItemText(oldId)
3747        parentId = self.PatternTree.GetItemParent(oldId)
3748        if parentId:
3749            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3750            if 'Phases' in parentName:
3751                tabId = self.dataDisplay.GetSelection()
3752        try:
3753            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)    #Msg is Rvals dict if Ok=True
3754        finally:
3755            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3756            dlg.Destroy()
3757            wx.Yield()
3758        if OK:
3759            Rw = Msg['Rwp']
3760            lamMax = Msg.get('lamMax',0.001)
3761            text = 'Load new result?'
3762            if lamMax >= 10.:
3763                text += '\nWARNING: Steepest descents dominates;'+   \
3764                ' minimum may not have been reached\nor result may be false minimum.'+  \
3765                ' You should reconsider your parameter suite'
3766            dlg2 = wx.MessageDialog(self,text,'Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3767            try:
3768                if dlg2.ShowModal() == wx.ID_OK:
3769                    Id = 0
3770                    self.PatternTree.DeleteChildren(self.root)
3771                    self.HKL = []
3772                    G2IO.ProjFileOpen(self,False)
3773                    Id =  self.root
3774                    txt = None
3775                    for txt in oldPath:
3776                        Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3777                    self.PickIdText = None  #force reload of page
3778                    if Id:
3779                        self.PickId = Id
3780                        self.PatternTree.SelectItem(Id)
3781                        G2gd.MovePatternTreeToGrid(self,Id) # reload current tree item, should update current plot
3782            finally:
3783                dlg2.Destroy()
3784        else:
3785            self.ErrorDialog('Refinement error',Msg)
3786
3787    def OnSeqRefine(self,event):
3788        '''Perform a sequential refinement.
3789        Called from the Calculate/Sequential refine menu.
3790        '''       
3791        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3792        if not Id:
3793            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3794            self.PatternTree.SetItemPyData(Id,{})           
3795        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3796        Controls['ShowCell'] = True
3797        self.OnFileSave(event)
3798        # check that constraints are OK here
3799        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3800        if errmsg:
3801            self.ErrorDialog('Refinement error',errmsg)
3802            return
3803        if warnmsg:
3804            print('Conflict between refinment flag settings and constraints:\n'+
3805                  warnmsg+'\nRefinement not possible')
3806            self.ErrorDialog('Refinement Flag Error',
3807                             'Conflict between refinment flag settings and constraints:\n'+
3808                             warnmsg+'\nRefinement not possible')
3809            return
3810        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3811            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3812            parent=self)           
3813        Size = dlg.GetSize()
3814        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3815            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3816        dlg.CenterOnParent()
3817        try:
3818            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)     #Msg is Rvals dict if Ok=True
3819        finally:
3820            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3821            dlg.Destroy()
3822            wx.Yield()
3823        if OK:
3824            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3825            try:
3826                if dlg.ShowModal() == wx.ID_OK:
3827                    Id = 0
3828#                    self.G2plotNB.setReplotFlags() # mark all plots as old - doesn't exist!
3829                    self.PickIdText = None  #force reload of PickId contents
3830                    self.PatternTree.DeleteChildren(self.root)
3831                    if len(self.HKL): self.HKL = []
3832                    if self.G2plotNB.plotList:
3833                        self.G2plotNB.clear()
3834                    G2IO.ProjFileOpen(self,False)
3835                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3836                    self.PatternTree.SelectItem(Id)
3837                    G2gd.MovePatternTreeToGrid(self,Id) # reload current tree item, should update current plot
3838#                    self.G2plotNB.replotAll() # refresh any plots not yet updated - doesn't exist!
3839            finally:
3840                dlg.Destroy()
3841        else:
3842            self.ErrorDialog('Sequential refinement error',Msg)
3843       
3844    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3845        'Display an error message'
3846        result = None
3847        if parent is None:
3848            dlg = wx.MessageDialog(self, message, title,  wtype)
3849        else:
3850            dlg = wx.MessageDialog(parent, message, title,  wtype)
3851            dlg.CenterOnParent() # not working on Mac
3852        try:
3853            result = dlg.ShowModal()
3854        finally:
3855            dlg.Destroy()
3856        return result
3857   
3858    def OnSaveMultipleImg(self,event):
3859        '''Select and save multiple image parameter and mask files
3860        '''
3861        G2IO.SaveMultipleImg(self)
3862       
3863class GSASIImain(wx.App):
3864    '''Defines a wxApp for GSAS-II
3865
3866    Creates a wx frame (self.main) which contains the display of the
3867    data tree.
3868    '''
3869    def OnInit(self):
3870        '''Called automatically when the app is created.'''
3871        import platform
3872        if '2.7' not in sys.version[:5]:
3873            dlg = wx.MessageDialog(None, 
3874                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
3875                'Python version error',  wx.OK)
3876            try:
3877                result = dlg.ShowModal()
3878            finally:
3879                dlg.Destroy()
3880            sys.exit()
3881        self.main = GSASII(None)
3882        self.main.Show()
3883        self.SetTopWindow(self.main)
3884        # save the current package versions
3885        self.main.PackageVersions = []
3886        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
3887        for p in (wx,mpl,np,sp,ogl):
3888            self.main.PackageVersions.append([p.__name__,p.__version__])
3889        try:
3890            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
3891        except:
3892            try:
3893                from PIL import PILLOW_VERSION
3894                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
3895            except:
3896                pass
3897        self.main.PackageVersions.append([' Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
3898       
3899        return True
3900    # def MacOpenFile(self, filename):
3901    #     '''Called on Mac every time a file is dropped on the app when it is running,
3902    #     treat this like a File/Open project menu action.
3903    #     Should be ignored on other platforms
3904    #     '''
3905    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3906    #     print 'MacOpen',filename
3907    #     if os.path.splitext(filename)[1] == '.py': return
3908    #     # end PATCH
3909    #     self.main.OnFileOpen(None,filename)
3910    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3911       
3912def main():
3913    '''Start up the GSAS-II application'''
3914    #application = GSASIImain() # don't redirect output, someday we
3915    # may want to do this if we can
3916    application = GSASIImain(0)
3917    if GSASIIpath.GetConfigValue('wxInspector'):
3918        import wx.lib.inspection as wxeye
3919        wxeye.InspectionTool().Show()
3920
3921    #application.main.OnRefine(None)
3922    application.MainLoop()
3923   
3924if __name__ == '__main__':
3925    # print versions
3926    print "Python module versions loaded:"
3927    print "python:     ",sys.version.split()[0]
3928    print "wxpython:   ",wx.__version__
3929    print "matplotlib: ",mpl.__version__
3930    print "numpy:      ",np.__version__
3931    print "scipy:      ",sp.__version__
3932    print "OpenGL:     ",ogl.__version__
3933    try:
3934        from PIL import Image
3935        try:
3936            from PIL import PILLOW_VERSION
3937            version = PILLOW_VERSION
3938        except:
3939            version = Image.VERSION
3940        print "pillow:     ",version
3941    except ImportError:
3942        try:
3943            import Image
3944            print "Image (PIL):",Image.VERSION
3945        except ImportError:
3946            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3947    try:
3948        import mkl
3949        print "Max threads ",mkl.get_max_threads()
3950    except:
3951        pass
3952    import platform
3953    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3954    #print "wxPython description",wx.PlatformInfo
3955    print "This is GSAS-II version:     ",__version__,' revision #'+str(GSASIIpath.GetVersionNumber())
3956    GSASIIpath.InvokeDebugOpts()
3957    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.