source: trunk/GSASII.py @ 1035

Last change on this file since 1035 was 1035, checked in by vondreele, 9 years ago

further adventures in residual reporting in cif files
hId & pId must be taken from least squares run - put in appropriate places & used by G2cif.py

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 117.5 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2013-08-15 16:32:19 +0000 (Thu, 15 Aug 2013) $
6# $Author: vondreele $
7# $Revision: 1035 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1035 2013-08-15 16:32:19Z vondreele $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import 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: 1035 $")
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},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},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        exp.export(mode)
1059
1060    def _Add_ExportMenuItems(self,parent):
1061        item = parent.Append(
1062            help='Select PWDR item to enable',id=wx.ID_ANY,
1063            kind=wx.ITEM_NORMAL,
1064            text='Export Powder Patterns...')
1065        self.ExportPattern.append(item)
1066        item.Enable(False)
1067        self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1068
1069        item = parent.Append(
1070            help='',id=wx.ID_ANY,
1071            kind=wx.ITEM_NORMAL,
1072            text='Export All Peak Lists...')
1073        self.ExportPeakList.append(item)
1074        item.Enable(True)
1075        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1076
1077        item = parent.Append(
1078            help='',id=wx.ID_ANY,
1079            kind=wx.ITEM_NORMAL,
1080            text='Export HKLs...')
1081        self.ExportHKL.append(item)
1082        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1083
1084        item = parent.Append(
1085            help='Select PDF item to enable',
1086            id=wx.ID_ANY,
1087            kind=wx.ITEM_NORMAL,
1088            text='Export PDF...')
1089        self.ExportPDF.append(item)
1090        item.Enable(False)
1091        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1092
1093        item = parent.Append(
1094            help='',id=wx.ID_ANY,
1095            kind=wx.ITEM_NORMAL,
1096            text='Export Phase...')
1097        self.ExportPhase.append(item)
1098        item.Enable(False)
1099        self.Bind(wx.EVT_MENU, self.OnExportPhase, id=item.GetId())
1100
1101#        item = parent.Append(
1102#            help='',id=wx.ID_ANY,
1103#            kind=wx.ITEM_NORMAL,
1104#            text='Export CIF...')
1105#        self.ExportCIF.append(item)
1106#        item.Enable(False)
1107#        self.Bind(wx.EVT_MENU, self.OnExportCIF, id=item.GetId())
1108               
1109    def FillMainMenu(self,menubar):
1110        '''Define contents of the main GSAS-II menu for the (main) data tree window
1111        in the mac, used also for the data item windows as well.
1112        '''
1113        File = wx.Menu(title='')
1114        menubar.Append(menu=File, title='&File')
1115        self._Add_FileMenuItems(File)
1116        Data = wx.Menu(title='')
1117        menubar.Append(menu=Data, title='Data')
1118        self._Add_DataMenuItems(Data)
1119        Calculate = wx.Menu(title='')       
1120        menubar.Append(menu=Calculate, title='&Calculate')
1121        self._Add_CalculateMenuItems(Calculate)
1122        Import = wx.Menu(title='')       
1123        menubar.Append(menu=Import, title='Import')
1124        self._Add_ImportMenu_Phase(Import)
1125        self._Add_ImportMenu_powder(Import)
1126        self._Add_ImportMenu_Sfact(Import)
1127        self.ExportMenu = wx.Menu(title='')
1128        menubar.Append(menu=self.ExportMenu, title='Export')
1129        self._init_Exports(self.ExportMenu)
1130        self._Add_ExportMenuItems(self.ExportMenu)
1131        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1132            morehelpitems=[('&Tutorials','Tutorials')])
1133        menubar.Append(menu=HelpMenu,title='&Help')
1134
1135    def _init_ctrls(self, parent):
1136        wx.Frame.__init__(self, name='GSASII', parent=parent,
1137            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1138        clientSize = wx.ClientDisplayRect()
1139        Size = self.GetSize()
1140        xPos = clientSize[2]-Size[0]
1141        self.SetPosition(wx.Point(xPos,clientSize[1]))
1142        self._init_Imports()
1143        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1144        self.MakePDF = []
1145        self.Refine = []
1146        self.SeqRefine = []
1147        self.ExportPattern = []
1148        self.ExportPeakList = []
1149        self.ExportHKL = []
1150        self.ExportPDF = []
1151        self.ExportPhase = []
1152        self.ExportCIF = []
1153        #
1154        self.GSASIIMenu = wx.MenuBar()
1155        self.FillMainMenu(self.GSASIIMenu)
1156        self.SetMenuBar(self.GSASIIMenu)
1157        self.Bind(wx.EVT_SIZE, self.OnSize)
1158        self.CreateStatusBar()
1159        self.mainPanel = wx.Panel(self,-1)
1160       
1161        wxID_PATTERNTREE = wx.NewId()
1162        self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE,
1163            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1164        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,
1165            self.OnPatternTreeSelChanged, id=wxID_PATTERNTREE)
1166        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1167            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1168        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1169            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1170        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1171            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1172        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1173            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1174        self.root = self.PatternTree.AddRoot('Loaded Data: ')
1175       
1176        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1177            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1178        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1179        plotFrame.Show()
1180       
1181        self.dataDisplay = None
1182       
1183    def __init__(self, parent):
1184        self._init_ctrls(parent)
1185        self.Bind(wx.EVT_CLOSE, self.ExitMain)
1186        # various defaults
1187        self.oldFocus = None
1188        self.GSASprojectfile = ''
1189        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
1190        self.undofile = ''
1191        self.TreeItemDelete = False
1192        self.Offset = [0.0,0.0]
1193        self.delOffset = .02
1194        self.refOffset = -100.0
1195        self.refDelt = .01
1196        self.Weight = False
1197        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
1198        self.IfPlot = False
1199        self.PatternId = 0
1200        self.PickId = 0
1201        self.PeakTable = []
1202        self.LimitsTable = []
1203        self.HKL = []
1204        self.Lines = []
1205        self.itemPicked = None
1206        self.dataFrame = None
1207        self.Interpolate = 'nearest'
1208        self.ContourColor = 'Paired'
1209        self.VcovColor = 'RdYlGn'
1210        self.RamaColor = 'Blues'
1211        self.Projection = 'equal area'
1212        self.logPlot = False
1213        self.qPlot = False
1214        self.Contour = False
1215        self.Legend = False
1216        self.SinglePlot = False
1217        self.SubBack = False
1218        self.plotView = 0
1219        self.Image = 0
1220        self.oldImagefile = ''
1221        self.ImageZ = []
1222        self.Integrate = 0
1223        self.imageDefault = {}
1224        self.Sngl = 0
1225        self.ifGetRing = False
1226        self.setPoly = False
1227        arg = sys.argv
1228        if len(arg) > 1:
1229            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1230            self.dirname = os.path.dirname(arg[1])
1231            if self.dirname: os.chdir(self.dirname)
1232            try:
1233                G2IO.ProjFileOpen(self)
1234                self.PatternTree.Expand(self.root)
1235                for item in self.Refine: item.Enable(True)
1236                for item in self.SeqRefine: item.Enable(True)
1237            except:
1238                print 'Error opening file',arg[1]
1239
1240    def OnSize(self,event):
1241        'Called when the main window is resized. Not sure why'
1242        w,h = self.GetClientSizeTuple()
1243        self.mainPanel.SetSize(wx.Size(w,h))
1244        self.PatternTree.SetSize(wx.Size(w,h))
1245                       
1246    def OnPatternTreeSelChanged(self, event):
1247        '''Called when a data tree item is selected'''
1248        if self.TreeItemDelete:
1249            self.TreeItemDelete = False
1250        else:
1251            pltNum = self.G2plotNB.nb.GetSelection()
1252            if pltNum >= 0:                         #to avoid the startup with no plot!
1253                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1254                pltPlot = pltPage.figure
1255            item = event.GetItem()
1256            G2gd.MovePatternTreeToGrid(self,item)
1257            if self.oldFocus:
1258                self.oldFocus.SetFocus()
1259       
1260    def OnPatternTreeItemCollapsed(self, event):
1261        'Called when a tree item is collapsed'
1262        event.Skip()
1263
1264    def OnPatternTreeItemExpanded(self, event):
1265        'Called when a tree item is expanded'
1266        event.Skip()
1267       
1268    def OnPatternTreeItemDelete(self, event):
1269        'Called when a tree item is deleted -- not sure what this does'
1270        self.TreeItemDelete = True
1271
1272    def OnPatternTreeItemActivated(self, event):
1273        'Called when a tree item is activated'
1274        event.Skip()
1275       
1276    def OnPatternTreeKeyDown(self,event):
1277        'Not sure what this does'
1278        key = event.GetKeyCode()
1279        item = self.PickId
1280        if type(item) is int: return # is this the toplevel in tree?
1281        if key == wx.WXK_UP:
1282            self.oldFocus = wx.Window.FindFocus()
1283            self.PatternTree.GetPrevSibling(item)
1284        elif key == wx.WXK_DOWN:
1285            self.oldFocus = wx.Window.FindFocus()
1286            self.PatternTree.GetNextSibling(item)
1287               
1288    def OnReadPowderPeaks(self,event):
1289        'Bound to menu Data/Read Powder Peaks -- still needed?'
1290        Cuka = 1.54052
1291        self.CheckNotebook()
1292        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
1293            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
1294        try:
1295            if dlg.ShowModal() == wx.ID_OK:
1296                self.HKL = []
1297                self.powderfile = dlg.GetPath()
1298                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
1299                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
1300                data = ['PKS',Cuka,0.0]
1301                names = ['Type','Lam','Zero'] 
1302                codes = [0,0,0]
1303                inst = [G2IO.makeInstDict(names,data,codes),{}]
1304                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1305                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
1306                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),peaks)
1307                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1308                self.PatternTree.Expand(Id)
1309                self.PatternTree.SelectItem(Id)
1310                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1311        finally:
1312            dlg.Destroy()
1313           
1314    def OnImageRead(self,event):
1315        'Called to read in an image in any known format'
1316        self.CheckNotebook()
1317        dlg = wx.FileDialog(
1318            self, 'Choose image files', '.', '',
1319            'Any image file (*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img)|'
1320            '*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.zip|'
1321            'European detector file (*.edf)|*.edf|'
1322            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
1323            'MAR file (*.mar*)|*.mar*|'
1324            'GE Image (*.avg;*.sum)|*.avg;*.sum|'
1325            'ADSC Image (*.img)|*.img|'
1326            'GSAS-II Image (*.G2img)|*.G2img|'
1327            'Zip archive (*.zip)|*.zip|'
1328            'All files (*.*)|*.*',
1329            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
1330        try:
1331            if dlg.ShowModal() == wx.ID_OK:
1332                imagefiles = dlg.GetPaths()
1333                imagefiles.sort()
1334                for imagefile in imagefiles:
1335                    # if a zip file, open and extract
1336                    if os.path.splitext(imagefile)[1].lower() == '.zip':
1337                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
1338                        if extractedfile is not None and extractedfile != imagefile:
1339                            imagefile = extractedfile
1340                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
1341                    if Comments:
1342                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
1343                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1344                        Imax = np.amax(Image)
1345                        Imin = max(0.0,np.amin(Image))          #force positive
1346                        if self.imageDefault:
1347                            Data = copy.copy(self.imageDefault)
1348                            Data['showLines'] = True
1349                            Data['ring'] = []
1350                            Data['rings'] = []
1351                            Data['cutoff'] = 10
1352                            Data['pixLimit'] = 20
1353                            Data['edgemin'] = 100000000
1354                            Data['calibdmin'] = 0.5
1355                            Data['calibskip'] = 0
1356                            Data['ellipses'] = []
1357                            Data['calibrant'] = ''
1358                            Data['GonioAngles'] = [0.,0.,0.]
1359                        else:
1360                            Data['type'] = 'PWDR'
1361                            Data['color'] = 'Paired'
1362                            Data['tilt'] = 0.0
1363                            Data['rotation'] = 0.0
1364                            Data['showLines'] = False
1365                            Data['ring'] = []
1366                            Data['rings'] = []
1367                            Data['cutoff'] = 10
1368                            Data['pixLimit'] = 20
1369                            Data['calibdmin'] = 0.5
1370                            Data['calibskip'] = 0
1371                            Data['edgemin'] = 100000000
1372                            Data['ellipses'] = []
1373                            Data['GonioAngles'] = [0.,0.,0.]
1374                            Data['calibrant'] = ''
1375                            Data['IOtth'] = [2.0,5.0]
1376                            Data['LRazimuth'] = [135,225]
1377                            Data['azmthOff'] = 0.0
1378                            Data['outChannels'] = 2500
1379                            Data['outAzimuths'] = 1
1380                            Data['centerAzm'] = False
1381                            Data['fullIntegrate'] = False
1382                            Data['setRings'] = False
1383                            Data['background image'] = ['',1.0]                           
1384                        Data['setDefault'] = False
1385                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1386                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
1387                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1388                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1389                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
1390                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'strain':np.zeros((3,3))})
1391                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
1392                        self.PickId = Id
1393                        self.Image = Id
1394                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1395                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
1396        finally:
1397            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
1398            os.chdir(path)
1399            dlg.Destroy()
1400
1401    def CheckNotebook(self):
1402        '''Make sure the data tree has the minimally expected controls.
1403        (BHT) correct?
1404        '''
1405        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
1406            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
1407            self.PatternTree.SetItemPyData(sub,[''])
1408        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
1409            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
1410            self.PatternTree.SetItemPyData(sub,{'deriv type':'analytic Hessian',    #default controls
1411                'min dM/M':0.0001,'shift factor':1.,'max cyc':3,'F**2':True,
1412                'minF/sig':0,})
1413        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
1414            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
1415            self.PatternTree.SetItemPyData(sub,{})
1416        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
1417            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
1418            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
1419        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
1420            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
1421            self.PatternTree.SetItemPyData(sub,{})
1422        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
1423            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
1424            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
1425                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
1426               
1427    class CopyDialog(wx.Dialog):
1428        '''Creates a dialog for copying control settings between
1429        data tree items'''
1430        def __init__(self,parent,title,text,data):
1431            wx.Dialog.__init__(self,parent,-1,title, 
1432                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1433            self.data = data
1434            panel = wx.Panel(self)
1435            mainSizer = wx.BoxSizer(wx.VERTICAL)
1436            topLabl = wx.StaticText(panel,-1,text)
1437            mainSizer.Add((10,10),1)
1438            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1439            mainSizer.Add((10,10),1)
1440            ncols = len(data)/40+1
1441            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=ncols,hgap=2,vgap=2)
1442            for id,item in enumerate(self.data):
1443                ckbox = wx.CheckBox(panel,id,item[1])
1444                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
1445                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
1446            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1447            OkBtn = wx.Button(panel,-1,"Ok")
1448            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1449            cancelBtn = wx.Button(panel,-1,"Cancel")
1450            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1451            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1452            btnSizer.Add((20,20),1)
1453            btnSizer.Add(OkBtn)
1454            btnSizer.Add((20,20),1)
1455            btnSizer.Add(cancelBtn)
1456            btnSizer.Add((20,20),1)
1457           
1458            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1459            panel.SetSizer(mainSizer)
1460            panel.Fit()
1461            self.Fit()
1462       
1463        def OnCopyChange(self,event):
1464            id = event.GetId()
1465            self.data[id][0] = self.FindWindowById(id).GetValue()       
1466           
1467        def OnOk(self,event):
1468            parent = self.GetParent()
1469            parent.Raise()
1470            self.EndModal(wx.ID_OK)             
1471           
1472        def OnCancel(self,event):
1473            parent = self.GetParent()
1474            parent.Raise()
1475            self.EndModal(wx.ID_CANCEL)             
1476           
1477        def GetData(self):
1478            return self.data
1479       
1480    class SumDialog(wx.Dialog):
1481        'Allows user to supply scale factor(s) when summing data'
1482        def __init__(self,parent,title,text,dataType,data):
1483            wx.Dialog.__init__(self,parent,-1,title, 
1484                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1485            self.data = data
1486            panel = wx.Panel(self)
1487            mainSizer = wx.BoxSizer(wx.VERTICAL)
1488            topLabl = wx.StaticText(panel,-1,text)
1489            mainSizer.Add((10,10),1)
1490            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1491            mainSizer.Add((10,10),1)
1492            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1493            for id,item in enumerate(self.data[:-1]):
1494                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
1495                name.SetEditable(False)
1496                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1497                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1498                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1499                dataGridSizer.Add(scale,0,wx.LEFT,10)
1500                dataGridSizer.Add(name,0,wx.RIGHT,10)
1501            if dataType:
1502                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
1503                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
1504                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
1505                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
1506                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
1507                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
1508            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1509            OkBtn = wx.Button(panel,-1,"Ok")
1510            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1511            cancelBtn = wx.Button(panel,-1,"Cancel")
1512            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1513            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1514            btnSizer.Add((20,20),1)
1515            btnSizer.Add(OkBtn)
1516            btnSizer.Add((20,20),1)
1517            btnSizer.Add(cancelBtn)
1518            btnSizer.Add((20,20),1)
1519           
1520            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1521            panel.SetSizer(mainSizer)
1522            panel.Fit()
1523            self.Fit()
1524
1525        def OnScaleChange(self,event):
1526            id = event.GetId()
1527            value = self.FindWindowById(id).GetValue()
1528            try:
1529                self.data[id][0] = float(value)
1530                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1531            except ValueError:
1532                if value and '-' not in value[0]:
1533                    print 'bad input - numbers only'
1534                    self.FindWindowById(id).SetValue('0.000')
1535           
1536        def OnNameChange(self,event):
1537            self.data[-1] = self.name.GetValue() 
1538           
1539        def OnOk(self,event):
1540            parent = self.GetParent()
1541            parent.Raise()
1542            self.EndModal(wx.ID_OK)             
1543           
1544        def OnCancel(self,event):
1545            parent = self.GetParent()
1546            parent.Raise()
1547            self.EndModal(wx.ID_CANCEL)             
1548           
1549        def GetData(self):
1550            return self.data
1551           
1552    class ConstraintDialog(wx.Dialog):
1553        '''Window to edit Constraint values
1554        '''
1555        def __init__(self,parent,title,text,data,separator='*'):
1556            wx.Dialog.__init__(self,parent,-1,title, 
1557                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1558            self.data = data
1559            panel = wx.Panel(self)
1560            mainSizer = wx.BoxSizer(wx.VERTICAL)
1561            topLabl = wx.StaticText(panel,-1,text)
1562            mainSizer.Add((10,10),1)
1563            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1564            mainSizer.Add((10,10),1)
1565            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1566            for id,item in enumerate(self.data[:-1]):
1567                lbl = item[1]
1568                if lbl[-1] != '=': lbl += ' ' + separator + ' '
1569                name = wx.StaticText(panel,-1,lbl,size=wx.Size(200,20),
1570                                     style=wx.ALIGN_RIGHT)
1571                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1572                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1573                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1574                dataGridSizer.Add(name,0,wx.LEFT,10)
1575                dataGridSizer.Add(scale,0,wx.RIGHT,10)
1576            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1577            OkBtn = wx.Button(panel,-1,"Ok")
1578            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1579            cancelBtn = wx.Button(panel,-1,"Cancel")
1580            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1581            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1582            btnSizer.Add((20,20),1)
1583            btnSizer.Add(OkBtn)
1584            btnSizer.Add((20,20),1)
1585            btnSizer.Add(cancelBtn)
1586            btnSizer.Add((20,20),1)
1587           
1588            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1589            panel.SetSizer(mainSizer)
1590            panel.Fit()
1591            self.Fit()
1592            self.CenterOnParent()
1593           
1594        def OnNameChange(self,event):
1595            self.data[-1] = self.name.GetValue() 
1596           
1597        def OnScaleChange(self,event):
1598            id = event.GetId()
1599            value = self.FindWindowById(id).GetValue()
1600            try:
1601                self.data[id][0] = float(value)
1602                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1603            except ValueError:
1604                if value and '-' not in value[0]:
1605                    print 'bad input - numbers only'
1606                    self.FindWindowById(id).SetValue('0.000')
1607           
1608        def OnOk(self,event):
1609            parent = self.GetParent()
1610            parent.Raise()
1611            self.EndModal(wx.ID_OK)             
1612           
1613        def OnCancel(self,event):
1614            parent = self.GetParent()
1615            parent.Raise()
1616            self.EndModal(wx.ID_CANCEL)             
1617           
1618        def GetData(self):
1619            return self.data
1620           
1621    def OnPwdrSum(self,event):
1622        'Sum together powder data(?)'
1623        TextList = []
1624        DataList = []
1625        SumList = []
1626        Names = []
1627        Inst = None
1628        SumItemList = []
1629        Comments = ['Sum equals: \n']
1630        if self.PatternTree.GetCount():
1631            item, cookie = self.PatternTree.GetFirstChild(self.root)
1632            while item:
1633                name = self.PatternTree.GetItemText(item)
1634                Names.append(name)
1635                if 'PWDR' in name:
1636                    TextList.append([0.0,name])
1637                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
1638                    if not Inst:
1639                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
1640                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1641            if len(TextList) < 2:
1642                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
1643                return
1644            TextList.append('default_sum_name')               
1645            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
1646            try:
1647                if dlg.ShowModal() == wx.ID_OK:
1648                    lenX = 0
1649                    Xminmax = [0,0]
1650                    Xsum = []
1651                    Ysum = []
1652                    Vsum = []
1653                    result = dlg.GetData()
1654                    for i,item in enumerate(result[:-1]):
1655                        scale,name = item
1656                        data = DataList[i]
1657                        if scale:
1658                            Comments.append("%10.3f %s" % (scale,' * '+name))
1659                            x,y,w,yc,yb,yd = data   #numpy arrays!
1660                            v = 1./w
1661                            if lenX:
1662                                if lenX != len(x):
1663                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
1664                                        '\nExpected:'+str(lenX)+ \
1665                                        '\nFound:   '+str(len(x))+'\nfor '+name)
1666                                    return
1667                            else:
1668                                lenX = len(x)
1669                            if Xminmax[1]:
1670                                if Xminmax != [x[0],x[-1]]:
1671                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
1672                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
1673                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
1674                                    return
1675                                else:
1676                                    for j,yi in enumerate(y):
1677                                         Ysum[j] += scale*yi
1678                                         Vsum[j] += abs(scale)*v[j]
1679                            else:
1680                                Xminmax = [x[0],x[-1]]
1681                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
1682                                for j,yi in enumerate(y):
1683                                    Xsum.append(x[j])
1684                                    Ysum.append(scale*yi)
1685                                    Vsum.append(abs(scale*v[j]))
1686                    Wsum = 1./np.array(Vsum)
1687                    outname = 'PWDR '+result[-1]
1688                    Id = 0
1689                    if outname in Names:
1690                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1691                        try:
1692                            if dlg2.ShowModal() == wx.ID_OK:
1693                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1694                                self.PatternTree.Delete(Id)
1695                        finally:
1696                            dlg2.Destroy()
1697                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1698                    if Id:
1699                        Sample = G2pdG.SetDefaultSample()
1700                        self.PatternTree.SetItemPyData(Id,[{'wtFactor':1.0},[np.array(Xsum),np.array(Ysum),np.array(Wsum),
1701                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
1702                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
1703                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
1704                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
1705                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1706                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
1707                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1708                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
1709                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
1710                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1711                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
1712                        self.PatternTree.SelectItem(Id)
1713                        self.PatternTree.Expand(Id)
1714                   
1715            finally:
1716                dlg.Destroy()
1717
1718    def OnImageSum(self,event):
1719        'Sum together image data(?)'
1720        TextList = []
1721        DataList = []
1722        SumList = []
1723        Names = []
1724        Inst = []
1725        SumItemList = []
1726        Comments = ['Sum equals: \n']
1727        if self.PatternTree.GetCount():
1728            item, cookie = self.PatternTree.GetFirstChild(self.root)
1729            while item:
1730                name = self.PatternTree.GetItemText(item)
1731                Names.append(name)
1732                if 'IMG' in name:
1733                    TextList.append([0.0,name])
1734                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
1735                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
1736                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1737            if len(TextList) < 2:
1738                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
1739                return
1740            TextList.append('default_sum_name')               
1741            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
1742            try:
1743                if dlg.ShowModal() == wx.ID_OK:
1744                    imSize = 0
1745                    result = dlg.GetData()
1746                    First = True
1747                    Found = False
1748                    for i,item in enumerate(result[:-1]):
1749                        scale,name = item
1750                        data = DataList[i]
1751                        if scale:
1752                            Found = True                               
1753                            Comments.append("%10.3f %s" % (scale,' * '+name))
1754                            Npix,imagefile = data
1755                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
1756                            if First:
1757                                newImage = np.zeros_like(image)
1758                                First = False
1759                            if imSize:
1760                                if imSize != Npix:
1761                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
1762                                        '\nExpected:'+str(imSize)+ \
1763                                        '\nFound:   '+str(Npix)+'\nfor '+name)
1764                                    return
1765                                newImage = newImage+scale*image
1766                            else:
1767                                imSize = Npix
1768                                newImage = newImage+scale*image
1769                            del(image)
1770                    if not Found:
1771                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
1772                        return
1773                       
1774                    newImage = np.asfarray(newImage,dtype=np.float32)                       
1775                    outname = 'IMG '+result[-1]
1776                    Id = 0
1777                    if outname in Names:
1778                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1779                        try:
1780                            if dlg2.ShowModal() == wx.ID_OK:
1781                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1782                        finally:
1783                            dlg2.Destroy()
1784                    else:
1785                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1786                    if Id:
1787                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
1788                            'G2img files (*.G2img)|*.G2img', 
1789                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1790                        if dlg.ShowModal() == wx.ID_OK:
1791                            newimagefile = dlg.GetPath()
1792                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
1793                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
1794                            Imax = np.amax(newImage)
1795                            Imin = np.amin(newImage)
1796                            newImage = []
1797                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
1798                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1799                        del(newImage)
1800                        if self.imageDefault:
1801                            Data = copy.copy(self.imageDefault)
1802                        Data['showLines'] = True
1803                        Data['ring'] = []
1804                        Data['rings'] = []
1805                        Data['cutoff'] = 10
1806                        Data['pixLimit'] = 20
1807                        Data['ellipses'] = []
1808                        Data['calibrant'] = ''
1809                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1810                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
1811                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1812                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1813                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),{})
1814                        self.PatternTree.SelectItem(Id)
1815                        self.PatternTree.Expand(Id)
1816                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
1817                        self.Image = self.PickId
1818            finally:
1819                dlg.Destroy()
1820                     
1821    def OnAddPhase(self,event):
1822        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
1823        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
1824            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
1825        else:
1826            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1827        PhaseName = ''
1828        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
1829            style=wx.OK)
1830        if dlg.ShowModal() == wx.ID_OK:
1831            PhaseName = dlg.GetValue()
1832        dlg.Destroy()
1833        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
1834        E,SGData = G2spc.SpcGroup('P 1')
1835        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
1836       
1837    def OnDeletePhase(self,event):
1838        'Delete a phase from the tree. Called by Data/Delete Phase menu'
1839        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
1840        if self.dataFrame:
1841            self.dataFrame.Clear() 
1842        TextList = []
1843        DelList = []
1844        DelItemList = []
1845        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
1846            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1847        else:
1848            return
1849        if sub:
1850            item, cookie = self.PatternTree.GetFirstChild(sub)
1851            while item:
1852                TextList.append(self.PatternTree.GetItemText(item))
1853                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
1854            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
1855            try:
1856                if dlg.ShowModal() == wx.ID_OK:
1857                    result = dlg.GetSelections()
1858                    for i in result: DelList.append([i,TextList[i]])
1859                    item, cookie = self.PatternTree.GetFirstChild(sub)
1860                    i = 0
1861                    while item:
1862                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
1863                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1864                        i += 1
1865                    for item in DelItemList:
1866                        name = self.PatternTree.GetItemText(item)
1867                        self.PatternTree.Delete(item)
1868                        self.G2plotNB.Delete(name)
1869                    item, cookie = self.PatternTree.GetFirstChild(self.root)
1870                    while item:
1871                        name = self.PatternTree.GetItemText(item)
1872                        if 'PWDR' in name:
1873                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
1874                            refList = self.PatternTree.GetItemPyData(Id)
1875                            for i,item in DelList:
1876                                del(refList[item])
1877                            self.PatternTree.SetItemPyData(Id,refList)
1878                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1879            finally:
1880                dlg.Destroy()
1881               
1882    def OnRenameData(self,event):
1883        'Renames an existing phase. Called by Data/Rename Phase menu'
1884        name = self.PatternTree.GetItemText(self.PickId)     
1885        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
1886            dataType = name[:name.index(' ')+1]                 #includes the ' '
1887            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
1888                defaultValue=name[name.index(' ')+1:])
1889            try:
1890                if dlg.ShowModal() == wx.ID_OK:
1891                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
1892            finally:
1893                dlg.Destroy()
1894       
1895    def GetFileList(self,fileType,skip=None):        #potentially useful?
1896        'Appears unused. Note routine of same name in GSASIIpwdGUI'
1897        fileList = []
1898        Source = ''
1899        id, cookie = self.PatternTree.GetFirstChild(self.root)
1900        while id:
1901            name = self.PatternTree.GetItemText(id)
1902            if fileType in name:
1903                if id == skip:
1904                    Source = name
1905                else:
1906                    fileList.append([False,name,id])
1907            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1908        if skip:
1909            return fileList,Source
1910        else:
1911            return fileList
1912           
1913    def OnDataDelete(self, event):
1914        '''Delete one or more histograms from data tree. Called by the
1915        Data/DeleteData menu
1916        '''
1917        TextList = ['All Data']
1918        DelList = []
1919        DelItemList = []
1920        ifPWDR = False
1921        ifIMG = False
1922        ifHKLF = False
1923        ifPDF = False
1924        if self.PatternTree.GetCount():
1925            item, cookie = self.PatternTree.GetFirstChild(self.root)
1926            while item:
1927                name = self.PatternTree.GetItemText(item)
1928                if name not in ['Notebook','Controls','Covariance','Constraints','Restraints','Phases']:
1929                    if 'PWDR' in name: ifPWDR = True
1930                    if 'IMG' in name: ifIMG = True
1931                    if 'HKLF' in name: ifHKLF = True
1932                    if 'PDF' in name: ifPDF = True
1933                    TextList.append(name)
1934                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1935            if ifPWDR: TextList.insert(1,'All PWDR')
1936            if ifIMG: TextList.insert(1,'All IMG')
1937            if ifHKLF: TextList.insert(1,'All HKLF')
1938            if ifPDF: TextList.insert(1,'All PDF')               
1939            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
1940            try:
1941                if dlg.ShowModal() == wx.ID_OK:
1942                    result = dlg.GetSelections()
1943                    for i in result: DelList.append(TextList[i])
1944                    if 'All Data' in DelList:
1945                        DelList = [item for item in TextList if item[:3] != 'All']
1946                    elif 'All PWDR' in DelList:
1947                        DelList = [item for item in TextList if item[:4] == 'PWDR']
1948                    elif 'All IMG' in DelList:
1949                        DelList = [item for item in TextList if item[:3] == 'IMG']
1950                    elif 'All HKLF' in DelList:
1951                        DelList = [item for item in TextList if item[:4] == 'HKLF']
1952                    elif 'All PDF' in DelList:
1953                        DelList = [item for item in TextList if item[:3] == 'PDF']
1954                    item, cookie = self.PatternTree.GetFirstChild(self.root)
1955                    while item:
1956                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
1957                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1958                    for item in DelItemList:
1959                        self.PatternTree.Delete(item)
1960                    self.PickId = 0
1961                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
1962            finally:
1963                dlg.Destroy()
1964
1965    def OnFileOpen(self, event, filename=None):
1966        '''Reads in a GSAS-II .gpx project file in response to the
1967        File/Open Project menu button
1968        '''
1969        result = wx.ID_OK
1970        Id = 0
1971        if self.PatternTree.GetChildrenCount(self.root,False):
1972            if self.dataFrame:
1973                self.dataFrame.Clear() 
1974            dlg = wx.MessageDialog(
1975                self,
1976                'Do you want to overwrite the current project? '
1977                'Any unsaved changes will be lost. Press OK to continue.',
1978                'Overwrite?',  wx.OK | wx.CANCEL)
1979            try:
1980                result = dlg.ShowModal()
1981                if result == wx.ID_OK:
1982                    self.PatternTree.DeleteChildren(self.root)
1983                    self.GSASprojectfile = ''
1984                    if self.HKL: self.HKL = []
1985                    if self.G2plotNB.plotList:
1986                        self.G2plotNB.clear()
1987            finally:
1988                dlg.Destroy()
1989        if result != wx.ID_OK: return
1990
1991        if not filename:
1992            if self.dataDisplay: self.dataDisplay.Destroy()
1993            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
1994                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
1995            try:
1996                if dlg.ShowModal() != wx.ID_OK: return
1997                self.GSASprojectfile = dlg.GetPath()
1998                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
1999                self.dirname = dlg.GetDirectory()
2000            finally:
2001                dlg.Destroy()
2002        else:
2003            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2004            self.dirname = os.path.split(filename)[0]
2005
2006        G2IO.ProjFileOpen(self)
2007        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2008        self.PatternTree.Expand(self.root)
2009        self.HKL = []
2010        item, cookie = self.PatternTree.GetFirstChild(self.root)
2011        while item and not Id:
2012            name = self.PatternTree.GetItemText(item)
2013            if name[:4] in ['PWDR','HKLF','IMG ','PDF ']:
2014                Id = item
2015            elif name == 'Controls':
2016                data = self.PatternTree.GetItemPyData(item)
2017                if data:
2018                    for item in self.Refine: item.Enable(True)
2019                    for item in self.SeqRefine: item.Enable(True)
2020            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2021        if Id:
2022            self.PatternTree.SelectItem(Id)
2023        self.CheckNotebook()
2024        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2025
2026    def OnFileClose(self, event):
2027        '''Clears the data tree in response to the
2028        File/Close Project menu button. User is given option to save
2029        the project.
2030        '''
2031        if self.dataFrame:
2032            self.dataFrame.Clear()
2033            self.dataFrame.SetLabel('GSAS-II data display') 
2034        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2035        try:
2036            result = dlg.ShowModal()
2037            if result == wx.ID_OK:
2038                self.OnFileSaveMenu(event)
2039            if result != wx.ID_CANCEL:
2040                self.GSASprojectfile = ''
2041                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2042                self.PatternTree.DeleteChildren(self.root)
2043                if self.HKL: self.HKL = []
2044                if self.G2plotNB.plotList:
2045                    self.G2plotNB.clear()
2046        finally:
2047            dlg.Destroy()
2048
2049    def OnFileSave(self, event):
2050        '''Save the current project in response to the
2051        File/Save Project menu button
2052        '''
2053       
2054        if self.GSASprojectfile: 
2055            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2056            G2IO.ProjFileSave(self)
2057        else:
2058            self.OnFileSaveas(event)
2059
2060    def OnFileSaveas(self, event):
2061        '''Save the current project in response to the
2062        File/Save as menu button
2063        '''
2064        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2065            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2066        try:
2067            if dlg.ShowModal() == wx.ID_OK:
2068                self.GSASprojectfile = dlg.GetPath()
2069                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2070                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2071                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2072                G2IO.ProjFileSave(self)
2073                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2074        finally:
2075            dlg.Destroy()
2076
2077    def ExitMain(self, event):
2078        '''Called if the main window is closed'''
2079        if self.undofile:
2080            os.remove(self.undofile)
2081        sys.exit()
2082       
2083    def OnFileExit(self, event):
2084        '''Called in response to the File/Quit menu button'''
2085        if self.dataFrame:
2086            self.dataFrame.Clear() 
2087            self.dataFrame.Destroy()
2088        self.Close()
2089       
2090    def OnExportPatterns(self,event):
2091        names = ['All']
2092        exports = []
2093        item, cookie = self.PatternTree.GetFirstChild(self.root)
2094        while item:
2095            name = self.PatternTree.GetItemText(item)
2096            if 'PWDR' in name:
2097                names.append(name)
2098            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2099        if names:
2100            dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2101            if dlg.ShowModal() == wx.ID_OK:
2102                sel = dlg.GetSelections()
2103                if sel[0] == 0:
2104                    exports = names[1:]
2105                else:
2106                    for x in sel:
2107                        exports.append(names[x])
2108            dlg.Destroy()
2109        if exports:
2110            dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '', 
2111                'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2112                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2113            try:
2114                if dlg.ShowModal() == wx.ID_OK:
2115                    powderfile = dlg.GetPath()
2116                    powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2117                    if 'fxye' in powderfile:
2118                        G2IO.powderFxyeSave(self,exports,powderfile)
2119                    else:       #just xye
2120                        G2IO.powderXyeSave(self,exports,powderfile)
2121            finally:
2122                dlg.Destroy()
2123       
2124    def OnExportPeakList(self,event):
2125        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2126            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2127        try:
2128            if dlg.ShowModal() == wx.ID_OK:
2129                self.peaklistfile = dlg.GetPath()
2130                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2131                file = open(self.peaklistfile,'w')               
2132                item, cookie = self.PatternTree.GetFirstChild(self.root)
2133                while item:
2134                    name = self.PatternTree.GetItemText(item)
2135                    if 'PWDR' in name:
2136                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2137                        while item2:
2138                            name2 = self.PatternTree.GetItemText(item2)
2139                            if name2 == 'Peak List':
2140                                peaks = self.PatternTree.GetItemPyData(item2)
2141                                file.write("%s \n" % (name+' Peak List'))               
2142                                for peak in peaks:
2143                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2144                                        (peak[0],peak[2],peak[4],peak[6]))
2145                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2146                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2147                file.close()
2148        finally:
2149            dlg.Destroy()
2150       
2151    def OnExportHKL(self,event):
2152        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2153            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2154        try:
2155            if dlg.ShowModal() == wx.ID_OK:
2156                self.peaklistfile = dlg.GetPath()
2157                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2158                file = open(self.peaklistfile,'w')               
2159                item, cookie = self.PatternTree.GetFirstChild(self.root)
2160                while item:
2161                    name = self.PatternTree.GetItemText(item)
2162                    if 'PWDR' in name:
2163                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2164                        while item2:
2165                            name2 = self.PatternTree.GetItemText(item2)
2166                            if name2 == 'Reflection Lists':
2167                                data = self.PatternTree.GetItemPyData(item2)
2168                                phases = data.keys()
2169                                for phase in phases:
2170                                    peaks = data[phase]
2171                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2172                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2173                                    for peak in peaks:
2174                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2175                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2176                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2177                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2178                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2179                file.close()
2180        finally:
2181            dlg.Destroy()
2182       
2183    def OnExportPDF(self,event):
2184        #need S(Q) and G(R) to be saved here - probably best from selection?
2185        names = ['All']
2186        exports = []
2187        item, cookie = self.PatternTree.GetFirstChild(self.root)
2188        while item:
2189            name = self.PatternTree.GetItemText(item)
2190            if 'PDF' in name:
2191                names.append(name)
2192            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2193        if names:
2194            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2195            if dlg.ShowModal() == wx.ID_OK:
2196                sel = dlg.GetSelections()
2197                if sel[0] == 0:
2198                    exports = names[1:]
2199                else:
2200                    for x in sel:
2201                        exports.append(names[x])
2202            dlg.Destroy()
2203        if exports:
2204            G2IO.PDFSave(self,exports)
2205       
2206    def OnExportPhase(self,event):
2207        event.Skip()
2208       
2209    def OnExportCIF(self,event):
2210        event.Skip()
2211
2212    def OnMakePDFs(self,event):
2213        '''Calculates PDFs
2214        '''
2215        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2216        TextList = ['All PWDR']
2217        PDFlist = []
2218        Names = []
2219        if self.PatternTree.GetCount():
2220            id, cookie = self.PatternTree.GetFirstChild(self.root)
2221            while id:
2222                name = self.PatternTree.GetItemText(id)
2223                Names.append(name)
2224                if 'PWDR' in name:
2225                    TextList.append(name)
2226                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2227            if len(TextList) == 1:
2228                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2229                return
2230            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2231            try:
2232                if dlg.ShowModal() == wx.ID_OK:
2233                    result = dlg.GetSelections()
2234                    for i in result: PDFlist.append(TextList[i])
2235                    if 0 in result:
2236                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2237                    for item in PDFlist:
2238                        PWDRname = item[4:]
2239                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2240                        Data = {
2241                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2242                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2243                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2244                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2245                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2246                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2247                            'Lorch':True,}
2248                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2249                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2250                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2251                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2252                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2253                for item in self.ExportPDF: item.Enable(True)
2254            finally:
2255                dlg.Destroy()
2256               
2257    def GetPWDRdatafromTree(self,PWDRname):
2258        ''' Returns powder data from GSASII tree
2259
2260        :param str PWDRname: a powder histogram name as obtained from
2261          :meth:`GSASIIstruct.GetHistogramNames`
2262
2263        :returns: PWDRdata = powder data dictionary with
2264          Powder data arrays, Limits, Instrument Parameters,
2265          Sample Parameters           
2266        '''
2267        PWDRdata = {}
2268        try:
2269            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2270        except ValueError:
2271            PWDRdata['wtFactor'] = 1.0
2272        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2273        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2274        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2275        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2276        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2277        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2278        if 'ranId' not in PWDRdata['Sample Parameters']:
2279            PWDRdata['Sample Parameters']['ranId'] = ran.randint(0,sys.maxint)
2280        PWDRdata['ranId'] = PWDRdata['Sample Parameters']['ranId']
2281        return PWDRdata
2282
2283    def GetHKLFdatafromTree(self,HKLFname):
2284        ''' Returns single crystal data from GSASII tree
2285
2286        :param str HKLFname: a single crystal histogram name as obtained
2287          from
2288          :meth:`GSASIIstruct.GetHistogramNames`
2289
2290        :returns: HKLFdata = single crystal data list of reflections
2291
2292        '''
2293        HKLFdata = {}
2294        try:
2295            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2296        except ValueError:
2297            HKLFdata['wtFactor'] = 1.0
2298        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2299        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2300        return HKLFdata
2301       
2302    def GetPhaseData(self):
2303        '''Returns a list of defined phases. Used only in GSASIIgrid
2304        Note routine :meth:`GSASIIstruct.GetPhaseData` also exists.
2305        '''
2306        phaseData = {}
2307        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2308            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2309        else:
2310            print 'no phases to be refined'
2311            return
2312        if sub:
2313            item, cookie = self.PatternTree.GetFirstChild(sub)
2314            while item:
2315                phaseName = self.PatternTree.GetItemText(item)
2316                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2317                if 'ranId' not in phaseData[phaseName]:
2318                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2319                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2320        return phaseData               
2321                   
2322    def GetUsedHistogramsAndPhasesfromTree(self):
2323        ''' Returns all histograms that are found in any phase
2324        and any phase that uses a histogram
2325        :returns: two dicts:
2326
2327            * Histograms = dictionary of histograms as {name:data,...}
2328            * Phases = dictionary of phases that use histograms
2329        '''
2330        phaseData = self.GetPhaseData()
2331        if not phaseData:
2332            return {},{}
2333        Histograms = {}
2334        Phases = {}
2335        for phase in phaseData:
2336            Phase = phaseData[phase]
2337            if Phase['Histograms']:
2338                if phase not in Phases:
2339                    Phases[phase] = Phase
2340                for hist in Phase['Histograms']:
2341                    if hist not in Histograms:
2342                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
2343                        if 'PWDR' in hist[:4]: 
2344                            Histograms[hist] = self.GetPWDRdatafromTree(item)
2345                        elif 'HKLF' in hist[:4]:
2346                            Histograms[hist] = self.GetHKLFdatafromTree(item)
2347                        #future restraint, etc. histograms here
2348        return Histograms,Phases
2349       
2350    class ViewParmDialog(wx.Dialog):
2351        '''Window to show all parameters in the refinement.
2352        Called from :meth:`OnViewLSParms`
2353        '''
2354        def __init__(self,parent,title,parmDict):
2355            wx.Dialog.__init__(self,parent,-1,title,size=(300,430),
2356                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2357            panel = wx.Panel(self,size=(300,430))
2358            parmNames = parmDict.keys()
2359            parmNames.sort()
2360            parmText = ' p:h:Parameter       refine?              value\n'
2361            for name in parmNames:
2362                parmData = parmDict[name]
2363                try:
2364                    parmText += ' %s \t%12.4g \n'%(name.ljust(19)+'\t'+parmData[1],parmData[0])
2365                except TypeError:
2366                    pass
2367            parmTable = wx.TextCtrl(panel,-1,parmText,
2368                style=wx.TE_MULTILINE|wx.TE_READONLY,size=(290,400))
2369            mainSizer = wx.BoxSizer(wx.VERTICAL)
2370            mainSizer.Add(parmTable)
2371            panel.SetSizer(mainSizer)
2372                           
2373    def OnViewLSParms(self,event):
2374        '''Displays a window showing all parameters in the refinement.
2375        Called from the Calculate/View LS Parms menu.
2376        '''
2377        parmDict = {}
2378        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
2379        rigidbodyDict = self.PatternTree.GetItemPyData(   
2380            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
2381        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2382        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2383        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
2384        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
2385        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
2386        varyList = rbVary+phaseVary+hapVary+histVary
2387        parmDict.update(rbDict)
2388        parmDict.update(phaseDict)
2389        parmDict.update(hapDict)
2390        parmDict.update(histDict)
2391        for parm in parmDict:
2392            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
2393                'Omega','Chi','Phi','nDebye','nPeaks']:
2394                parmDict[parm] = [parmDict[parm],' ']
2395            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
2396                parmDict[parm] = [parmDict[parm],' ']
2397            elif parm in varyList:
2398                parmDict[parm] = [parmDict[parm],'True']
2399            else:
2400                parmDict[parm] = [parmDict[parm],'False']
2401        parmDict[' Num refined'] = [len(varyList),'']
2402        dlg = self.ViewParmDialog(self,'Parameters for least squares',parmDict)
2403        try:
2404            if dlg.ShowModal() == wx.ID_OK:
2405                print 'do something with changes?? - No!'
2406        finally:
2407            dlg.Destroy()
2408       
2409    def OnRefine(self,event):
2410        '''Perform a refinement.
2411        Called from the Calculate/Refine menu.
2412        '''       
2413        self.OnFileSave(event)
2414        # check that constraints are OK here
2415        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2416        if errmsg:
2417            print('Error in constraints:\n'+errmsg+
2418                  '\nRefinement not possible')
2419            self.ErrorDialog('Constraint Error',
2420                             'Error in constraints:\n'+errmsg+
2421                             '\nRefinement not possible')
2422            return
2423        if warnmsg:
2424            print('Conflict between refinment flag settings and constraints:\n'+
2425                  warnmsg+'\nRefinement not possible')
2426            self.ErrorDialog('Refinement Flag Error',
2427                             'Conflict between refinment flag settings and constraints:\n'+
2428                             warnmsg+
2429                             '\nRefinement not possible')
2430            return
2431        #works - but it'd be better if it could restore plots
2432        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
2433            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2434        screenSize = wx.ClientDisplayRect()
2435        Size = dlg.GetSize()
2436        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2437        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2438        dlg.SetSize(Size)
2439        Rw = 100.00
2440        try:
2441            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
2442        finally:
2443            dlg.Destroy()
2444        oldId =  self.PatternTree.GetSelection()
2445        oldName = self.PatternTree.GetItemText(oldId)
2446        parentId = self.PatternTree.GetItemParent(oldId)
2447        parentName = ''
2448        if parentId:
2449            parentName = self.PatternTree.GetItemText(parentId)
2450        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
2451        try:
2452            if dlg.ShowModal() == wx.ID_OK:
2453                Id = 0
2454                self.PatternTree.DeleteChildren(self.root)
2455                if self.HKL: self.HKL = []
2456                if self.G2plotNB.plotList:
2457                    self.G2plotNB.clear()
2458                G2IO.ProjFileOpen(self)
2459                item, cookie = self.PatternTree.GetFirstChild(self.root)
2460                while item and not Id:
2461                    name = self.PatternTree.GetItemText(item)
2462                    if name[:4] in ['PWDR','HKLF']:
2463                        Id = item
2464                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2465                if parentName:
2466                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
2467                    if parentId:
2468                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
2469                    else:
2470                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
2471                    self.PatternTree.SelectItem(itemId)
2472                elif Id:
2473                    self.PatternTree.SelectItem(Id)
2474        finally:
2475            dlg.Destroy()
2476
2477    def OnSeqRefine(self,event):
2478        '''Perform a sequential refinement.
2479        Called from the Calculate/Sequential refine menu.
2480        '''       
2481        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequental results')
2482        if not Id:
2483            Id = self.PatternTree.AppendItem(self.root,text='Sequental results')
2484            self.PatternTree.SetItemPyData(Id,{})           
2485        self.OnFileSave(event)
2486        # check that constraints are OK here
2487        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2488        if errmsg:
2489            print('Error in constraints:\n'+errmsg+
2490                  '\nRefinement not possible')
2491            self.ErrorDialog('Constraint Error',
2492                             'Error in constraints:\n'+errmsg+
2493                             '\nRefinement not possible')
2494            return
2495        if warnmsg:
2496            print('Conflict between refinment flag settings and constraints:\n'+
2497                  warnmsg+'\nRefinement not possible')
2498            self.ErrorDialog('Refinement Flag Error',
2499                             'Conflict between refinment flag settings and constraints:\n'+
2500                             warnmsg+'\nRefinement not possible')
2501            return
2502        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
2503            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2504        screenSize = wx.ClientDisplayRect()
2505        Size = dlg.GetSize()
2506        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2507        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2508        dlg.SetSize(Size)
2509        try:
2510            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
2511        finally:
2512            dlg.Destroy()       
2513        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
2514        try:
2515            if dlg.ShowModal() == wx.ID_OK:
2516                Id = 0
2517                self.PatternTree.DeleteChildren(self.root)
2518                if self.HKL: self.HKL = []
2519                if self.G2plotNB.plotList:
2520                    self.G2plotNB.clear()
2521                G2IO.ProjFileOpen(self)
2522                item, cookie = self.PatternTree.GetFirstChild(self.root)
2523                while item and not Id:
2524                    name = self.PatternTree.GetItemText(item)
2525                    if name[:4] in ['PWDR','HKLF']:
2526                        Id = item
2527                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2528                if Id:
2529                    self.PatternTree.SelectItem(Id)
2530        finally:
2531            dlg.Destroy()
2532       
2533    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
2534        'Display an error message'
2535        result = None
2536        if parent is None:
2537            dlg = wx.MessageDialog(self, message, title,  wtype)
2538        else:
2539            dlg = wx.MessageDialog(parent, message, title,  wtype)
2540            dlg.CenterOnParent() # not working on Mac
2541        try:
2542            result = dlg.ShowModal()
2543        finally:
2544            dlg.Destroy()
2545        return result
2546
2547class GSASIImain(wx.App):
2548    '''Defines a wxApp for GSAS-II
2549
2550    Creates a wx frame (self.main) which contains the display of the
2551    data tree.
2552    '''
2553    def OnInit(self):
2554        '''Called automatically when the app is created.'''
2555        self.main = GSASII(None)
2556        self.main.Show()
2557        self.SetTopWindow(self.main)
2558        return True
2559    def MacOpenFile(self, filename):
2560        '''Called on Mac every time a file is dropped on the app when it is running,
2561        treat this like a File/Open project menu action.
2562        Should be ignored on other platforms
2563        '''
2564        self.main.OnFileOpen(None,filename)
2565
2566def main():
2567    '''Start up the GSAS-II application'''
2568    #application = GSASIImain() # don't redirect output, someday we
2569    # may want to do this if we can
2570    application = GSASIImain(0)
2571    if wxInspector: wxeye.InspectionTool().Show()
2572
2573    #application.main.OnRefine(None)
2574    application.MainLoop()
2575   
2576if __name__ == '__main__':
2577    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.