source: trunk/GSASII.py @ 1076

Last change on this file since 1076 was 1076, checked in by vondreele, 8 years ago

again fix MC/SA window scroll behavior & small start on dummy histograms

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