source: trunk/GSASII.py @ 1077

Last change on this file since 1077 was 1077, checked in by toby, 8 years ago

cleanup plot & svn bugs; set missing keywords; CIF export done; update docs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 118.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2013-10-03 18:11:47 +0000 (Thu, 03 Oct 2013) $
6# $Author: toby $
7# $Revision: 1077 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1077 2013-10-03 18:11:47Z toby $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32import wx.lib.inspection as wxeye
33try:
34    import OpenGL as ogl
35except ImportError:
36    print('*******************************************************')
37    print('PyOpenGL is missing from your python installation')
38    print('     - we will try to install it')
39    print('*******************************************************')
40    def install_with_easyinstall(package):
41        try: 
42            print "trying a system-wide PyOpenGl install"
43            easy_install.main(['-f',os.path.split(__file__)[0],package])
44            return
45        except:
46            pass
47        try: 
48            print "trying a user level PyOpenGl install"
49            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
50            return
51        except:
52            print "Install of '+package+' failed. Please report this information:"
53            import traceback
54            print traceback.format_exc()
55            sys.exit()
56    from setuptools.command import easy_install
57    install_with_easyinstall('PyOpenGl')
58    print('*******************************************************')         
59    print('OpenGL has been installed. Please restart GSAS-II')
60    print('*******************************************************')         
61    sys.exit()
62
63# load the GSAS routines
64import GSASIIpath
65GSASIIpath.SetVersionNumber("$Revision: 1077 $")
66import GSASIIIO as G2IO
67import GSASIIgrid as G2gd
68import GSASIIplot as G2plt
69import GSASIIpwd as G2pwd
70import GSASIIpwdGUI as G2pdG
71import GSASIIspc as G2spc
72import GSASIIstrMain as G2stMn
73import GSASIIstrIO as G2stIO
74import GSASIImapvars as G2mv
75import GSASIIsolve as G2sol
76
77#wx inspector - use as needed
78wxInspector = False
79
80# print versions
81print "Python module versions loaded:"
82print "python:     ",sys.version[:5]
83print "wxpython:   ",wx.__version__
84print "matplotlib: ",mpl.__version__
85print "numpy:      ",np.__version__
86print "scipy:      ",sp.__version__
87print "OpenGL:     ",ogl.__version__
88try:
89    import mkl
90    print "Max threads ",mkl.get_max_threads()
91except:
92    pass
93#    print "MKL module not present"
94__version__ = '0.2.0'
95G2gd.__version__ = __version__
96print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
97
98# useful degree trig functions
99sind = lambda x: math.sin(x*math.pi/180.)
100cosd = lambda x: math.cos(x*math.pi/180.)
101tand = lambda x: math.tan(x*math.pi/180.)
102asind = lambda x: 180.*math.asin(x)/math.pi
103acosd = lambda x: 180.*math.acos(x)/math.pi
104atan2d = lambda x,y: 180.*math.atan2(y,x)/math.pi
105
106def create(parent):
107    return GSASII(parent)
108
109class GSASII(wx.Frame):
110    '''Define the main GSAS-II frame and its associated menu items
111    '''
112    def _Add_FileMenuItems(self, parent):
113        item = parent.Append(
114            help='Open a gsasii project file (*.gpx)', id=wx.ID_ANY,
115            kind=wx.ITEM_NORMAL,text='&Open project...')
116        self.Bind(wx.EVT_MENU, self.OnFileOpen, id=item.GetId())
117        item = parent.Append(
118            help='Save project to old file', id=wx.ID_ANY,
119            kind=wx.ITEM_NORMAL,text='&Save project')
120        self.Bind(wx.EVT_MENU, self.OnFileSave, id=item.GetId())
121        item = parent.Append(
122            help='Save project to new file', id=wx.ID_ANY,
123            kind=wx.ITEM_NORMAL,text='Save As...')
124        self.Bind(wx.EVT_MENU, self.OnFileSaveas, id=item.GetId())
125        item = parent.Append(
126            help='Close project, saving is optional', id=wx.ID_ANY,
127            kind=wx.ITEM_NORMAL,text='&Close project')
128        self.Bind(wx.EVT_MENU, self.OnFileClose, id=item.GetId())
129        item = parent.Append(
130            help='Exit from gsasii', id=wx.ID_ANY,
131            kind=wx.ITEM_NORMAL,text='&Exit')
132        self.Bind(wx.EVT_MENU, self.OnFileExit, id=item.GetId())
133       
134    def _Add_DataMenuItems(self,parent):
135        item = parent.Append(
136            help='',id=wx.ID_ANY,
137            kind=wx.ITEM_NORMAL,
138            text='Read image data...')
139        self.Bind(wx.EVT_MENU, self.OnImageRead, id=item.GetId())
140        item = parent.Append(
141            help='',id=wx.ID_ANY,
142            kind=wx.ITEM_NORMAL,
143            text='Read Powder Pattern Peaks...')
144        self.Bind(wx.EVT_MENU, self.OnReadPowderPeaks, id=item.GetId())
145        item = parent.Append(
146            help='',id=wx.ID_ANY,
147            kind=wx.ITEM_NORMAL,
148            text='Sum powder data')
149        self.Bind(wx.EVT_MENU, self.OnPwdrSum, id=item.GetId())
150        item = parent.Append(
151            help='',id=wx.ID_ANY,
152            kind=wx.ITEM_NORMAL,
153            text='Sum image data')
154        self.Bind(wx.EVT_MENU, self.OnImageSum, id=item.GetId())
155        item = parent.Append(
156            help='',id=wx.ID_ANY,
157            kind=wx.ITEM_NORMAL,
158            text='Add phase')
159        self.Bind(wx.EVT_MENU, self.OnAddPhase, id=item.GetId())
160        item = parent.Append(
161            help='',id=wx.ID_ANY,
162            kind=wx.ITEM_NORMAL,
163            text='Delete phase')
164        self.Bind(wx.EVT_MENU, self.OnDeletePhase, id=item.GetId())
165        item = parent.Append(
166            help='',id=wx.ID_ANY,
167            kind=wx.ITEM_NORMAL,
168            text='Rename data') 
169        self.Bind(wx.EVT_MENU, self.OnRenameData, id=item.GetId())
170        item = parent.Append(
171            help='',id=wx.ID_ANY,
172            kind=wx.ITEM_NORMAL,
173            text='Delete data')
174        self.Bind(wx.EVT_MENU, self.OnDataDelete, id=item.GetId())
175               
176    def _Add_CalculateMenuItems(self,parent):
177        item = parent.Append(help='Make new PDFs from selected powder patterns', 
178            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='Make new PDFs')
179        self.MakePDF.append(item)
180#        item.Enable(False)
181        self.Bind(wx.EVT_MENU, self.OnMakePDFs, id=item.GetId())
182       
183        item = parent.Append(help='View least squares parameters', 
184            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='&View LS parms')
185        self.Bind(wx.EVT_MENU, self.OnViewLSParms, id=item.GetId())
186       
187        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
188            text='&Refine')
189        if len(self.Refine): # extend state for new menus to match main (on mac)
190            state = self.Refine[0].IsEnabled()
191        else:
192            state = False
193        item.Enable(state)
194        self.Refine.append(item)
195        self.Bind(wx.EVT_MENU, self.OnRefine, id=item.GetId())
196       
197        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
198            text='Sequental refine')
199        if len(self.SeqRefine): # extend state for new menus to match main (on mac)
200            state = self.SeqRefine[0].IsEnabled()
201        else:
202            state = False
203        item.Enable(state)
204        self.SeqRefine.append(item)
205        self.Bind(wx.EVT_MENU, self.OnSeqRefine, id=item.GetId())
206       
207    def _init_Imports(self):
208        '''import all the G2phase*.py & G2sfact*.py & G2pwd*.py files that
209        are found in the path
210        '''
211
212        self.ImportPhaseReaderlist = []
213        self._init_Import_routines('phase',self.ImportPhaseReaderlist,'Phase')
214        self.ImportSfactReaderlist = []
215        self._init_Import_routines('sfact',self.ImportSfactReaderlist,'Struct_Factor')
216        self.ImportPowderReaderlist = []
217        self._init_Import_routines('pwd',self.ImportPowderReaderlist,'Powder_Data')
218        self.ImportMenuId = {}
219
220    def _init_Import_routines(self,prefix,readerlist,errprefix):
221        '''import all the import readers matching the prefix
222        '''
223        #path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file
224        #pathlist = sys.path[:]
225        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
226        #path2GSAS2 = os.path.join(
227        #    os.path.dirname(os.path.realpath(__file__)), # location of this file
228        #    'imports')
229        pathlist = sys.path[:]
230        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
231
232        filelist = []
233        for path in pathlist:
234            for filename in glob.iglob(os.path.join(
235                path,
236                "G2"+prefix+"*.py")):
237                filelist.append(filename)   
238                #print 'debug: found',filename
239        filelist = sorted(list(set(filelist))) # remove duplicates
240        for filename in filelist:
241            path,rootname = os.path.split(filename)
242            pkg = os.path.splitext(rootname)[0]
243            try:
244                fp = None
245                fp, fppath,desc = imp.find_module(pkg,[path,])
246                pkg = imp.load_module(pkg,fp,fppath,desc)
247                for clss in inspect.getmembers(pkg): # find classes defined in package
248                    if clss[0].startswith('_'): continue
249                    if inspect.isclass(clss[1]):
250                        # check if we have the required methods
251                        for m in 'Reader','ExtensionValidator','ContentsValidator':
252                            if not hasattr(clss[1],m): break
253                            if not callable(getattr(clss[1],m)): break
254                        else:
255                            reader = clss[1]() # create an import instance
256                            readerlist.append(reader)
257            except AttributeError:
258                print 'Import_'+errprefix+': Attribute Error'+str(filename)
259                pass
260            except ImportError:
261                print 'Import_'+errprefix+': Error importing file'+str(filename)
262                pass
263            if fp: fp.close()
264
265    def OnImportGeneric(self,reader,readerlist,label,multiple=False):
266        '''Used to import Phases, powder dataset or single
267        crystal datasets (structure factor tables) using reader objects
268        subclassed from GSASIIIO.ImportPhase, GSASIIIO.ImportStructFactor
269        or GSASIIIO.ImportPowderData. If a reader is specified, only
270        that will be attempted, but if no reader is specified, every one
271        that is potentially compatible (by file extension) will
272        be tried on the selected file(s).
273
274        :param readerobject reader: This will be a reference to
275          a particular object to be used to read a file or None,
276          if every appropriate reader should be used.
277
278        :param list readerlist: a list of reader objects appropriate for
279          the current read attempt. At present, this will be either
280          self.ImportPhaseReaderlist, self.ImportSfactReaderlist or
281          self.ImportPowderReaderlist (defined in _init_Imports from
282          the files found in the path), but in theory this list could
283          be tailored. Used only when reader is None.
284
285        :param str label: string to place on the open file dialog:
286          Open `label` input file
287
288        :param bool multiple: True if multiple files can be selected
289          in the file dialog. False is default. At present True is used
290          only for reading of powder data.
291         
292        :returns: a list of reader objects (rd_list) that were able
293          to read the specified file(s). This list may be empty.
294        '''
295        self.lastimport = ''
296        self.zipfile = None
297        if reader is None:
298            multiple = False
299            #print "use all formats"
300            choices = "any file (*.*)|*.*"
301            choices += "|zip archive (.zip)|*.zip"
302            extdict = {}
303            # compile a list of allowed extensions
304            for rd in readerlist:
305                fmt = rd.formatName
306                for extn in rd.extensionlist:
307                    if not extdict.get(extn): extdict[extn] = []
308                    extdict[extn] += [fmt,]
309            for extn in sorted(extdict.keys(),cmp=lambda x,y: cmp(x.lower(), y.lower())):
310                fmt = ''
311                for f in extdict[extn]:
312                    if fmt != "": fmt += ', '
313                    fmt += f
314                choices += "|" + fmt + " file (*" + extn + ")|*" + extn
315        else:
316            readerlist = [reader,]
317            # compile a list of allowed extensions
318            choices = reader.formatName + " file ("
319            w = ""
320            for extn in reader.extensionlist:
321                if w != "": w += ";"
322                w += "*" + extn
323            choices += w + ")|" + w
324            choices += "|zip archive (.zip)|*.zip"
325            if not reader.strictExtension:
326                choices += "|any file (*.*)|*.*"
327        # get the file(s)
328        if multiple:
329            mode = style=wx.OPEN | wx.CHANGE_DIR | wx.MULTIPLE
330        else:
331            mode = style=wx.OPEN | wx.CHANGE_DIR
332        dlg = wx.FileDialog(self, message="Choose "+label+" input file",
333            defaultFile="",wildcard=choices, style=mode)
334        try:
335            if dlg.ShowModal() == wx.ID_OK:
336                if multiple:
337                    filelist = dlg.GetPaths()
338                    if len(filelist) == 0: return []
339                else:
340                    filename = dlg.GetPath()
341                    filelist = [filename,]
342                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
343            else: # cancel was pressed
344                return []
345        finally:
346            dlg.Destroy()
347        rd_list = []
348        filelist1 = []
349        for filename in filelist:
350            # is this a zip file?
351            if os.path.splitext(filename)[1].lower() == '.zip':
352                extractedfiles = G2IO.ExtractFileFromZip(
353                    filename,parent=self,
354                    multipleselect=True)
355                if extractedfiles is None: continue # error or Cancel
356                if extractedfiles != filename:
357                    self.zipfile = filename # save zip name
358                    filelist1 += extractedfiles
359                    continue
360            filelist1.append(filename)
361        filelist = filelist1
362        for filename in filelist:
363            # is this a zip file?
364            if os.path.splitext(filename)[1].lower() == '.zip':
365                extractedfile = G2IO.ExtractFileFromZip(filename,parent=self)
366                if extractedfile is None: continue # error or Cancel
367                if extractedfile != filename:
368                    filename,self.zipfile = extractedfile,filename # now use the file that was created
369            # set what formats are compatible with this file
370            primaryReaders = []
371            secondaryReaders = []
372            for r in readerlist:
373                flag = r.ExtensionValidator(filename)
374                if flag is None: 
375                    secondaryReaders.append(r)
376                elif flag:
377                    primaryReaders.append(r)
378            if len(secondaryReaders) + len(primaryReaders) == 0:
379                self.ErrorDialog('No Format','No matching format for file '+filename)
380                return []
381
382            fp = None
383            msg = ''
384            try:
385                fp = open(filename,'Ur')
386                if len(filelist) == 1:
387                    # confirm we have the right file
388                    rdmsg = 'File '+str(filename)+' begins:\n\n'
389                    for i in range(3):
390                        rdmsg += fp.readline()
391                    rdmsg += '\n\nDo you want to read this file?'
392                    if not all([ord(c) < 128 and ord(c) != 0 for c in rdmsg]): # show only if ASCII
393                        rdmsg = 'File '+str(
394                            filename)+' is a binary file. Do you want to read this file?'
395                    result = wx.ID_NO
396                    # it would be better to use something that
397                    # would resize better, but this will do for now
398                    dlg = wx.MessageDialog(
399                        self, rdmsg,
400                        'Is this the file you want?', 
401                        wx.YES_NO | wx.ICON_QUESTION,
402                        )
403                    dlg.SetSize((700,300)) # does not resize on Mac
404                    try:
405                        result = dlg.ShowModal()
406                    finally:
407                        dlg.Destroy()
408                    if result == wx.ID_NO: return []
409                           
410                self.lastimport = filename
411                # try the file first with Readers that specify the
412                # files extension and later with ones that allow it
413                flag = False
414                for rd in primaryReaders+secondaryReaders:
415                    try:
416                        fp.seek(0)  # rewind
417                        if not rd.ContentsValidator(fp): continue # rejected on cursory check
418                        repeat = True
419                        rdbuffer = {} # create temporary storage for file reader
420                        block = 0
421                        while repeat:
422                            block += 1
423                            repeat = False
424                            fp.seek(0)  # rewind
425                            rd.objname = os.path.basename(filename)
426                            flag = rd.Reader(filename,fp,self,
427                                             buffer=rdbuffer,
428                                             blocknum=block)
429                            if flag:
430                                rd_list.append(copy.deepcopy(rd)) # success
431                                if rd.repeat: repeat = True
432                    except:
433                        import traceback
434                        print traceback.format_exc()
435                        msg += '\nError reading file '+filename+' with format '+ rd.formatName
436                        #self.ErrorDialog('Read Error',
437                        #                 'Error reading file '+filename
438                        #                 +' with format '+ rd.formatName)
439                        continue
440                    if flag:
441                        if rd.warnings:
442                            self.ErrorDialog('Read Warning','The '+ rd.formatName+
443                                             ' reader reported a warning message:\n\n'+
444                                             rd.warnings)
445                        break # success reading
446                else:
447                    if reader:
448                        self.ErrorDialog('Read Error','The '+ rd.formatName+
449                                         ' reader was not able to read file '+filename+msg)
450                    else:
451                        self.ErrorDialog('Read Error','No reader is able to read file '+filename+msg)
452            except:
453                import traceback
454                print traceback.format_exc()
455                self.ErrorDialog('Open Error','Error on open of file '+filename)
456            if fp: fp.close()
457        return rd_list
458
459    def _Add_ImportMenu_Phase(self,parent):
460        '''configure the Import Phase menus accord to the readers found in _init_Imports
461        '''
462        submenu = wx.Menu()
463        item = parent.AppendMenu(wx.ID_ANY, 'Phase',
464            submenu, help='Import phase data')
465        for reader in self.ImportPhaseReaderlist:
466            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
467                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
468            self.ImportMenuId[item.GetId()] = reader
469            self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
470        item = submenu.Append(wx.ID_ANY,
471                              help='Import phase data, use file to try to determine format',
472                              kind=wx.ITEM_NORMAL,
473                              text='guess format from file')
474        self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
475
476    def OnImportPhase(self,event):
477        '''Called in response to an Import/Phase/... menu item
478        to read phase information.
479        dict self.ImportMenuId is used to look up the specific
480        reader item associated with the menu item, which will be
481        None for the last menu item, which is the "guess" option
482        where all appropriate formats will be tried.
483        '''
484        # look up which format was requested
485        reqrdr = self.ImportMenuId.get(event.GetId())
486        rdlist = self.OnImportGeneric(reqrdr,
487                                  self.ImportPhaseReaderlist,
488                                  'phase')
489        if len(rdlist) == 0: return
490        # for now rdlist is only expected to have one element
491        # but this will allow multiple phases to be imported
492        self.CheckNotebook()
493        for rd in rdlist:
494            dlg = wx.TextEntryDialog( # allow editing of phase name
495                self, 'Enter the name for the new phase',
496                'Edit phase name', rd.Phase['General']['Name'],
497                style=wx.OK)
498            dlg.CenterOnParent()
499            if dlg.ShowModal() == wx.ID_OK:
500                rd.Phase['General']['Name'] = dlg.GetValue()
501            dlg.Destroy()
502            PhaseName = rd.Phase['General']['Name']
503            print 'Read phase '+str(PhaseName)+' from file '+str(self.lastimport)
504            if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
505                sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
506            else:
507                sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
508            psub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
509            self.PatternTree.SetItemPyData(psub,rd.Phase)
510            self.PatternTree.Expand(self.root) # make sure phases are seen
511            self.PatternTree.Expand(sub) 
512            self.PatternTree.Expand(psub) 
513        return # success
514       
515    def _Add_ImportMenu_Sfact(self,parent):
516        '''configure the Import Structure Factor menus accord to the readers found in _init_Imports
517        '''
518        submenu = wx.Menu()
519        item = parent.AppendMenu(wx.ID_ANY, 'Structure Factor',
520            submenu, help='Import Structure Factor data')
521        for reader in self.ImportSfactReaderlist:
522            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,               
523                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
524            self.ImportMenuId[item.GetId()] = reader
525            self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
526        item = submenu.Append(wx.ID_ANY,
527            help='Import Structure Factor, use file to try to determine format',
528            kind=wx.ITEM_NORMAL,
529            text='guess format from file')
530        self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
531
532    def OnImportSfact(self,event):
533        '''Called in response to an Import/Structure Factor/... menu item
534        to read single crystal datasets.
535        dict self.ImportMenuId is used to look up the specific
536        reader item associated with the menu item, which will be
537        None for the last menu item, which is the "guess" option
538        where all appropriate formats will be tried.
539        '''
540        # look up which format was requested
541        reqrdr = self.ImportMenuId.get(event.GetId())
542        rdlist = self.OnImportGeneric(reqrdr,self.ImportSfactReaderlist,
543            'Structure Factor')
544        if len(rdlist) == 0: return
545        self.CheckNotebook()
546        for rd in rdlist:
547            HistName = rd.objname
548            if len(rdlist) <= 2: 
549                dlg = wx.TextEntryDialog( # allow editing of Structure Factor name
550                    self, 'Enter the name for the new Structure Factor',
551                    'Edit Structure Factor name', HistName,
552                    style=wx.OK)
553                dlg.CenterOnParent()
554                if dlg.ShowModal() == wx.ID_OK:
555                    HistName = dlg.GetValue()
556                dlg.Destroy()
557            print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
558            Id = self.PatternTree.AppendItem(parent=self.root,
559                                             text='HKLF '+HistName)
560            self.PatternTree.SetItemPyData(Id,[{'wtFactor':1.0,'Dummy':False},rd.RefList])
561            Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
562            self.PatternTree.SetItemPyData(Sub,rd.Parameters)
563            self.PatternTree.SetItemPyData(
564                self.PatternTree.AppendItem(Id,text='HKL Plot Controls'),
565                rd.Controls)
566            self.PatternTree.SetItemPyData(
567                self.PatternTree.AppendItem(Id,text='Reflection List'),[])  #dummy entry for GUI use
568            self.PatternTree.SelectItem(Id)
569            self.PatternTree.Expand(Id)
570            self.Sngl = Id
571        return # success
572
573    def _Add_ImportMenu_powder(self,parent):
574        '''configure the Powder Data menus accord to the readers found in _init_Imports
575        '''
576        submenu = wx.Menu()
577        item = parent.AppendMenu(wx.ID_ANY, 'Powder Data',
578            submenu, help='Import Powder data')
579        for reader in self.ImportPowderReaderlist:
580            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
581                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
582            self.ImportMenuId[item.GetId()] = reader
583            self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
584        item = submenu.Append(wx.ID_ANY,
585            help='Import powder data, use file to try to determine format',
586            kind=wx.ITEM_NORMAL,text='guess format from file')
587        self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
588           
589    def ReadPowderInstprm(self,instfile):       #fix the write routine for [inst1,inst2] style
590        '''Read a GSAS-II (new) instrument parameter file
591
592        :param str instfile: name of instrument parameter file
593
594        '''
595        if os.path.splitext(instfile)[1].lower() != '.instprm': # invalid file
596            return None           
597        if not os.path.exists(instfile): # no such file
598            return None
599        File = open(instfile,'r')
600        S = File.readline()
601        if not S.startswith('#GSAS-II'): # not a valid file
602            File.close()
603            return None
604        newItems = []
605        newVals = []
606        while S:
607            if S[0] == '#':
608                S = File.readline()
609                continue
610            [item,val] = S[:-1].split(':')
611            newItems.append(item)
612            try:
613                newVals.append(float(val))
614            except ValueError:
615                newVals.append(val)                       
616            S = File.readline()               
617        File.close()
618        # add a second MT dict here. TOF parms? (BHT)
619        return G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}
620       
621    def ReadPowderIparm(self,instfile,bank,databanks,rd):
622        '''Read a GSAS (old) instrument parameter file
623
624        :param str instfile: name of instrument parameter file
625
626        :param int bank: the bank number read in the raw data file
627
628        :param int databanks: the number of banks in the raw data file.
629          If the number of banks in the data and instrument parameter files
630          agree, then the sets of banks are assumed to match up and bank
631          is used to select the instrument parameter file. If not, the user
632          is asked to make a selection.
633
634        :param obj rd: the raw data (histogram) data object. This
635          sets rd.instbank.
636
637        '''
638        if not os.path.exists(instfile): # no such file
639            return {}
640        fp = 0
641        try:
642            fp = open(instfile,'Ur')
643            Iparm = {}
644            for S in fp:
645                Iparm[S[:12]] = S[12:-1]
646        except IOError:
647            print('Error reading file:'+str(instfile))
648        if fp:       
649            fp.close()
650
651        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
652        hType = Iparm['INS   HTYPE '].strip()
653        if ibanks == 1: # there is only one bank here, return it
654            rd.instbank = 1
655            return Iparm
656        if 'PNT' in hType:
657            rd.instbank = bank
658        elif ibanks != databanks:
659            # number of banks in data and prm file not not agree, need a
660            # choice from a human here
661            choices = []
662            for i in range(1,1+ibanks):
663                choices.append('Bank '+str(i))
664            bank = rd.BlockSelector(
665                choices, self,
666                title='Select an instrument parameter bank for '+
667                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
668                '\nOr use Cancel to select from the default parameter sets',
669                header='Block Selector')
670        if bank is None: return {}
671        # pull out requested bank # bank from the data, and change the bank to 1
672        IparmS = {}
673        for key in Iparm:
674            if key[4:6] == "  ":
675                IparmS[key] = Iparm[key]
676            elif int(key[4:6].strip()) == bank:
677                IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
678        rd.instbank = bank
679        return IparmS
680                       
681    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
682        '''Open and read an instrument parameter file for a data file
683        Returns the list of parameters used in the data tree
684
685        :param obj rd: the raw data (histogram) data object.
686
687        :param str prevIparm: not used
688
689        :param str lastIparmfile: Name of last instrument parameter
690          file that was read, or a empty string.
691
692        :param str lastdatafile: Name of last data file that was read.
693
694        :returns: a list of two dicts, the first containing instrument parameters
695          and the second used for future TOF datasets (timemaps?)
696
697        '''
698        def SetPowderInstParms(Iparm, rd):
699            '''extracts values from instrument parameters in rd.instdict
700            or in array Iparm.
701            Create and return the contents of the instrument parameter tree entry.
702            '''
703            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
704            # override inst values with values read from data file
705            if rd.instdict.get('type'):
706                DataType = rd.instdict.get('type')
707            data = [DataType,]
708            instname = Iparm.get('INS  1INAME ')
709            if instname:
710                rd.Sample['InstrName'] = instname.strip()
711            if 'C' in DataType:
712                wave1 = None
713                wave2 = 0.0
714                if rd.instdict.get('wave'):
715                    wl = rd.instdict.get('wave')
716                    wave1 = wl[0]
717                    if len(wl) > 1: wave2 = wl[1]
718                s = Iparm['INS  1 ICONS']
719                if not wave1:
720                    wave1 = G2IO.sfloat(s[:10])
721                    wave2 = G2IO.sfloat(s[10:20])
722                v = (wave1,wave2,
723                     G2IO.sfloat(s[20:30]),G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
724                if not v[1]:
725                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
726                    v = (v[0],v[2],v[4])
727                    codes = [0,0,0,0]
728                else:
729                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
730                    codes = [0,0,0,0,0,0]
731                data.extend(v)
732                v1 = Iparm['INS  1PRCF1 '].split()                                                 
733                v = Iparm['INS  1PRCF11'].split()
734                data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
735                azm = float(Iparm.get('INS  1DETAZM','0.0'))
736                v = Iparm['INS  1PRCF12'].split()
737                if v1[0] == 3:
738                    data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
739                else:
740                    data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
741                codes.extend([0,0,0,0,0,0,0])
742                return [G2IO.makeInstDict(names,data,codes),{}]
743            elif 'T' in DataType:
744                names = ['Type','2-theta','difC','difA','Zero','alpha','beta-0','beta-1',
745                    'beta-q','sig-0','sig-1','sig-q','X','Y','Azimuth']
746                codes = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
747                azm = 0.
748                if 'INS  1DETAZM' in Iparm:
749                    azm = float(Iparm['INS  1DETAZM'])
750                s = Iparm['INS  1BNKPAR'].split()
751                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
752                s = Iparm['INS  1 ICONS'].split()
753                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])    #difC, difA, Zero
754                s = Iparm['INS  1PRCF1 '].split()
755                pfType = int(s[0])
756                s = Iparm['INS  1PRCF11'].split()
757                if abs(pfType) == 1:
758                    data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])])
759                    s = Iparm['INS  1PRCF12'].split()
760                    data.extend([0.0,0.0,G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])
761                elif abs(pfType) in [3,4,5]:
762                    data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])
763                    if abs(pfType) == 4:
764                        data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,azm])
765                    else:
766                        s = Iparm['INS  1PRCF12'].split()
767                        data.extend([0.0,0.0,G2IO.sfloat(s[0]),0.0,0.0,0.0,azm])                       
768                Inst1 = G2IO.makeInstDict(names,data,codes)
769                Inst2 = {}
770                if pfType < 0:
771                    Ipab = 'INS  1PAB'+str(-pfType)
772                    Npab = int(Iparm[Ipab+'  '].strip())
773                    Inst2['Pdabc'] = []
774                    for i in range(Npab):
775                        k = Ipab+str(i+1).rjust(2)
776                        s = Iparm[k].split()
777                        Inst2['Pdabc'].append([float(t) for t in s])
778                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
779                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
780                if 'INS  1I ITYP' in Iparm:
781                    s = Iparm['INS  1I ITYP'].split()
782                    Ityp = int(s[0])
783                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
784                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
785                    if Ityp in [1,2,4]:
786                        Inst2['Itype'] = Itypes[Ityp-1]
787                        Inst2['Tminmax'] = Tminmax
788                        Icoeff = []
789                        Iesd = []
790                        Icovar = []                   
791                        for i in range(3):
792                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
793                            Icoeff += [float(S) for S in s]
794                            s = Iparm['INS  1IECOF'+str(i+1)].split()
795                            Iesd += [float(S) for S in s]
796                        for i in range(8):
797                            s = Iparm['INS  1IECOR'+str(i+1)].split()
798                            Icovar += [float(S) for S in s]
799                        Inst2['Icoeff'] = Icoeff
800                        Inst2['Iesd'] = Iesd
801                        Inst2['Icovar'] = Icovar
802                return [Inst1,Inst2]
803
804        # stuff we might need from the reader
805        filename = rd.powderentry[0]
806        bank = rd.powderentry[2]
807        numbanks = rd.numbanks
808        # is there an instrument parameter file defined for the current data set?
809        if rd.instparm or (lastdatafile == filename and lastIparmfile):
810            if rd.instparm:
811                instfile = os.path.join(os.path.split(filename)[0],
812                                    rd.instparm)
813            else:
814                # for multiple reads of one data file, reuse the inst parm file
815                instfile = lastIparmfile
816            if os.path.exists(instfile):
817                #print 'debug: try read',instfile
818                instParmList = self.ReadPowderInstprm(instfile)
819                if instParmList is not None:
820                    rd.instfile = instfile
821                    rd.instmsg = 'GSAS-II file '+instfile
822                    return instParmList
823                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
824                if Iparm:
825                    #print 'debug: success'
826                    rd.instfile = instfile
827                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
828                    return SetPowderInstParms(Iparm,rd)
829            else:
830                self.ErrorDialog('Open Error','Error opening instrument parameter file '
831                    +str(instfile)+' requested by file '+ filename)
832        # is there an instrument parameter file matching the current file
833        # with extension .inst or .prm? If so read it
834        basename = os.path.splitext(filename)[0]
835        for ext in '.instprm','.prm','.inst','.ins':
836            instfile = basename + ext
837            instParmList = self.ReadPowderInstprm(instfile)
838            if instParmList is not None:
839                rd.instfile = instfile
840                rd.instmsg = 'GSAS-II file '+instfile
841                return instParmList
842            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
843            if Iparm:
844                #print 'debug: success'
845                rd.instfile = instfile
846                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
847                return SetPowderInstParms(Iparm,rd)
848            else:
849                #print 'debug: open/read failed',instfile
850                pass # fail silently
851
852        # did we read the data file from a zip? If so, look there for a
853        # instrument parameter file
854        if self.zipfile:
855            for ext in '.instprm','.prm','.inst','.ins':
856                instfile = G2IO.ExtractFileFromZip(
857                    self.zipfile,
858                    selection=os.path.split(basename + ext)[1],
859                    parent=self)
860                if instfile is not None and instfile != self.zipfile:
861                    print 'debug:',instfile,'created from ',self.zipfile
862                    instParmList = self.ReadPowderInstprm(instfile)
863                    if instParmList is not None:
864                        rd.instfile = instfile
865                        rd.instmsg = 'GSAS-II file '+instfile
866                        return instParmList
867                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
868                    if Iparm:
869                        rd.instfile = instfile
870                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
871                        return SetPowderInstParms(Iparm,rd)
872                    else:
873                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
874                        pass # fail silently
875
876        while True: # loop until we get a file that works or we get a cancel
877            instfile = ''
878            dlg = wx.FileDialog(
879                self,
880                'Choose inst. param file for "'
881                +rd.idstring
882                +'" (or Cancel for default)',
883                '.', '',
884                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
885                'GSAS-II iparm file (*.instprm)|*.instprm|'
886                'All files (*.*)|*.*', 
887                wx.OPEN|wx.CHANGE_DIR)
888            if os.path.exists(lastIparmfile):
889                dlg.SetFilename(lastIparmfile)
890            if dlg.ShowModal() == wx.ID_OK:
891                instfile = dlg.GetPath()
892            dlg.Destroy()
893            if not instfile: break
894            instParmList = self.ReadPowderInstprm(instfile)
895            if instParmList is not None:
896                rd.instfile = instfile
897                rd.instmsg = 'GSAS-II file '+instfile
898                return instParmList
899            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
900            if Iparm:
901                #print 'debug: success with',instfile
902                rd.instfile = instfile
903                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
904                return SetPowderInstParms(Iparm,rd)
905            else:
906                self.ErrorDialog('Read Error',
907                                 'Error opening/reading file '+str(instfile))
908       
909        # still no success: offer user choice of defaults
910        while True: # loop until we get a choice
911            choices = []
912            head = 'Select from default instrument parameters for '+rd.idstring
913
914            for l in rd.defaultIparm_lbl:
915                choices.append('Defaults for '+l)
916            res = rd.BlockSelector(
917                choices,
918                ParentFrame=self,
919                title=head,
920                header='Select default inst parms',
921                useCancel=False)
922            if res is None: continue
923            rd.instfile = ''
924            rd.instmsg = 'default: '+rd.defaultIparm_lbl[res]
925            return SetPowderInstParms(rd.defaultIparms[res],rd)
926
927    def OnImportPowder(self,event):
928        '''Called in response to an Import/Powder Data/... menu item
929        to read a powder diffraction data set.
930        dict self.ImportMenuId is used to look up the specific
931        reader item associated with the menu item, which will be
932        None for the last menu item, which is the "guess" option
933        where all appropriate formats will be tried.
934
935        Also reads an instrument parameter file for each dataset.
936        '''
937        reqrdr = self.ImportMenuId.get(event.GetId())  # look up which format was requested
938        rdlist = self.OnImportGeneric(
939            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
940        if len(rdlist) == 0: return
941        self.CheckNotebook()
942        Iparm = None
943        lastIparmfile = ''
944        lastdatafile = ''
945        for rd in rdlist:
946            # get instrument parameters for each dataset
947            Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
948            if rd.repeat_instparm: 
949                lastIparmfile = rd.instfile
950            lastdatafile = rd.powderentry[0]
951            print 'Read powder data '+str(rd.idstring)+ \
952                ' from file '+str(self.lastimport) + \
953                ' with parameters from '+str(rd.instmsg)
954            # data are read, now store them in the tree
955            Id = self.PatternTree.AppendItem(parent=self.root,
956                text='PWDR '+rd.idstring)
957            if 'T' in Iparm1['Type'][0]:
958                if not rd.clockWd and rd.GSAS:
959                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
960                cw = np.diff(rd.powderdata[0])
961                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
962                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
963                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
964                if 'Itype' in Iparm2:
965                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
966                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
967                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
968                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
969                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
970                    var = 1./rd.powderdata[2][Ibeg:Ifin]
971                    var += WYI*rd.powderdata[1]**2
972                    var /= YI**2
973                    rd.powderdata[2] = 1./var
974                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
975                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
976                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
977            Tmin = min(rd.powderdata[0])
978            Tmax = max(rd.powderdata[0])
979            self.PatternTree.SetItemPyData(Id,[{'wtFactor':1.0,'Dummy':False},rd.powderdata])
980            self.PatternTree.SetItemPyData(
981                self.PatternTree.AppendItem(Id,text='Comments'),
982                rd.comments)
983            self.PatternTree.SetItemPyData(
984                self.PatternTree.AppendItem(Id,text='Limits'),
985                [(Tmin,Tmax),[Tmin,Tmax]])
986            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
987            self.PatternTree.SetItemPyData(
988                self.PatternTree.AppendItem(Id,text='Background'),
989                [['chebyschev',True,3,1.0,0.0,0.0],
990                 {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
991            self.PatternTree.SetItemPyData(
992                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
993                [Iparm1,Iparm2])
994            self.PatternTree.SetItemPyData(
995                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
996                rd.Sample)
997            self.PatternTree.SetItemPyData(
998                self.PatternTree.AppendItem(Id,text='Peak List')
999                ,[])
1000            self.PatternTree.SetItemPyData(
1001                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1002                [])
1003            self.PatternTree.SetItemPyData(
1004                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1005                [])
1006            self.PatternTree.SetItemPyData(
1007                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1008                {})
1009            self.PatternTree.Expand(Id)
1010        self.PatternTree.SelectItem(Id)
1011        return # success
1012
1013    def _init_Exports(self,menu):
1014        '''This is a place holder for when exports are handled in a manner similar to imports
1015        '''
1016#        submenu = wx.Menu()
1017#        item = menu.AppendMenu(
1018#            wx.ID_ANY, 'entire project',
1019#            submenu, help='Export entire project')
1020
1021        # for now hard-code CIF testing here
1022        item = menu.Append(
1023            wx.ID_ANY,
1024            help='full CIF file includes powder/reflection data',
1025            kind=wx.ITEM_NORMAL,
1026            text='full CIF file')
1027        self.Bind(wx.EVT_MENU, self.OnTestCIF, id=item.GetId())
1028        item = menu.Append(
1029            wx.ID_ANY,
1030            help='quick CIF file with no data, no distance/angle table',
1031            kind=wx.ITEM_NORMAL,
1032            text='quick CIF file')
1033        self.Bind(wx.EVT_MENU, self.OnTestCIF, id=item.GetId())
1034
1035    def OnTestCIF(self,event):
1036        # hard-code CIF testing here
1037       
1038        # get the menu command on Windows and Linux
1039        menu = self.ExportMenu.FindItemById(event.GetId())
1040        mode = 'full'
1041        if menu:
1042            if 'quick' in menu.GetLabel():
1043                mode = 'simple'
1044        else: # this works on the Mac
1045            try: 
1046                if 'quick' in event.EventObject.GetLabelText(event.Id):
1047                    mode = 'simple'
1048            except:
1049                pass
1050        #path2GSAS2 = os.path.join(
1051        #    os.path.dirname(os.path.realpath(__file__)), # location of this file
1052        #    'exports')
1053        #if path2GSAS2 not in sys.path: sys.path.append(path2GSAS2)
1054        #reload(G2IO)
1055        import G2cif
1056        reload(G2cif)
1057        exp = G2cif.ExportCIF(self)
1058        sexp = G2cif.ExportSimpleCIF(self)
1059        if mode == 'full':
1060            exp.export()
1061        else:
1062            sexp.export()
1063
1064    def _Add_ExportMenuItems(self,parent):
1065        item = parent.Append(
1066            help='Select PWDR item to enable',id=wx.ID_ANY,
1067            kind=wx.ITEM_NORMAL,
1068            text='Export Powder Patterns...')
1069        self.ExportPattern.append(item)
1070        item.Enable(False)
1071        self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1072
1073        item = parent.Append(
1074            help='',id=wx.ID_ANY,
1075            kind=wx.ITEM_NORMAL,
1076            text='Export All Peak Lists...')
1077        self.ExportPeakList.append(item)
1078        item.Enable(True)
1079        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1080
1081        item = parent.Append(
1082            help='',id=wx.ID_ANY,
1083            kind=wx.ITEM_NORMAL,
1084            text='Export HKLs...')
1085        self.ExportHKL.append(item)
1086        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1087
1088        item = parent.Append(
1089            help='Select PDF item to enable',
1090            id=wx.ID_ANY,
1091            kind=wx.ITEM_NORMAL,
1092            text='Export PDF...')
1093        self.ExportPDF.append(item)
1094        item.Enable(False)
1095        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1096
1097        item = parent.Append(
1098            help='',id=wx.ID_ANY,
1099            kind=wx.ITEM_NORMAL,
1100            text='Export Phase...')
1101        self.ExportPhase.append(item)
1102        item.Enable(False)
1103        self.Bind(wx.EVT_MENU, self.OnExportPhase, id=item.GetId())
1104
1105#        item = parent.Append(
1106#            help='',id=wx.ID_ANY,
1107#            kind=wx.ITEM_NORMAL,
1108#            text='Export CIF...')
1109#        self.ExportCIF.append(item)
1110#        item.Enable(False)
1111#        self.Bind(wx.EVT_MENU, self.OnExportCIF, id=item.GetId())
1112               
1113    def FillMainMenu(self,menubar):
1114        '''Define contents of the main GSAS-II menu for the (main) data tree window
1115        in the mac, used also for the data item windows as well.
1116        '''
1117        File = wx.Menu(title='')
1118        menubar.Append(menu=File, title='&File')
1119        self._Add_FileMenuItems(File)
1120        Data = wx.Menu(title='')
1121        menubar.Append(menu=Data, title='Data')
1122        self._Add_DataMenuItems(Data)
1123        Calculate = wx.Menu(title='')       
1124        menubar.Append(menu=Calculate, title='&Calculate')
1125        self._Add_CalculateMenuItems(Calculate)
1126        Import = wx.Menu(title='')       
1127        menubar.Append(menu=Import, title='Import')
1128        self._Add_ImportMenu_Phase(Import)
1129        self._Add_ImportMenu_powder(Import)
1130        self._Add_ImportMenu_Sfact(Import)
1131        self.ExportMenu = wx.Menu(title='')
1132        menubar.Append(menu=self.ExportMenu, title='Export')
1133        self._init_Exports(self.ExportMenu)
1134        self._Add_ExportMenuItems(self.ExportMenu)
1135        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1136            morehelpitems=[('&Tutorials','Tutorials')])
1137        menubar.Append(menu=HelpMenu,title='&Help')
1138
1139    def _init_ctrls(self, parent):
1140        wx.Frame.__init__(self, name='GSASII', parent=parent,
1141            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1142        clientSize = wx.ClientDisplayRect()
1143        Size = self.GetSize()
1144        xPos = clientSize[2]-Size[0]
1145        self.SetPosition(wx.Point(xPos,clientSize[1]))
1146        self._init_Imports()
1147        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1148        self.MakePDF = []
1149        self.Refine = []
1150        self.SeqRefine = []
1151        self.ExportPattern = []
1152        self.ExportPeakList = []
1153        self.ExportHKL = []
1154        self.ExportPDF = []
1155        self.ExportPhase = []
1156        self.ExportCIF = []
1157        #
1158        self.GSASIIMenu = wx.MenuBar()
1159        self.FillMainMenu(self.GSASIIMenu)
1160        self.SetMenuBar(self.GSASIIMenu)
1161        self.Bind(wx.EVT_SIZE, self.OnSize)
1162        self.CreateStatusBar()
1163        self.mainPanel = wx.Panel(self,-1)
1164       
1165        wxID_PATTERNTREE = wx.NewId()
1166        self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE,
1167            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1168        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,
1169            self.OnPatternTreeSelChanged, id=wxID_PATTERNTREE)
1170        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1171            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1172        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1173            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1174        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1175            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1176        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1177            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1178        self.root = self.PatternTree.AddRoot('Loaded Data: ')
1179       
1180        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1181            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1182        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1183        plotFrame.Show()
1184       
1185        self.dataDisplay = None
1186       
1187    def __init__(self, parent):
1188        self._init_ctrls(parent)
1189        self.Image = wx.Image(
1190            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
1191            wx.BITMAP_TYPE_ICO)
1192        if "wxMSW" in wx.PlatformInfo:
1193            img = self.Image.Scale(16, 16).ConvertToBitmap()
1194        elif "wxGTK" in wx.PlatformInfo:
1195            img = self.Image.Scale(22, 22).ConvertToBitmap()
1196        else:
1197            img = self.Image.ConvertToBitmap()
1198        self.SetIcon(wx.IconFromBitmap(img))
1199        self.Bind(wx.EVT_CLOSE, self.ExitMain)
1200        # various defaults
1201        self.oldFocus = None
1202        self.GSASprojectfile = ''
1203        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
1204        self.undofile = ''
1205        self.TreeItemDelete = False
1206        self.Offset = [0.0,0.0]
1207        self.delOffset = .02
1208        self.refOffset = -100.0
1209        self.refDelt = .01
1210        self.Weight = False
1211        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
1212        self.IfPlot = False
1213        self.PatternId = 0
1214        self.PickId = 0
1215        self.PeakTable = []
1216        self.LimitsTable = []
1217        self.HKL = []
1218        self.Lines = []
1219        self.itemPicked = None
1220        self.dataFrame = None
1221        self.Interpolate = 'nearest'
1222        self.ContourColor = 'Paired'
1223        self.VcovColor = 'RdYlGn'
1224        self.RamaColor = 'Blues'
1225        self.Projection = 'equal area'
1226        self.logPlot = False
1227        self.qPlot = False
1228        self.Contour = False
1229        self.Legend = False
1230        self.SinglePlot = False
1231        self.SubBack = False
1232        self.plotView = 0
1233        self.Image = 0
1234        self.oldImagefile = ''
1235        self.ImageZ = []
1236        self.Integrate = 0
1237        self.imageDefault = {}
1238        self.Sngl = 0
1239        self.ifGetRing = False
1240        self.setPoly = False
1241        arg = sys.argv
1242        if len(arg) > 1:
1243            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1244            self.dirname = os.path.dirname(arg[1])
1245            if self.dirname: os.chdir(self.dirname)
1246            try:
1247                G2IO.ProjFileOpen(self)
1248                self.PatternTree.Expand(self.root)
1249                for item in self.Refine: item.Enable(True)
1250                for item in self.SeqRefine: item.Enable(True)
1251            except:
1252                print 'Error opening file',arg[1]
1253
1254    def OnSize(self,event):
1255        'Called when the main window is resized. Not sure why'
1256        w,h = self.GetClientSizeTuple()
1257        self.mainPanel.SetSize(wx.Size(w,h))
1258        self.PatternTree.SetSize(wx.Size(w,h))
1259                       
1260    def OnPatternTreeSelChanged(self, event):
1261        '''Called when a data tree item is selected'''
1262        if self.TreeItemDelete:
1263            self.TreeItemDelete = False
1264        else:
1265            pltNum = self.G2plotNB.nb.GetSelection()
1266            if pltNum >= 0:                         #to avoid the startup with no plot!
1267                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1268                pltPlot = pltPage.figure
1269            item = event.GetItem()
1270            G2gd.MovePatternTreeToGrid(self,item)
1271            if self.oldFocus:
1272                self.oldFocus.SetFocus()
1273       
1274    def OnPatternTreeItemCollapsed(self, event):
1275        'Called when a tree item is collapsed'
1276        event.Skip()
1277
1278    def OnPatternTreeItemExpanded(self, event):
1279        'Called when a tree item is expanded'
1280        event.Skip()
1281       
1282    def OnPatternTreeItemDelete(self, event):
1283        'Called when a tree item is deleted -- not sure what this does'
1284        self.TreeItemDelete = True
1285
1286    def OnPatternTreeItemActivated(self, event):
1287        'Called when a tree item is activated'
1288        event.Skip()
1289       
1290    def OnPatternTreeKeyDown(self,event):
1291        'Not sure what this does'
1292        key = event.GetKeyCode()
1293        item = self.PickId
1294        if type(item) is int: return # is this the toplevel in tree?
1295        if key == wx.WXK_UP:
1296            self.oldFocus = wx.Window.FindFocus()
1297            self.PatternTree.GetPrevSibling(item)
1298        elif key == wx.WXK_DOWN:
1299            self.oldFocus = wx.Window.FindFocus()
1300            self.PatternTree.GetNextSibling(item)
1301               
1302    def OnReadPowderPeaks(self,event):
1303        'Bound to menu Data/Read Powder Peaks -- still needed?'
1304        Cuka = 1.54052
1305        self.CheckNotebook()
1306        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
1307            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
1308        try:
1309            if dlg.ShowModal() == wx.ID_OK:
1310                self.HKL = []
1311                self.powderfile = dlg.GetPath()
1312                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
1313                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
1314                data = ['PKS',Cuka,0.0]
1315                names = ['Type','Lam','Zero'] 
1316                codes = [0,0,0]
1317                inst = [G2IO.makeInstDict(names,data,codes),{}]
1318                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1319                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
1320                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),peaks)
1321                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1322                self.PatternTree.Expand(Id)
1323                self.PatternTree.SelectItem(Id)
1324                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1325        finally:
1326            dlg.Destroy()
1327           
1328    def OnImageRead(self,event):
1329        'Called to read in an image in any known format'
1330        self.CheckNotebook()
1331        dlg = wx.FileDialog(
1332            self, 'Choose image files', '.', '',
1333            'Any image file (*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img)|'
1334            '*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.zip|'
1335            'European detector file (*.edf)|*.edf|'
1336            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
1337            'MAR file (*.mar*)|*.mar*|'
1338            'GE Image (*.avg;*.sum)|*.avg;*.sum|'
1339            'ADSC Image (*.img)|*.img|'
1340            'GSAS-II Image (*.G2img)|*.G2img|'
1341            'Zip archive (*.zip)|*.zip|'
1342            'All files (*.*)|*.*',
1343            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
1344        try:
1345            if dlg.ShowModal() == wx.ID_OK:
1346                imagefiles = dlg.GetPaths()
1347                imagefiles.sort()
1348                for imagefile in imagefiles:
1349                    # if a zip file, open and extract
1350                    if os.path.splitext(imagefile)[1].lower() == '.zip':
1351                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
1352                        if extractedfile is not None and extractedfile != imagefile:
1353                            imagefile = extractedfile
1354                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
1355                    if Comments:
1356                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
1357                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1358                        Imax = np.amax(Image)
1359                        Imin = max(0.0,np.amin(Image))          #force positive
1360                        if self.imageDefault:
1361                            Data = copy.copy(self.imageDefault)
1362                            Data['showLines'] = True
1363                            Data['ring'] = []
1364                            Data['rings'] = []
1365                            Data['cutoff'] = 10
1366                            Data['pixLimit'] = 20
1367                            Data['edgemin'] = 100000000
1368                            Data['calibdmin'] = 0.5
1369                            Data['calibskip'] = 0
1370                            Data['ellipses'] = []
1371                            Data['calibrant'] = ''
1372                            Data['GonioAngles'] = [0.,0.,0.]
1373                            Data['DetDepth'] = 0.
1374                            Data['DetDepthRef'] = False
1375                        else:
1376                            Data['type'] = 'PWDR'
1377                            Data['color'] = 'Paired'
1378                            Data['tilt'] = 0.0
1379                            Data['rotation'] = 0.0
1380                            Data['showLines'] = False
1381                            Data['ring'] = []
1382                            Data['rings'] = []
1383                            Data['cutoff'] = 10
1384                            Data['pixLimit'] = 20
1385                            Data['calibdmin'] = 0.5
1386                            Data['calibskip'] = 0
1387                            Data['edgemin'] = 100000000
1388                            Data['ellipses'] = []
1389                            Data['GonioAngles'] = [0.,0.,0.]
1390                            Data['DetDepth'] = 0.
1391                            Data['DetDepthRef'] = False
1392                            Data['calibrant'] = ''
1393                            Data['IOtth'] = [2.0,5.0]
1394                            Data['LRazimuth'] = [135,225]
1395                            Data['azmthOff'] = 0.0
1396                            Data['outChannels'] = 2500
1397                            Data['outAzimuths'] = 1
1398                            Data['centerAzm'] = False
1399                            Data['fullIntegrate'] = False
1400                            Data['setRings'] = False
1401                            Data['background image'] = ['',1.0]                           
1402                        Data['setDefault'] = False
1403                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1404                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
1405                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1406                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1407                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
1408                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'strain':np.zeros((3,3))})
1409                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
1410                        self.PickId = Id
1411                        self.Image = Id
1412                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1413                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
1414        finally:
1415            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
1416            os.chdir(path)
1417            dlg.Destroy()
1418
1419    def CheckNotebook(self):
1420        '''Make sure the data tree has the minimally expected controls.
1421        (BHT) correct?
1422        '''
1423        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
1424            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
1425            self.PatternTree.SetItemPyData(sub,[''])
1426        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
1427            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
1428            self.PatternTree.SetItemPyData(sub,{'deriv type':'analytic Hessian',    #default controls
1429                'min dM/M':0.0001,'shift factor':1.,'max cyc':3,'F**2':True,
1430                'minF/sig':0,})
1431        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
1432            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
1433            self.PatternTree.SetItemPyData(sub,{})
1434        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
1435            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
1436            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
1437        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
1438            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
1439            self.PatternTree.SetItemPyData(sub,{})
1440        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
1441            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
1442            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
1443                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
1444               
1445    class CopyDialog(wx.Dialog):
1446        '''Creates a dialog for copying control settings between
1447        data tree items'''
1448        def __init__(self,parent,title,text,data):
1449            wx.Dialog.__init__(self,parent,-1,title, 
1450                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1451            self.data = data
1452            panel = wx.Panel(self)
1453            mainSizer = wx.BoxSizer(wx.VERTICAL)
1454            topLabl = wx.StaticText(panel,-1,text)
1455            mainSizer.Add((10,10),1)
1456            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1457            mainSizer.Add((10,10),1)
1458            ncols = len(data)/40+1
1459            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=ncols,hgap=2,vgap=2)
1460            for id,item in enumerate(self.data):
1461                ckbox = wx.CheckBox(panel,id,item[1])
1462                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
1463                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
1464            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1465            OkBtn = wx.Button(panel,-1,"Ok")
1466            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1467            cancelBtn = wx.Button(panel,-1,"Cancel")
1468            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1469            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1470            btnSizer.Add((20,20),1)
1471            btnSizer.Add(OkBtn)
1472            btnSizer.Add((20,20),1)
1473            btnSizer.Add(cancelBtn)
1474            btnSizer.Add((20,20),1)
1475           
1476            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1477            panel.SetSizer(mainSizer)
1478            panel.Fit()
1479            self.Fit()
1480       
1481        def OnCopyChange(self,event):
1482            id = event.GetId()
1483            self.data[id][0] = self.FindWindowById(id).GetValue()       
1484           
1485        def OnOk(self,event):
1486            parent = self.GetParent()
1487            parent.Raise()
1488            self.EndModal(wx.ID_OK)             
1489           
1490        def OnCancel(self,event):
1491            parent = self.GetParent()
1492            parent.Raise()
1493            self.EndModal(wx.ID_CANCEL)             
1494           
1495        def GetData(self):
1496            return self.data
1497       
1498    class SumDialog(wx.Dialog):
1499        'Allows user to supply scale factor(s) when summing data'
1500        def __init__(self,parent,title,text,dataType,data):
1501            wx.Dialog.__init__(self,parent,-1,title, 
1502                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1503            self.data = data
1504            panel = wx.Panel(self)
1505            mainSizer = wx.BoxSizer(wx.VERTICAL)
1506            topLabl = wx.StaticText(panel,-1,text)
1507            mainSizer.Add((10,10),1)
1508            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1509            mainSizer.Add((10,10),1)
1510            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1511            for id,item in enumerate(self.data[:-1]):
1512                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
1513                name.SetEditable(False)
1514                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1515                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1516                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1517                dataGridSizer.Add(scale,0,wx.LEFT,10)
1518                dataGridSizer.Add(name,0,wx.RIGHT,10)
1519            if dataType:
1520                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
1521                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
1522                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
1523                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
1524                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
1525                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
1526            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1527            OkBtn = wx.Button(panel,-1,"Ok")
1528            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1529            cancelBtn = wx.Button(panel,-1,"Cancel")
1530            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1531            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1532            btnSizer.Add((20,20),1)
1533            btnSizer.Add(OkBtn)
1534            btnSizer.Add((20,20),1)
1535            btnSizer.Add(cancelBtn)
1536            btnSizer.Add((20,20),1)
1537           
1538            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1539            panel.SetSizer(mainSizer)
1540            panel.Fit()
1541            self.Fit()
1542
1543        def OnScaleChange(self,event):
1544            id = event.GetId()
1545            value = self.FindWindowById(id).GetValue()
1546            try:
1547                self.data[id][0] = float(value)
1548                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1549            except ValueError:
1550                if value and '-' not in value[0]:
1551                    print 'bad input - numbers only'
1552                    self.FindWindowById(id).SetValue('0.000')
1553           
1554        def OnNameChange(self,event):
1555            self.data[-1] = self.name.GetValue() 
1556           
1557        def OnOk(self,event):
1558            parent = self.GetParent()
1559            parent.Raise()
1560            self.EndModal(wx.ID_OK)             
1561           
1562        def OnCancel(self,event):
1563            parent = self.GetParent()
1564            parent.Raise()
1565            self.EndModal(wx.ID_CANCEL)             
1566           
1567        def GetData(self):
1568            return self.data
1569           
1570    class ConstraintDialog(wx.Dialog):
1571        '''Window to edit Constraint values
1572        '''
1573        def __init__(self,parent,title,text,data,separator='*'):
1574            wx.Dialog.__init__(self,parent,-1,title, 
1575                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1576            self.data = data
1577            panel = wx.Panel(self)
1578            mainSizer = wx.BoxSizer(wx.VERTICAL)
1579            topLabl = wx.StaticText(panel,-1,text)
1580            mainSizer.Add((10,10),1)
1581            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1582            mainSizer.Add((10,10),1)
1583            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1584            for id,item in enumerate(self.data[:-1]):
1585                lbl = item[1]
1586                if lbl[-1] != '=': lbl += ' ' + separator + ' '
1587                name = wx.StaticText(panel,-1,lbl,size=wx.Size(200,20),
1588                                     style=wx.ALIGN_RIGHT)
1589                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1590                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1591                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1592                dataGridSizer.Add(name,0,wx.LEFT,10)
1593                dataGridSizer.Add(scale,0,wx.RIGHT,10)
1594            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1595            OkBtn = wx.Button(panel,-1,"Ok")
1596            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1597            cancelBtn = wx.Button(panel,-1,"Cancel")
1598            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1599            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1600            btnSizer.Add((20,20),1)
1601            btnSizer.Add(OkBtn)
1602            btnSizer.Add((20,20),1)
1603            btnSizer.Add(cancelBtn)
1604            btnSizer.Add((20,20),1)
1605           
1606            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1607            panel.SetSizer(mainSizer)
1608            panel.Fit()
1609            self.Fit()
1610            self.CenterOnParent()
1611           
1612        def OnNameChange(self,event):
1613            self.data[-1] = self.name.GetValue() 
1614           
1615        def OnScaleChange(self,event):
1616            id = event.GetId()
1617            value = self.FindWindowById(id).GetValue()
1618            try:
1619                self.data[id][0] = float(value)
1620                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1621            except ValueError:
1622                if value and '-' not in value[0]:
1623                    print 'bad input - numbers only'
1624                    self.FindWindowById(id).SetValue('0.000')
1625           
1626        def OnOk(self,event):
1627            parent = self.GetParent()
1628            parent.Raise()
1629            self.EndModal(wx.ID_OK)             
1630           
1631        def OnCancel(self,event):
1632            parent = self.GetParent()
1633            parent.Raise()
1634            self.EndModal(wx.ID_CANCEL)             
1635           
1636        def GetData(self):
1637            return self.data
1638           
1639    def OnPwdrSum(self,event):
1640        'Sum together powder data(?)'
1641        TextList = []
1642        DataList = []
1643        SumList = []
1644        Names = []
1645        Inst = None
1646        SumItemList = []
1647        Comments = ['Sum equals: \n']
1648        if self.PatternTree.GetCount():
1649            item, cookie = self.PatternTree.GetFirstChild(self.root)
1650            while item:
1651                name = self.PatternTree.GetItemText(item)
1652                Names.append(name)
1653                if 'PWDR' in name:
1654                    TextList.append([0.0,name])
1655                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
1656                    if not Inst:
1657                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
1658                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1659            if len(TextList) < 2:
1660                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
1661                return
1662            TextList.append('default_sum_name')               
1663            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
1664            try:
1665                if dlg.ShowModal() == wx.ID_OK:
1666                    lenX = 0
1667                    Xminmax = [0,0]
1668                    Xsum = []
1669                    Ysum = []
1670                    Vsum = []
1671                    result = dlg.GetData()
1672                    for i,item in enumerate(result[:-1]):
1673                        scale,name = item
1674                        data = DataList[i]
1675                        if scale:
1676                            Comments.append("%10.3f %s" % (scale,' * '+name))
1677                            x,y,w,yc,yb,yd = data   #numpy arrays!
1678                            v = 1./w
1679                            if lenX:
1680                                if lenX != len(x):
1681                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
1682                                        '\nExpected:'+str(lenX)+ \
1683                                        '\nFound:   '+str(len(x))+'\nfor '+name)
1684                                    return
1685                            else:
1686                                lenX = len(x)
1687                            if Xminmax[1]:
1688                                if Xminmax != [x[0],x[-1]]:
1689                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
1690                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
1691                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
1692                                    return
1693                                else:
1694                                    for j,yi in enumerate(y):
1695                                         Ysum[j] += scale*yi
1696                                         Vsum[j] += abs(scale)*v[j]
1697                            else:
1698                                Xminmax = [x[0],x[-1]]
1699                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
1700                                for j,yi in enumerate(y):
1701                                    Xsum.append(x[j])
1702                                    Ysum.append(scale*yi)
1703                                    Vsum.append(abs(scale*v[j]))
1704                    Wsum = 1./np.array(Vsum)
1705                    outname = 'PWDR '+result[-1]
1706                    Id = 0
1707                    if outname in Names:
1708                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1709                        try:
1710                            if dlg2.ShowModal() == wx.ID_OK:
1711                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1712                                self.PatternTree.Delete(Id)
1713                        finally:
1714                            dlg2.Destroy()
1715                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1716                    if Id:
1717                        Sample = G2pdG.SetDefaultSample()
1718                        self.PatternTree.SetItemPyData(Id,[{'wtFactor':1.0,'Dummy':False},[np.array(Xsum),np.array(Ysum),np.array(Wsum),
1719                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
1720                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
1721                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
1722                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
1723                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1724                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
1725                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1726                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
1727                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
1728                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1729                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
1730                        self.PatternTree.SelectItem(Id)
1731                        self.PatternTree.Expand(Id)
1732            finally:
1733                dlg.Destroy()
1734
1735    def OnImageSum(self,event):
1736        'Sum together image data(?)'
1737        TextList = []
1738        DataList = []
1739        SumList = []
1740        Names = []
1741        Inst = []
1742        SumItemList = []
1743        Comments = ['Sum equals: \n']
1744        if self.PatternTree.GetCount():
1745            item, cookie = self.PatternTree.GetFirstChild(self.root)
1746            while item:
1747                name = self.PatternTree.GetItemText(item)
1748                Names.append(name)
1749                if 'IMG' in name:
1750                    TextList.append([0.0,name])
1751                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
1752                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
1753                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1754            if len(TextList) < 2:
1755                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
1756                return
1757            TextList.append('default_sum_name')               
1758            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
1759            try:
1760                if dlg.ShowModal() == wx.ID_OK:
1761                    imSize = 0
1762                    result = dlg.GetData()
1763                    First = True
1764                    Found = False
1765                    for i,item in enumerate(result[:-1]):
1766                        scale,name = item
1767                        data = DataList[i]
1768                        if scale:
1769                            Found = True                               
1770                            Comments.append("%10.3f %s" % (scale,' * '+name))
1771                            Npix,imagefile = data
1772                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
1773                            if First:
1774                                newImage = np.zeros_like(image)
1775                                First = False
1776                            if imSize:
1777                                if imSize != Npix:
1778                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
1779                                        '\nExpected:'+str(imSize)+ \
1780                                        '\nFound:   '+str(Npix)+'\nfor '+name)
1781                                    return
1782                                newImage = newImage+scale*image
1783                            else:
1784                                imSize = Npix
1785                                newImage = newImage+scale*image
1786                            del(image)
1787                    if not Found:
1788                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
1789                        return
1790                       
1791                    newImage = np.asfarray(newImage,dtype=np.float32)                       
1792                    outname = 'IMG '+result[-1]
1793                    Id = 0
1794                    if outname in Names:
1795                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1796                        try:
1797                            if dlg2.ShowModal() == wx.ID_OK:
1798                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1799                        finally:
1800                            dlg2.Destroy()
1801                    else:
1802                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1803                    if Id:
1804                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
1805                            'G2img files (*.G2img)|*.G2img', 
1806                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1807                        if dlg.ShowModal() == wx.ID_OK:
1808                            newimagefile = dlg.GetPath()
1809                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
1810                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
1811                            Imax = np.amax(newImage)
1812                            Imin = np.amin(newImage)
1813                            newImage = []
1814                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
1815                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1816                        del(newImage)
1817                        if self.imageDefault:
1818                            Data = copy.copy(self.imageDefault)
1819                        Data['showLines'] = True
1820                        Data['ring'] = []
1821                        Data['rings'] = []
1822                        Data['cutoff'] = 10
1823                        Data['pixLimit'] = 20
1824                        Data['ellipses'] = []
1825                        Data['calibrant'] = ''
1826                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1827                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
1828                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1829                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1830                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),{})
1831                        self.PatternTree.SelectItem(Id)
1832                        self.PatternTree.Expand(Id)
1833                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
1834                        self.Image = self.PickId
1835            finally:
1836                dlg.Destroy()
1837                     
1838    def OnAddPhase(self,event):
1839        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
1840        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
1841            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
1842        else:
1843            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1844        PhaseName = ''
1845        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
1846            style=wx.OK)
1847        if dlg.ShowModal() == wx.ID_OK:
1848            PhaseName = dlg.GetValue()
1849        dlg.Destroy()
1850        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
1851        E,SGData = G2spc.SpcGroup('P 1')
1852        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
1853       
1854    def OnDeletePhase(self,event):
1855        'Delete a phase from the tree. Called by Data/Delete Phase menu'
1856        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
1857        if self.dataFrame:
1858            self.dataFrame.Clear() 
1859        TextList = []
1860        DelList = []
1861        DelItemList = []
1862        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
1863            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1864        else:
1865            return
1866        if sub:
1867            item, cookie = self.PatternTree.GetFirstChild(sub)
1868            while item:
1869                TextList.append(self.PatternTree.GetItemText(item))
1870                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
1871            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
1872            try:
1873                if dlg.ShowModal() == wx.ID_OK:
1874                    result = dlg.GetSelections()
1875                    for i in result: DelList.append([i,TextList[i]])
1876                    item, cookie = self.PatternTree.GetFirstChild(sub)
1877                    i = 0
1878                    while item:
1879                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
1880                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1881                        i += 1
1882                    for item in DelItemList:
1883                        name = self.PatternTree.GetItemText(item)
1884                        self.PatternTree.Delete(item)
1885                        self.G2plotNB.Delete(name)
1886                    item, cookie = self.PatternTree.GetFirstChild(self.root)
1887                    while item:
1888                        name = self.PatternTree.GetItemText(item)
1889                        if 'PWDR' in name:
1890                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
1891                            refList = self.PatternTree.GetItemPyData(Id)
1892                            for i,item in DelList:
1893                                del(refList[item])
1894                            self.PatternTree.SetItemPyData(Id,refList)
1895                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1896            finally:
1897                dlg.Destroy()
1898               
1899    def OnRenameData(self,event):
1900        'Renames an existing phase. Called by Data/Rename Phase menu'
1901        name = self.PatternTree.GetItemText(self.PickId)     
1902        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
1903            dataType = name[:name.index(' ')+1]                 #includes the ' '
1904            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
1905                defaultValue=name[name.index(' ')+1:])
1906            try:
1907                if dlg.ShowModal() == wx.ID_OK:
1908                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
1909            finally:
1910                dlg.Destroy()
1911       
1912    def GetFileList(self,fileType,skip=None):        #potentially useful?
1913        'Appears unused. Note routine of same name in GSASIIpwdGUI'
1914        fileList = []
1915        Source = ''
1916        id, cookie = self.PatternTree.GetFirstChild(self.root)
1917        while id:
1918            name = self.PatternTree.GetItemText(id)
1919            if fileType in name:
1920                if id == skip:
1921                    Source = name
1922                else:
1923                    fileList.append([False,name,id])
1924            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1925        if skip:
1926            return fileList,Source
1927        else:
1928            return fileList
1929           
1930    def OnDataDelete(self, event):
1931        '''Delete one or more histograms from data tree. Called by the
1932        Data/DeleteData menu
1933        '''
1934        TextList = ['All Data']
1935        DelList = []
1936        DelItemList = []
1937        ifPWDR = False
1938        ifIMG = False
1939        ifHKLF = False
1940        ifPDF = False
1941        if self.PatternTree.GetCount():
1942            item, cookie = self.PatternTree.GetFirstChild(self.root)
1943            while item:
1944                name = self.PatternTree.GetItemText(item)
1945                if name not in ['Notebook','Controls','Covariance','Constraints','Restraints','Phases']:
1946                    if 'PWDR' in name: ifPWDR = True
1947                    if 'IMG' in name: ifIMG = True
1948                    if 'HKLF' in name: ifHKLF = True
1949                    if 'PDF' in name: ifPDF = True
1950                    TextList.append(name)
1951                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1952            if ifPWDR: TextList.insert(1,'All PWDR')
1953            if ifIMG: TextList.insert(1,'All IMG')
1954            if ifHKLF: TextList.insert(1,'All HKLF')
1955            if ifPDF: TextList.insert(1,'All PDF')               
1956            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
1957            try:
1958                if dlg.ShowModal() == wx.ID_OK:
1959                    result = dlg.GetSelections()
1960                    for i in result: DelList.append(TextList[i])
1961                    if 'All Data' in DelList:
1962                        DelList = [item for item in TextList if item[:3] != 'All']
1963                    elif 'All PWDR' in DelList:
1964                        DelList = [item for item in TextList if item[:4] == 'PWDR']
1965                    elif 'All IMG' in DelList:
1966                        DelList = [item for item in TextList if item[:3] == 'IMG']
1967                    elif 'All HKLF' in DelList:
1968                        DelList = [item for item in TextList if item[:4] == 'HKLF']
1969                    elif 'All PDF' in DelList:
1970                        DelList = [item for item in TextList if item[:3] == 'PDF']
1971                    item, cookie = self.PatternTree.GetFirstChild(self.root)
1972                    while item:
1973                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
1974                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1975                    for item in DelItemList:
1976                        self.PatternTree.Delete(item)
1977                    self.PickId = 0
1978                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
1979            finally:
1980                dlg.Destroy()
1981
1982    def OnFileOpen(self, event, filename=None):
1983        '''Reads in a GSAS-II .gpx project file in response to the
1984        File/Open Project menu button
1985        '''
1986        result = wx.ID_OK
1987        Id = 0
1988        if self.PatternTree.GetChildrenCount(self.root,False):
1989            if self.dataFrame:
1990                self.dataFrame.Clear() 
1991            dlg = wx.MessageDialog(
1992                self,
1993                'Do you want to overwrite the current project? '
1994                'Any unsaved changes will be lost. Press OK to continue.',
1995                'Overwrite?',  wx.OK | wx.CANCEL)
1996            try:
1997                result = dlg.ShowModal()
1998                if result == wx.ID_OK:
1999                    self.PatternTree.DeleteChildren(self.root)
2000                    self.GSASprojectfile = ''
2001                    if self.HKL: self.HKL = []
2002                    if self.G2plotNB.plotList:
2003                        self.G2plotNB.clear()
2004            finally:
2005                dlg.Destroy()
2006        if result != wx.ID_OK: return
2007
2008        if not filename:
2009            if self.dataDisplay: self.dataDisplay.Destroy()
2010            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2011                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2012            try:
2013                if dlg.ShowModal() != wx.ID_OK: return
2014                self.GSASprojectfile = dlg.GetPath()
2015                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2016                self.dirname = dlg.GetDirectory()
2017            finally:
2018                dlg.Destroy()
2019        else:
2020            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2021            self.dirname = os.path.split(filename)[0]
2022
2023        G2IO.ProjFileOpen(self)
2024        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2025        self.PatternTree.Expand(self.root)
2026        self.HKL = []
2027        item, cookie = self.PatternTree.GetFirstChild(self.root)
2028        while item and not Id:
2029            name = self.PatternTree.GetItemText(item)
2030            if name[:4] in ['PWDR','HKLF','IMG ','PDF ']:
2031                Id = item
2032            elif name == 'Controls':
2033                data = self.PatternTree.GetItemPyData(item)
2034                if data:
2035                    for item in self.Refine: item.Enable(True)
2036                    for item in self.SeqRefine: item.Enable(True)
2037            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2038        if Id:
2039            self.PatternTree.SelectItem(Id)
2040        self.CheckNotebook()
2041        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2042
2043    def OnFileClose(self, event):
2044        '''Clears the data tree in response to the
2045        File/Close Project menu button. User is given option to save
2046        the project.
2047        '''
2048        if self.dataFrame:
2049            self.dataFrame.Clear()
2050            self.dataFrame.SetLabel('GSAS-II data display') 
2051        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2052        try:
2053            result = dlg.ShowModal()
2054            if result == wx.ID_OK:
2055                self.OnFileSaveMenu(event)
2056            if result != wx.ID_CANCEL:
2057                self.GSASprojectfile = ''
2058                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2059                self.PatternTree.DeleteChildren(self.root)
2060                if self.HKL: self.HKL = []
2061                if self.G2plotNB.plotList:
2062                    self.G2plotNB.clear()
2063        finally:
2064            dlg.Destroy()
2065
2066    def OnFileSave(self, event):
2067        '''Save the current project in response to the
2068        File/Save Project menu button
2069        '''
2070       
2071        if self.GSASprojectfile: 
2072            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2073            G2IO.ProjFileSave(self)
2074        else:
2075            self.OnFileSaveas(event)
2076
2077    def OnFileSaveas(self, event):
2078        '''Save the current project in response to the
2079        File/Save as menu button
2080        '''
2081        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2082            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2083        try:
2084            if dlg.ShowModal() == wx.ID_OK:
2085                self.GSASprojectfile = dlg.GetPath()
2086                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2087                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2088                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2089                G2IO.ProjFileSave(self)
2090                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2091        finally:
2092            dlg.Destroy()
2093
2094    def ExitMain(self, event):
2095        '''Called if the main window is closed'''
2096        if self.undofile:
2097            os.remove(self.undofile)
2098        sys.exit()
2099       
2100    def OnFileExit(self, event):
2101        '''Called in response to the File/Quit menu button'''
2102        if self.dataFrame:
2103            self.dataFrame.Clear() 
2104            self.dataFrame.Destroy()
2105        self.Close()
2106       
2107    def OnExportPatterns(self,event):
2108        names = ['All']
2109        exports = []
2110        item, cookie = self.PatternTree.GetFirstChild(self.root)
2111        while item:
2112            name = self.PatternTree.GetItemText(item)
2113            if 'PWDR' in name:
2114                names.append(name)
2115            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2116        if names:
2117            dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2118            if dlg.ShowModal() == wx.ID_OK:
2119                sel = dlg.GetSelections()
2120                if sel[0] == 0:
2121                    exports = names[1:]
2122                else:
2123                    for x in sel:
2124                        exports.append(names[x])
2125            dlg.Destroy()
2126        if exports:
2127            dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '', 
2128                'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2129                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2130            try:
2131                if dlg.ShowModal() == wx.ID_OK:
2132                    powderfile = dlg.GetPath()
2133                    powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2134                    if 'fxye' in powderfile:
2135                        G2IO.powderFxyeSave(self,exports,powderfile)
2136                    else:       #just xye
2137                        G2IO.powderXyeSave(self,exports,powderfile)
2138            finally:
2139                dlg.Destroy()
2140       
2141    def OnExportPeakList(self,event):
2142        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2143            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2144        try:
2145            if dlg.ShowModal() == wx.ID_OK:
2146                self.peaklistfile = dlg.GetPath()
2147                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2148                file = open(self.peaklistfile,'w')               
2149                item, cookie = self.PatternTree.GetFirstChild(self.root)
2150                while item:
2151                    name = self.PatternTree.GetItemText(item)
2152                    if 'PWDR' in name:
2153                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2154                        while item2:
2155                            name2 = self.PatternTree.GetItemText(item2)
2156                            if name2 == 'Peak List':
2157                                peaks = self.PatternTree.GetItemPyData(item2)
2158                                file.write("%s \n" % (name+' Peak List'))               
2159                                for peak in peaks:
2160                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2161                                        (peak[0],peak[2],peak[4],peak[6]))
2162                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2163                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2164                file.close()
2165        finally:
2166            dlg.Destroy()
2167       
2168    def OnExportHKL(self,event):
2169        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2170            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2171        try:
2172            if dlg.ShowModal() == wx.ID_OK:
2173                self.peaklistfile = dlg.GetPath()
2174                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2175                file = open(self.peaklistfile,'w')               
2176                item, cookie = self.PatternTree.GetFirstChild(self.root)
2177                while item:
2178                    name = self.PatternTree.GetItemText(item)
2179                    if 'PWDR' in name:
2180                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2181                        while item2:
2182                            name2 = self.PatternTree.GetItemText(item2)
2183                            if name2 == 'Reflection Lists':
2184                                data = self.PatternTree.GetItemPyData(item2)
2185                                phases = data.keys()
2186                                for phase in phases:
2187                                    peaks = data[phase]
2188                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2189                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2190                                    for peak in peaks:
2191                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2192                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2193                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2194                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2195                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2196                file.close()
2197        finally:
2198            dlg.Destroy()
2199       
2200    def OnExportPDF(self,event):
2201        #need S(Q) and G(R) to be saved here - probably best from selection?
2202        names = ['All']
2203        exports = []
2204        item, cookie = self.PatternTree.GetFirstChild(self.root)
2205        while item:
2206            name = self.PatternTree.GetItemText(item)
2207            if 'PDF' in name:
2208                names.append(name)
2209            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2210        if names:
2211            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2212            if dlg.ShowModal() == wx.ID_OK:
2213                sel = dlg.GetSelections()
2214                if sel[0] == 0:
2215                    exports = names[1:]
2216                else:
2217                    for x in sel:
2218                        exports.append(names[x])
2219            dlg.Destroy()
2220        if exports:
2221            G2IO.PDFSave(self,exports)
2222       
2223    def OnExportPhase(self,event):
2224        event.Skip()
2225       
2226    def OnExportCIF(self,event):
2227        event.Skip()
2228
2229    def OnMakePDFs(self,event):
2230        '''Calculates PDFs
2231        '''
2232        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2233        TextList = ['All PWDR']
2234        PDFlist = []
2235        Names = []
2236        if self.PatternTree.GetCount():
2237            id, cookie = self.PatternTree.GetFirstChild(self.root)
2238            while id:
2239                name = self.PatternTree.GetItemText(id)
2240                Names.append(name)
2241                if 'PWDR' in name:
2242                    TextList.append(name)
2243                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2244            if len(TextList) == 1:
2245                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2246                return
2247            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2248            try:
2249                if dlg.ShowModal() == wx.ID_OK:
2250                    result = dlg.GetSelections()
2251                    for i in result: PDFlist.append(TextList[i])
2252                    if 0 in result:
2253                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2254                    for item in PDFlist:
2255                        PWDRname = item[4:]
2256                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2257                        Data = {
2258                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2259                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2260                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2261                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2262                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2263                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2264                            'Lorch':True,}
2265                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2266                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2267                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2268                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2269                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2270                for item in self.ExportPDF: item.Enable(True)
2271            finally:
2272                dlg.Destroy()
2273               
2274    def GetPWDRdatafromTree(self,PWDRname):
2275        ''' Returns powder data from GSASII tree
2276
2277        :param str PWDRname: a powder histogram name as obtained from
2278          :meth:`GSASIIstruct.GetHistogramNames`
2279
2280        :returns: PWDRdata = powder data dictionary with
2281          Powder data arrays, Limits, Instrument Parameters,
2282          Sample Parameters           
2283        '''
2284        PWDRdata = {}
2285        try:
2286            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2287        except ValueError:
2288            PWDRdata['wtFactor'] = 1.0
2289        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2290        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2291        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2292        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2293        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2294        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2295        if 'ranId' not in PWDRdata['Sample Parameters']:
2296            PWDRdata['Sample Parameters']['ranId'] = ran.randint(0,sys.maxint)
2297        PWDRdata['ranId'] = PWDRdata['Sample Parameters']['ranId']
2298        return PWDRdata
2299
2300    def GetHKLFdatafromTree(self,HKLFname):
2301        ''' Returns single crystal data from GSASII tree
2302
2303        :param str HKLFname: a single crystal histogram name as obtained
2304          from
2305          :meth:`GSASIIstruct.GetHistogramNames`
2306
2307        :returns: HKLFdata = single crystal data list of reflections
2308
2309        '''
2310        HKLFdata = {}
2311        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2312#        try:
2313#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2314#        except ValueError:
2315#            HKLFdata['wtFactor'] = 1.0
2316        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2317        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2318        return HKLFdata
2319       
2320    def GetPhaseData(self):
2321        '''Returns a list of defined phases. Used only in GSASIIgrid
2322        Note routine :meth:`GSASIIstruct.GetPhaseData` also exists.
2323        '''
2324        phaseData = {}
2325        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2326            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2327        else:
2328            print 'no phases to be refined'
2329            return
2330        if sub:
2331            item, cookie = self.PatternTree.GetFirstChild(sub)
2332            while item:
2333                phaseName = self.PatternTree.GetItemText(item)
2334                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2335                if 'ranId' not in phaseData[phaseName]:
2336                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2337                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2338        return phaseData               
2339                   
2340    def GetUsedHistogramsAndPhasesfromTree(self):
2341        ''' Returns all histograms that are found in any phase
2342        and any phase that uses a histogram
2343        :returns: two dicts:
2344
2345            * Histograms = dictionary of histograms as {name:data,...}
2346            * Phases = dictionary of phases that use histograms
2347        '''
2348        phaseData = self.GetPhaseData()
2349        if not phaseData:
2350            return {},{}
2351        Histograms = {}
2352        Phases = {}
2353        for phase in phaseData:
2354            Phase = phaseData[phase]
2355            if Phase['Histograms']:
2356                if phase not in Phases:
2357                    Phases[phase] = Phase
2358                for hist in Phase['Histograms']:
2359                    if hist not in Histograms:
2360                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
2361                        if 'PWDR' in hist[:4]: 
2362                            Histograms[hist] = self.GetPWDRdatafromTree(item)
2363                        elif 'HKLF' in hist[:4]:
2364                            Histograms[hist] = self.GetHKLFdatafromTree(item)
2365                        #future restraint, etc. histograms here
2366        return Histograms,Phases
2367       
2368    class ViewParmDialog(wx.Dialog):
2369        '''Window to show all parameters in the refinement.
2370        Called from :meth:`OnViewLSParms`
2371        '''
2372        def __init__(self,parent,title,parmDict):
2373            wx.Dialog.__init__(self,parent,-1,title,size=(300,430),
2374                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2375            panel = wx.Panel(self,size=(300,430))
2376            parmNames = parmDict.keys()
2377            parmNames.sort()
2378            parmText = ' p:h:Parameter       refine?              value\n'
2379            for name in parmNames:
2380                parmData = parmDict[name]
2381                try:
2382                    parmText += ' %s \t%12.4g \n'%(name.ljust(19)+'\t'+parmData[1],parmData[0])
2383                except TypeError:
2384                    pass
2385            parmTable = wx.TextCtrl(panel,-1,parmText,
2386                style=wx.TE_MULTILINE|wx.TE_READONLY,size=(290,400))
2387            mainSizer = wx.BoxSizer(wx.VERTICAL)
2388            mainSizer.Add(parmTable)
2389            panel.SetSizer(mainSizer)
2390                           
2391    def OnViewLSParms(self,event):
2392        '''Displays a window showing all parameters in the refinement.
2393        Called from the Calculate/View LS Parms menu.
2394        '''
2395        parmDict = {}
2396        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
2397        for phase in Phases:
2398            if 'pId' not in Phases[phase]:
2399                self.ErrorDialog('View parameter error','You must run least squares at least once')
2400                return
2401        rigidbodyDict = self.PatternTree.GetItemPyData(   
2402            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
2403        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2404        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2405        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
2406        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
2407        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
2408        varyList = rbVary+phaseVary+hapVary+histVary
2409        parmDict.update(rbDict)
2410        parmDict.update(phaseDict)
2411        parmDict.update(hapDict)
2412        parmDict.update(histDict)
2413        for parm in parmDict:
2414            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
2415                'Omega','Chi','Phi','nDebye','nPeaks']:
2416                parmDict[parm] = [parmDict[parm],' ']
2417            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
2418                parmDict[parm] = [parmDict[parm],' ']
2419            elif parm in varyList:
2420                parmDict[parm] = [parmDict[parm],'True']
2421            else:
2422                parmDict[parm] = [parmDict[parm],'False']
2423        parmDict[' Num refined'] = [len(varyList),'']
2424        dlg = self.ViewParmDialog(self,'Parameters for least squares',parmDict)
2425        try:
2426            if dlg.ShowModal() == wx.ID_OK:
2427                print 'do something with changes?? - No!'
2428        finally:
2429            dlg.Destroy()
2430       
2431    def OnRefine(self,event):
2432        '''Perform a refinement.
2433        Called from the Calculate/Refine menu.
2434        '''       
2435        self.OnFileSave(event)
2436        # check that constraints are OK here
2437        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2438        if errmsg:
2439            print('Error in constraints:\n'+errmsg+
2440                  '\nRefinement not possible')
2441            self.ErrorDialog('Constraint Error',
2442                             'Error in constraints:\n'+errmsg+
2443                             '\nRefinement not possible')
2444            return
2445        if warnmsg:
2446            print('Conflict between refinment flag settings and constraints:\n'+
2447                  warnmsg+'\nRefinement not possible')
2448            self.ErrorDialog('Refinement Flag Error',
2449                             'Conflict between refinment flag settings and constraints:\n'+
2450                             warnmsg+
2451                             '\nRefinement not possible')
2452            return
2453        #works - but it'd be better if it could restore plots
2454        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
2455            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2456        screenSize = wx.ClientDisplayRect()
2457        Size = dlg.GetSize()
2458        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2459        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2460        dlg.SetSize(Size)
2461        Rw = 100.00
2462        try:
2463            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
2464        finally:
2465            dlg.Destroy()
2466        oldId =  self.PatternTree.GetSelection()
2467        oldName = self.PatternTree.GetItemText(oldId)
2468        parentId = self.PatternTree.GetItemParent(oldId)
2469        parentName = ''
2470        if parentId:
2471            parentName = self.PatternTree.GetItemText(parentId)
2472        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
2473        try:
2474            if dlg.ShowModal() == wx.ID_OK:
2475                Id = 0
2476                self.PatternTree.DeleteChildren(self.root)
2477                if self.HKL: self.HKL = []
2478                if self.G2plotNB.plotList:
2479                    self.G2plotNB.clear()
2480                G2IO.ProjFileOpen(self)
2481                item, cookie = self.PatternTree.GetFirstChild(self.root)
2482                while item and not Id:
2483                    name = self.PatternTree.GetItemText(item)
2484                    if name[:4] in ['PWDR','HKLF']:
2485                        Id = item
2486                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2487                if parentName:
2488                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
2489                    if parentId:
2490                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
2491                    else:
2492                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
2493                    self.PatternTree.SelectItem(itemId)
2494                elif Id:
2495                    self.PatternTree.SelectItem(Id)
2496        finally:
2497            dlg.Destroy()
2498
2499    def OnSeqRefine(self,event):
2500        '''Perform a sequential refinement.
2501        Called from the Calculate/Sequential refine menu.
2502        '''       
2503        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequental results')
2504        if not Id:
2505            Id = self.PatternTree.AppendItem(self.root,text='Sequental results')
2506            self.PatternTree.SetItemPyData(Id,{})           
2507        self.OnFileSave(event)
2508        # check that constraints are OK here
2509        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2510        if errmsg:
2511            print('Error in constraints:\n'+errmsg+
2512                  '\nRefinement not possible')
2513            self.ErrorDialog('Constraint Error',
2514                             'Error in constraints:\n'+errmsg+
2515                             '\nRefinement not possible')
2516            return
2517        if warnmsg:
2518            print('Conflict between refinment flag settings and constraints:\n'+
2519                  warnmsg+'\nRefinement not possible')
2520            self.ErrorDialog('Refinement Flag Error',
2521                             'Conflict between refinment flag settings and constraints:\n'+
2522                             warnmsg+'\nRefinement not possible')
2523            return
2524        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
2525            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2526        screenSize = wx.ClientDisplayRect()
2527        Size = dlg.GetSize()
2528        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2529        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2530        dlg.SetSize(Size)
2531        try:
2532            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
2533        finally:
2534            dlg.Destroy()       
2535        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
2536        try:
2537            if dlg.ShowModal() == wx.ID_OK:
2538                Id = 0
2539                self.PatternTree.DeleteChildren(self.root)
2540                if self.HKL: self.HKL = []
2541                if self.G2plotNB.plotList:
2542                    self.G2plotNB.clear()
2543                G2IO.ProjFileOpen(self)
2544                item, cookie = self.PatternTree.GetFirstChild(self.root)
2545                while item and not Id:
2546                    name = self.PatternTree.GetItemText(item)
2547                    if name[:4] in ['PWDR','HKLF']:
2548                        Id = item
2549                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2550                if Id:
2551                    self.PatternTree.SelectItem(Id)
2552        finally:
2553            dlg.Destroy()
2554       
2555    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
2556        'Display an error message'
2557        result = None
2558        if parent is None:
2559            dlg = wx.MessageDialog(self, message, title,  wtype)
2560        else:
2561            dlg = wx.MessageDialog(parent, message, title,  wtype)
2562            dlg.CenterOnParent() # not working on Mac
2563        try:
2564            result = dlg.ShowModal()
2565        finally:
2566            dlg.Destroy()
2567        return result
2568
2569class GSASIImain(wx.App):
2570    '''Defines a wxApp for GSAS-II
2571
2572    Creates a wx frame (self.main) which contains the display of the
2573    data tree.
2574    '''
2575    def OnInit(self):
2576        '''Called automatically when the app is created.'''
2577        self.main = GSASII(None)
2578        self.main.Show()
2579        self.SetTopWindow(self.main)
2580        return True
2581    def MacOpenFile(self, filename):
2582        '''Called on Mac every time a file is dropped on the app when it is running,
2583        treat this like a File/Open project menu action.
2584        Should be ignored on other platforms
2585        '''
2586        self.main.OnFileOpen(None,filename)
2587
2588def main():
2589    '''Start up the GSAS-II application'''
2590    #application = GSASIImain() # don't redirect output, someday we
2591    # may want to do this if we can
2592    application = GSASIImain(0)
2593    if wxInspector: wxeye.InspectionTool().Show()
2594
2595    #application.main.OnRefine(None)
2596    application.MainLoop()
2597   
2598if __name__ == '__main__':
2599    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.