source: trunk/GSASII.py @ 2550

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

fix binary import for Bruker raw files
fix issues for new atoms in mag structures

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