source: trunk/GSASII.py @ 2360

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

new Mac .so files, incorporate data corrections into .instprm file

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