source: trunk/GSASII.py @ 1080

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

revise export menu; add multiple selection to G2gd.ItemSelector?

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