source: trunk/GSASII.py @ 2449

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

Fix CIF exports; make sure exporters are imported only once (on Mac)

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