source: trunk/GSASII.py @ 2509

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

fixes to help

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