source: trunk/GSASII.py @ 2479

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

Rework plotting to refresh after refinements

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