source: trunk/GSASII.py @ 2498

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

set Refine flag after linking hist & phase in import

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