source: trunk/GSASII.py @ 1084

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

installer uses xterm on GNOME shortcut

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 125.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2013-10-04 21:19:03 +0000 (Fri, 04 Oct 2013) $
6# $Author: toby $
7# $Revision: 1084 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1084 2013-10-04 21:19:03Z 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: 1084 $")
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        item = submenu.Append(wx.ID_ANY,
589            help='Create a powder data set entry that will be simulated',
590            kind=wx.ITEM_NORMAL,text='Simulate a dataset')
591        self.Bind(wx.EVT_MENU, self.OnDummyPowder, id=item.GetId())
592           
593    def ReadPowderInstprm(self,instfile):       #fix the write routine for [inst1,inst2] style
594        '''Read a GSAS-II (new) instrument parameter file
595
596        :param str instfile: name of instrument parameter file
597
598        '''
599        if os.path.splitext(instfile)[1].lower() != '.instprm': # invalid file
600            return None           
601        if not os.path.exists(instfile): # no such file
602            return None
603        File = open(instfile,'r')
604        S = File.readline()
605        if not S.startswith('#GSAS-II'): # not a valid file
606            File.close()
607            return None
608        newItems = []
609        newVals = []
610        while S:
611            if S[0] == '#':
612                S = File.readline()
613                continue
614            [item,val] = S[:-1].split(':')
615            newItems.append(item)
616            try:
617                newVals.append(float(val))
618            except ValueError:
619                newVals.append(val)                       
620            S = File.readline()               
621        File.close()
622        # add a second MT dict here. TOF parms? (BHT)
623        return G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}
624       
625    def ReadPowderIparm(self,instfile,bank,databanks,rd):
626        '''Read a GSAS (old) instrument parameter file
627
628        :param str instfile: name of instrument parameter file
629
630        :param int bank: the bank number read in the raw data file
631
632        :param int databanks: the number of banks in the raw data file.
633          If the number of banks in the data and instrument parameter files
634          agree, then the sets of banks are assumed to match up and bank
635          is used to select the instrument parameter file. If not, the user
636          is asked to make a selection.
637
638        :param obj rd: the raw data (histogram) data object. This
639          sets rd.instbank.
640
641        '''
642        if not os.path.exists(instfile): # no such file
643            return {}
644        fp = 0
645        try:
646            fp = open(instfile,'Ur')
647            Iparm = {}
648            for S in fp:
649                Iparm[S[:12]] = S[12:-1]
650        except IOError:
651            print('Error reading file:'+str(instfile))
652        if fp:       
653            fp.close()
654
655        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
656        hType = Iparm['INS   HTYPE '].strip()
657        if ibanks == 1: # there is only one bank here, return it
658            rd.instbank = 1
659            return Iparm
660        if 'PNT' in hType:
661            rd.instbank = bank
662        elif ibanks != databanks:
663            # number of banks in data and prm file not not agree, need a
664            # choice from a human here
665            choices = []
666            for i in range(1,1+ibanks):
667                choices.append('Bank '+str(i))
668            bank = rd.BlockSelector(
669                choices, self,
670                title='Select an instrument parameter bank for '+
671                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
672                '\nOr use Cancel to select from the default parameter sets',
673                header='Block Selector')
674        if bank is None: return {}
675        # pull out requested bank # bank from the data, and change the bank to 1
676        IparmS = {}
677        for key in Iparm:
678            if key[4:6] == "  ":
679                IparmS[key] = Iparm[key]
680            elif int(key[4:6].strip()) == bank:
681                IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
682        rd.instbank = bank
683        return IparmS
684                       
685    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
686        '''Open and read an instrument parameter file for a data file
687        Returns the list of parameters used in the data tree
688
689        :param obj rd: the raw data (histogram) data object.
690
691        :param str prevIparm: not used
692
693        :param str lastIparmfile: Name of last instrument parameter
694          file that was read, or a empty string.
695
696        :param str lastdatafile: Name of last data file that was read.
697
698        :returns: a list of two dicts, the first containing instrument parameters
699          and the second used for future TOF datasets (timemaps?)
700
701        '''
702        def SetPowderInstParms(Iparm, rd):
703            '''extracts values from instrument parameters in rd.instdict
704            or in array Iparm.
705            Create and return the contents of the instrument parameter tree entry.
706            '''
707            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
708            # override inst values with values read from data file
709            if rd.instdict.get('type'):
710                DataType = rd.instdict.get('type')
711            data = [DataType,]
712            instname = Iparm.get('INS  1INAME ')
713            if instname:
714                rd.Sample['InstrName'] = instname.strip()
715            if 'C' in DataType:
716                wave1 = None
717                wave2 = 0.0
718                if rd.instdict.get('wave'):
719                    wl = rd.instdict.get('wave')
720                    wave1 = wl[0]
721                    if len(wl) > 1: wave2 = wl[1]
722                s = Iparm['INS  1 ICONS']
723                if not wave1:
724                    wave1 = G2IO.sfloat(s[:10])
725                    wave2 = G2IO.sfloat(s[10:20])
726                v = (wave1,wave2,
727                     G2IO.sfloat(s[20:30]),G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
728                if not v[1]:
729                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
730                    v = (v[0],v[2],v[4])
731                    codes = [0,0,0,0]
732                else:
733                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
734                    codes = [0,0,0,0,0,0]
735                data.extend(v)
736                v1 = Iparm['INS  1PRCF1 '].split()                                                 
737                v = Iparm['INS  1PRCF11'].split()
738                data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
739                azm = float(Iparm.get('INS  1DETAZM','0.0'))
740                v = Iparm['INS  1PRCF12'].split()
741                if v1[0] == 3:
742                    data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
743                else:
744                    data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
745                codes.extend([0,0,0,0,0,0,0])
746                return [G2IO.makeInstDict(names,data,codes),{}]
747            elif 'T' in DataType:
748                names = ['Type','2-theta','difC','difA','Zero','alpha','beta-0','beta-1',
749                    'beta-q','sig-0','sig-1','sig-q','X','Y','Azimuth']
750                codes = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
751                azm = 0.
752                if 'INS  1DETAZM' in Iparm:
753                    azm = float(Iparm['INS  1DETAZM'])
754                s = Iparm['INS  1BNKPAR'].split()
755                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
756                s = Iparm['INS  1 ICONS'].split()
757                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])    #difC, difA, Zero
758                s = Iparm['INS  1PRCF1 '].split()
759                pfType = int(s[0])
760                s = Iparm['INS  1PRCF11'].split()
761                if abs(pfType) == 1:
762                    data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])])
763                    s = Iparm['INS  1PRCF12'].split()
764                    data.extend([0.0,0.0,G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])
765                elif abs(pfType) in [3,4,5]:
766                    data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])
767                    if abs(pfType) == 4:
768                        data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,azm])
769                    else:
770                        s = Iparm['INS  1PRCF12'].split()
771                        data.extend([0.0,0.0,G2IO.sfloat(s[0]),0.0,0.0,0.0,azm])                       
772                Inst1 = G2IO.makeInstDict(names,data,codes)
773                Inst2 = {}
774                if pfType < 0:
775                    Ipab = 'INS  1PAB'+str(-pfType)
776                    Npab = int(Iparm[Ipab+'  '].strip())
777                    Inst2['Pdabc'] = []
778                    for i in range(Npab):
779                        k = Ipab+str(i+1).rjust(2)
780                        s = Iparm[k].split()
781                        Inst2['Pdabc'].append([float(t) for t in s])
782                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
783                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
784                if 'INS  1I ITYP' in Iparm:
785                    s = Iparm['INS  1I ITYP'].split()
786                    Ityp = int(s[0])
787                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
788                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
789                    if Ityp in [1,2,4]:
790                        Inst2['Itype'] = Itypes[Ityp-1]
791                        Inst2['Tminmax'] = Tminmax
792                        Icoeff = []
793                        Iesd = []
794                        Icovar = []                   
795                        for i in range(3):
796                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
797                            Icoeff += [float(S) for S in s]
798                            s = Iparm['INS  1IECOF'+str(i+1)].split()
799                            Iesd += [float(S) for S in s]
800                        for i in range(8):
801                            s = Iparm['INS  1IECOR'+str(i+1)].split()
802                            Icovar += [float(S) for S in s]
803                        Inst2['Icoeff'] = Icoeff
804                        Inst2['Iesd'] = Iesd
805                        Inst2['Icovar'] = Icovar
806                return [Inst1,Inst2]
807
808        # stuff we might need from the reader
809        filename = rd.powderentry[0]
810        bank = rd.powderentry[2]
811        numbanks = rd.numbanks
812        # is there an instrument parameter file defined for the current data set?
813        if rd.instparm or (lastdatafile == filename and lastIparmfile):
814            if rd.instparm:
815                instfile = os.path.join(os.path.split(filename)[0],
816                                    rd.instparm)
817            else:
818                # for multiple reads of one data file, reuse the inst parm file
819                instfile = lastIparmfile
820            if os.path.exists(instfile):
821                #print 'debug: try read',instfile
822                instParmList = self.ReadPowderInstprm(instfile)
823                if instParmList is not None:
824                    rd.instfile = instfile
825                    rd.instmsg = 'GSAS-II file '+instfile
826                    return instParmList
827                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
828                if Iparm:
829                    #print 'debug: success'
830                    rd.instfile = instfile
831                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
832                    return SetPowderInstParms(Iparm,rd)
833            else:
834                self.ErrorDialog('Open Error','Error opening instrument parameter file '
835                    +str(instfile)+' requested by file '+ filename)
836        # is there an instrument parameter file matching the current file
837        # with extension .inst or .prm? If so read it
838        basename = os.path.splitext(filename)[0]
839        for ext in '.instprm','.prm','.inst','.ins':
840            instfile = basename + ext
841            instParmList = self.ReadPowderInstprm(instfile)
842            if instParmList is not None:
843                rd.instfile = instfile
844                rd.instmsg = 'GSAS-II file '+instfile
845                return instParmList
846            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
847            if Iparm:
848                #print 'debug: success'
849                rd.instfile = instfile
850                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
851                return SetPowderInstParms(Iparm,rd)
852            else:
853                #print 'debug: open/read failed',instfile
854                pass # fail silently
855
856        # did we read the data file from a zip? If so, look there for a
857        # instrument parameter file
858        if self.zipfile:
859            for ext in '.instprm','.prm','.inst','.ins':
860                instfile = G2IO.ExtractFileFromZip(
861                    self.zipfile,
862                    selection=os.path.split(basename + ext)[1],
863                    parent=self)
864                if instfile is not None and instfile != self.zipfile:
865                    print 'debug:',instfile,'created from ',self.zipfile
866                    instParmList = self.ReadPowderInstprm(instfile)
867                    if instParmList is not None:
868                        rd.instfile = instfile
869                        rd.instmsg = 'GSAS-II file '+instfile
870                        return instParmList
871                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
872                    if Iparm:
873                        rd.instfile = instfile
874                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
875                        return SetPowderInstParms(Iparm,rd)
876                    else:
877                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
878                        pass # fail silently
879
880        while True: # loop until we get a file that works or we get a cancel
881            instfile = ''
882            dlg = wx.FileDialog(
883                self,
884                'Choose inst. param file for "'
885                +rd.idstring
886                +'" (or Cancel for default)',
887                '.', '',
888                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
889                'GSAS-II iparm file (*.instprm)|*.instprm|'
890                'All files (*.*)|*.*', 
891                wx.OPEN|wx.CHANGE_DIR)
892            if os.path.exists(lastIparmfile):
893                dlg.SetFilename(lastIparmfile)
894            if dlg.ShowModal() == wx.ID_OK:
895                instfile = dlg.GetPath()
896            dlg.Destroy()
897            if not instfile: break
898            instParmList = self.ReadPowderInstprm(instfile)
899            if instParmList is not None:
900                rd.instfile = instfile
901                rd.instmsg = 'GSAS-II file '+instfile
902                return instParmList
903            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
904            if Iparm:
905                #print 'debug: success with',instfile
906                rd.instfile = instfile
907                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
908                return SetPowderInstParms(Iparm,rd)
909            else:
910                self.ErrorDialog('Read Error',
911                                 'Error opening/reading file '+str(instfile))
912       
913        # still no success: offer user choice of defaults
914        while True: # loop until we get a choice
915            choices = []
916            head = 'Select from default instrument parameters for '+rd.idstring
917
918            for l in rd.defaultIparm_lbl:
919                choices.append('Defaults for '+l)
920            res = rd.BlockSelector(
921                choices,
922                ParentFrame=self,
923                title=head,
924                header='Select default inst parms',
925                useCancel=False)
926            if res is None: continue
927            rd.instfile = ''
928            rd.instmsg = 'default: '+rd.defaultIparm_lbl[res]
929            return SetPowderInstParms(rd.defaultIparms[res],rd)
930
931    def OnImportPowder(self,event):
932        '''Called in response to an Import/Powder Data/... menu item
933        to read a powder diffraction data set.
934        dict self.ImportMenuId is used to look up the specific
935        reader item associated with the menu item, which will be
936        None for the last menu item, which is the "guess" option
937        where all appropriate formats will be tried.
938
939        Also reads an instrument parameter file for each dataset.
940        '''
941        reqrdr = self.ImportMenuId.get(event.GetId())  # look up which format was requested
942        rdlist = self.OnImportGeneric(
943            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
944        if len(rdlist) == 0: return
945        self.CheckNotebook()
946        Iparm = None
947        lastIparmfile = ''
948        lastdatafile = ''
949        for rd in rdlist:
950            # get instrument parameters for each dataset
951            Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
952            if rd.repeat_instparm: 
953                lastIparmfile = rd.instfile
954            lastdatafile = rd.powderentry[0]
955            print 'Read powder data '+str(rd.idstring)+ \
956                ' from file '+str(self.lastimport) + \
957                ' with parameters from '+str(rd.instmsg)
958            # data are read, now store them in the tree
959            Id = self.PatternTree.AppendItem(parent=self.root,
960                text='PWDR '+rd.idstring)
961            if 'T' in Iparm1['Type'][0]:
962                if not rd.clockWd and rd.GSAS:
963                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
964                cw = np.diff(rd.powderdata[0])
965                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
966                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
967                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
968                if 'Itype' in Iparm2:
969                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
970                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
971                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
972                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
973                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
974                    var = 1./rd.powderdata[2][Ibeg:Ifin]
975                    var += WYI*rd.powderdata[1]**2
976                    var /= YI**2
977                    rd.powderdata[2] = 1./var
978                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
979                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
980                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
981            Tmin = min(rd.powderdata[0])
982            Tmax = max(rd.powderdata[0])
983            self.PatternTree.SetItemPyData(Id,[{'wtFactor':1.0,'Dummy':False},rd.powderdata])
984            self.PatternTree.SetItemPyData(
985                self.PatternTree.AppendItem(Id,text='Comments'),
986                rd.comments)
987            self.PatternTree.SetItemPyData(
988                self.PatternTree.AppendItem(Id,text='Limits'),
989                [(Tmin,Tmax),[Tmin,Tmax]])
990            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
991            self.PatternTree.SetItemPyData(
992                self.PatternTree.AppendItem(Id,text='Background'),
993                [['chebyschev',True,3,1.0,0.0,0.0],
994                 {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
995            self.PatternTree.SetItemPyData(
996                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
997                [Iparm1,Iparm2])
998            self.PatternTree.SetItemPyData(
999                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1000                rd.Sample)
1001            self.PatternTree.SetItemPyData(
1002                self.PatternTree.AppendItem(Id,text='Peak List')
1003                ,[])
1004            self.PatternTree.SetItemPyData(
1005                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1006                [])
1007            self.PatternTree.SetItemPyData(
1008                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1009                [])
1010            self.PatternTree.SetItemPyData(
1011                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1012                {})
1013            self.PatternTree.Expand(Id)
1014        self.PatternTree.SelectItem(Id)
1015        return # success
1016
1017    def OnDummyPowder(self,event):
1018        '''Called in response to an Import/Powder Data/... menu item
1019        to read a powder diffraction data set.
1020        dict self.ImportMenuId is used to look up the specific
1021        reader item associated with the menu item, which will be
1022        None for the last menu item, which is the "guess" option
1023        where all appropriate formats will be tried.
1024
1025        Also reads an instrument parameter file for each dataset.
1026        '''
1027        print 'Start Dummy'
1028        rd = G2IO.ImportPowderData(
1029            extensionlist=tuple(),
1030            strictExtension=False,
1031            formatName = 'Simulate dataset',
1032            longFormatName = 'Compute a simulated pattern')
1033        # rd.powderdata = [
1034        #     np.array(x), # x-axis values
1035        #     np.array(y), # powder pattern intensities
1036        #     np.array(w), # 1/sig(intensity)^2 values (weights)
1037        #     np.zeros(N), # calc. intensities (zero)
1038        #     np.zeros(N), # calc. background (zero)
1039        #     np.zeros(N), # obs-calc profiles
1040        #     ]
1041        # rd.powderentry[0] = filename
1042        # #self.powderentry[1] = pos # bank offset (N/A here)
1043        # rd.powderentry[2] = 1 # xye file only has one bank
1044        # rd.idstring = ospath.basename(filename)
1045        return
1046        self.CheckNotebook()
1047        Iparm = None
1048        lastIparmfile = ''
1049        lastdatafile = ''
1050        # get instrument parameters for
1051        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1052
1053        for rd in rdlist:
1054            if rd.repeat_instparm: 
1055                lastIparmfile = rd.instfile
1056            lastdatafile = rd.powderentry[0]
1057            print 'Read powder data '+str(rd.idstring)+ \
1058                ' from file '+str(self.lastimport) + \
1059                ' with parameters from '+str(rd.instmsg)
1060            # data are read, now store them in the tree
1061            Id = self.PatternTree.AppendItem(parent=self.root,
1062                text='PWDR '+rd.idstring)
1063            if 'T' in Iparm1['Type'][0]:
1064                if not rd.clockWd and rd.GSAS:
1065                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1066                cw = np.diff(rd.powderdata[0])
1067                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1068                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1069                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1070                if 'Itype' in Iparm2:
1071                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1072                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1073                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1074                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1075                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1076                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1077                    var += WYI*rd.powderdata[1]**2
1078                    var /= YI**2
1079                    rd.powderdata[2] = 1./var
1080                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1081                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1082                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1083            Tmin = min(rd.powderdata[0])
1084            Tmax = max(rd.powderdata[0])
1085            self.PatternTree.SetItemPyData(Id,[{'wtFactor':1.0,'Dummy':True},rd.powderdata])
1086            self.PatternTree.SetItemPyData(
1087                self.PatternTree.AppendItem(Id,text='Comments'),
1088                rd.comments)
1089            self.PatternTree.SetItemPyData(
1090                self.PatternTree.AppendItem(Id,text='Limits'),
1091                [(Tmin,Tmax),[Tmin,Tmax]])
1092            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1093            self.PatternTree.SetItemPyData(
1094                self.PatternTree.AppendItem(Id,text='Background'),
1095                [['chebyschev',True,3,1.0,0.0,0.0],
1096                 {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1097            self.PatternTree.SetItemPyData(
1098                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1099                [Iparm1,Iparm2])
1100            self.PatternTree.SetItemPyData(
1101                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1102                rd.Sample)
1103            self.PatternTree.SetItemPyData(
1104                self.PatternTree.AppendItem(Id,text='Peak List')
1105                ,[])
1106            self.PatternTree.SetItemPyData(
1107                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1108                [])
1109            self.PatternTree.SetItemPyData(
1110                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1111                [])
1112            self.PatternTree.SetItemPyData(
1113                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1114                {})
1115            self.PatternTree.Expand(Id)
1116        self.PatternTree.SelectItem(Id)
1117        return # success
1118
1119    def OnDummyPowder(self,event):
1120        import testit
1121        reload(testit)
1122        testit.OnDummyPowder(self,event)
1123        return
1124
1125
1126    def _init_Exports(self,menu):
1127        '''Find exporter routines and add them into menus
1128        '''
1129        # set up the top-level menus
1130        projectmenu = wx.Menu()
1131        item = menu.AppendMenu(
1132            wx.ID_ANY, 'Entire project as',
1133            projectmenu, help='Export entire project')
1134
1135        phasemenu = wx.Menu()
1136        item = menu.AppendMenu(
1137            wx.ID_ANY, 'Phase as',
1138            phasemenu, help='Export phase or sometimes phases')
1139
1140        powdermenu = wx.Menu()
1141        item = menu.AppendMenu(
1142            wx.ID_ANY, 'Powder data as',
1143            powdermenu, help='Export powder diffraction histogram(s)')
1144
1145        singlemenu = wx.Menu()
1146        item = menu.AppendMenu(
1147            wx.ID_ANY, 'Single crystal data as',
1148            singlemenu, help='Export single crystal histogram(s)')
1149
1150        # find all the exporter files
1151        pathlist = sys.path
1152        filelist = []
1153        for path in pathlist:
1154            for filename in glob.iglob(os.path.join(
1155                path,
1156                "G2export*.py")):
1157                filelist.append(filename)   
1158        filelist = sorted(list(set(filelist))) # remove duplicates
1159        exporterlist = []
1160        # go through the routines and import them, saving objects that
1161        # have export routines (method Exporter)
1162        for filename in filelist:
1163            path,rootname = os.path.split(filename)
1164            pkg = os.path.splitext(rootname)[0]
1165            try:
1166                fp = None
1167                fp, fppath,desc = imp.find_module(pkg,[path,])
1168                pkg = imp.load_module(pkg,fp,fppath,desc)
1169                for clss in inspect.getmembers(pkg): # find classes defined in package
1170                    if clss[0].startswith('_'): continue
1171                    if inspect.isclass(clss[1]):
1172                        # check if we have the required methods
1173                        for m in 'Exporter','loadParmDict':
1174                            if not hasattr(clss[1],m): break
1175                            if not callable(getattr(clss[1],m)): break
1176                        else:
1177                            exporter = clss[1](self) # create an export instance
1178                            exporterlist.append(exporter)
1179            except AttributeError:
1180                print 'Import_'+errprefix+': Attribute Error'+str(filename)
1181                pass
1182            except ImportError:
1183                print 'Import_'+errprefix+': Error importing file'+str(filename)
1184                pass
1185            if fp: fp.close()
1186        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
1187        for obj in exporterlist:
1188            #print 'exporter',obj
1189            for typ in obj.exporttype:
1190                if typ == "project":
1191                    submenu = projectmenu
1192                elif typ == "phase":
1193                    submenu = phasemenu
1194                elif typ == "powder":
1195                    submenu = powdermenu
1196                elif typ == "single":
1197                    submenu = singlemenu
1198                else:
1199                    print("Error, unknown type in "+str(obj))
1200                    break
1201                item = submenu.Append(
1202                    wx.ID_ANY,
1203                    help=obj.longFormatName,
1204                    kind=wx.ITEM_NORMAL,
1205                    text=obj.formatName)
1206                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
1207                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
1208        #code to debug an Exporter. much is hard-coded below, but code is reloaded before
1209        # each use allowing faster development
1210        def DebugExporter(event):
1211            reload(G2IO)
1212            import G2export_example
1213            reload(G2export_example)
1214            G2export_example.ExportPhaseShelx(self).Exporter(event)           
1215        item = menu.Append(
1216            wx.ID_ANY,kind=wx.ITEM_NORMAL,
1217            help="reload and test ExportPhaseShelx",text="test ExportPhaseShelx")
1218        self.Bind(wx.EVT_MENU, DebugExporter, id=item.GetId())
1219        self.ExportLookup[item.GetId()] = 'phase'
1220           
1221    def _Add_ExportMenuItems(self,parent):
1222        item = parent.Append(
1223            help='Select PWDR item to enable',id=wx.ID_ANY,
1224            kind=wx.ITEM_NORMAL,
1225            text='Export Powder Patterns...')
1226        self.ExportPattern.append(item)
1227        item.Enable(False)
1228        self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1229
1230        item = parent.Append(
1231            help='',id=wx.ID_ANY,
1232            kind=wx.ITEM_NORMAL,
1233            text='Export All Peak Lists...')
1234        self.ExportPeakList.append(item)
1235        item.Enable(True)
1236        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1237
1238        item = parent.Append(
1239            help='',id=wx.ID_ANY,
1240            kind=wx.ITEM_NORMAL,
1241            text='Export HKLs...')
1242        self.ExportHKL.append(item)
1243        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1244
1245        item = parent.Append(
1246            help='Select PDF item to enable',
1247            id=wx.ID_ANY,
1248            kind=wx.ITEM_NORMAL,
1249            text='Export PDF...')
1250        self.ExportPDF.append(item)
1251        item.Enable(False)
1252        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1253
1254    def FillMainMenu(self,menubar):
1255        '''Define contents of the main GSAS-II menu for the (main) data tree window
1256        in the mac, used also for the data item windows as well.
1257        '''
1258        File = wx.Menu(title='')
1259        menubar.Append(menu=File, title='&File')
1260        self._Add_FileMenuItems(File)
1261        Data = wx.Menu(title='')
1262        menubar.Append(menu=Data, title='Data')
1263        self._Add_DataMenuItems(Data)
1264        Calculate = wx.Menu(title='')       
1265        menubar.Append(menu=Calculate, title='&Calculate')
1266        self._Add_CalculateMenuItems(Calculate)
1267        Import = wx.Menu(title='')       
1268        menubar.Append(menu=Import, title='Import')
1269        self._Add_ImportMenu_Phase(Import)
1270        self._Add_ImportMenu_powder(Import)
1271        self._Add_ImportMenu_Sfact(Import)
1272        self.ExportMenu = wx.Menu(title='')
1273        menubar.Append(menu=self.ExportMenu, title='Export')
1274        self._init_Exports(self.ExportMenu)
1275        self._Add_ExportMenuItems(self.ExportMenu)
1276        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1277            morehelpitems=[('&Tutorials','Tutorials')])
1278        menubar.Append(menu=HelpMenu,title='&Help')
1279
1280    def _init_ctrls(self, parent):
1281        wx.Frame.__init__(self, name='GSASII', parent=parent,
1282            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1283        clientSize = wx.ClientDisplayRect()
1284        Size = self.GetSize()
1285        xPos = clientSize[2]-Size[0]
1286        self.SetPosition(wx.Point(xPos,clientSize[1]))
1287        self._init_Imports()
1288        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1289        self.MakePDF = []
1290        self.Refine = []
1291        self.SeqRefine = []
1292        self.ExportPattern = []
1293        self.ExportPeakList = []
1294        self.ExportHKL = []
1295        self.ExportPDF = []
1296        self.ExportPhase = []
1297        self.ExportCIF = []
1298        #
1299        self.GSASIIMenu = wx.MenuBar()
1300        self.FillMainMenu(self.GSASIIMenu)
1301        self.SetMenuBar(self.GSASIIMenu)
1302        self.Bind(wx.EVT_SIZE, self.OnSize)
1303        self.CreateStatusBar()
1304        self.mainPanel = wx.Panel(self,-1)
1305       
1306        wxID_PATTERNTREE = wx.NewId()
1307        self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE,
1308            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1309        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,
1310            self.OnPatternTreeSelChanged, id=wxID_PATTERNTREE)
1311        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1312            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1313        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1314            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1315        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1316            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1317        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1318            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1319        self.root = self.PatternTree.AddRoot('Loaded Data: ')
1320       
1321        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1322            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1323        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1324        plotFrame.Show()
1325       
1326        self.dataDisplay = None
1327       
1328    def __init__(self, parent):
1329        self.ExportLookup = {}
1330        self._init_ctrls(parent)
1331        self.Image = wx.Image(
1332            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
1333            wx.BITMAP_TYPE_ICO)
1334        if "wxMSW" in wx.PlatformInfo:
1335            img = self.Image.Scale(16, 16).ConvertToBitmap()
1336        elif "wxGTK" in wx.PlatformInfo:
1337            img = self.Image.Scale(22, 22).ConvertToBitmap()
1338        else:
1339            img = self.Image.ConvertToBitmap()
1340        self.SetIcon(wx.IconFromBitmap(img))
1341        self.Bind(wx.EVT_CLOSE, self.ExitMain)
1342        # various defaults
1343        self.oldFocus = None
1344        self.GSASprojectfile = ''
1345        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
1346        self.undofile = ''
1347        self.TreeItemDelete = False
1348        self.Offset = [0.0,0.0]
1349        self.delOffset = .02
1350        self.refOffset = -100.0
1351        self.refDelt = .01
1352        self.Weight = False
1353        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
1354        self.IfPlot = False
1355        self.PatternId = 0
1356        self.PickId = 0
1357        self.PeakTable = []
1358        self.LimitsTable = []
1359        self.HKL = []
1360        self.Lines = []
1361        self.itemPicked = None
1362        self.dataFrame = None
1363        self.Interpolate = 'nearest'
1364        self.ContourColor = 'Paired'
1365        self.VcovColor = 'RdYlGn'
1366        self.RamaColor = 'Blues'
1367        self.Projection = 'equal area'
1368        self.logPlot = False
1369        self.qPlot = False
1370        self.Contour = False
1371        self.Legend = False
1372        self.SinglePlot = False
1373        self.SubBack = False
1374        self.plotView = 0
1375        self.Image = 0
1376        self.oldImagefile = ''
1377        self.ImageZ = []
1378        self.Integrate = 0
1379        self.imageDefault = {}
1380        self.Sngl = 0
1381        self.ifGetRing = False
1382        self.setPoly = False
1383        arg = sys.argv
1384        if len(arg) > 1:
1385            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1386            self.dirname = os.path.dirname(arg[1])
1387            if self.dirname: os.chdir(self.dirname)
1388            try:
1389                G2IO.ProjFileOpen(self)
1390                self.PatternTree.Expand(self.root)
1391                for item in self.Refine: item.Enable(True)
1392                for item in self.SeqRefine: item.Enable(True)
1393            except:
1394                print 'Error opening file',arg[1]
1395
1396    def OnSize(self,event):
1397        'Called when the main window is resized. Not sure why'
1398        w,h = self.GetClientSizeTuple()
1399        self.mainPanel.SetSize(wx.Size(w,h))
1400        self.PatternTree.SetSize(wx.Size(w,h))
1401                       
1402    def OnPatternTreeSelChanged(self, event):
1403        '''Called when a data tree item is selected'''
1404        if self.TreeItemDelete:
1405            self.TreeItemDelete = False
1406        else:
1407            pltNum = self.G2plotNB.nb.GetSelection()
1408            if pltNum >= 0:                         #to avoid the startup with no plot!
1409                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1410                pltPlot = pltPage.figure
1411            item = event.GetItem()
1412            G2gd.MovePatternTreeToGrid(self,item)
1413            if self.oldFocus:
1414                self.oldFocus.SetFocus()
1415       
1416    def OnPatternTreeItemCollapsed(self, event):
1417        'Called when a tree item is collapsed'
1418        event.Skip()
1419
1420    def OnPatternTreeItemExpanded(self, event):
1421        'Called when a tree item is expanded'
1422        event.Skip()
1423       
1424    def OnPatternTreeItemDelete(self, event):
1425        'Called when a tree item is deleted -- not sure what this does'
1426        self.TreeItemDelete = True
1427
1428    def OnPatternTreeItemActivated(self, event):
1429        'Called when a tree item is activated'
1430        event.Skip()
1431       
1432    def OnPatternTreeKeyDown(self,event):
1433        'Not sure what this does'
1434        key = event.GetKeyCode()
1435        item = self.PickId
1436        if type(item) is int: return # is this the toplevel in tree?
1437        if key == wx.WXK_UP:
1438            self.oldFocus = wx.Window.FindFocus()
1439            self.PatternTree.GetPrevSibling(item)
1440        elif key == wx.WXK_DOWN:
1441            self.oldFocus = wx.Window.FindFocus()
1442            self.PatternTree.GetNextSibling(item)
1443               
1444    def OnReadPowderPeaks(self,event):
1445        'Bound to menu Data/Read Powder Peaks -- still needed?'
1446        Cuka = 1.54052
1447        self.CheckNotebook()
1448        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
1449            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
1450        try:
1451            if dlg.ShowModal() == wx.ID_OK:
1452                self.HKL = []
1453                self.powderfile = dlg.GetPath()
1454                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
1455                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
1456                data = ['PKS',Cuka,0.0]
1457                names = ['Type','Lam','Zero'] 
1458                codes = [0,0,0]
1459                inst = [G2IO.makeInstDict(names,data,codes),{}]
1460                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1461                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
1462                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),peaks)
1463                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1464                self.PatternTree.Expand(Id)
1465                self.PatternTree.SelectItem(Id)
1466                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1467        finally:
1468            dlg.Destroy()
1469           
1470    def OnImageRead(self,event):
1471        'Called to read in an image in any known format'
1472        self.CheckNotebook()
1473        dlg = wx.FileDialog(
1474            self, 'Choose image files', '.', '',
1475            'Any image file (*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img)|'
1476            '*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.zip|'
1477            'European detector file (*.edf)|*.edf|'
1478            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
1479            'MAR file (*.mar*)|*.mar*|'
1480            'GE Image (*.avg;*.sum)|*.avg;*.sum|'
1481            'ADSC Image (*.img)|*.img|'
1482            'GSAS-II Image (*.G2img)|*.G2img|'
1483            'Zip archive (*.zip)|*.zip|'
1484            'All files (*.*)|*.*',
1485            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
1486        try:
1487            if dlg.ShowModal() == wx.ID_OK:
1488                imagefiles = dlg.GetPaths()
1489                imagefiles.sort()
1490                for imagefile in imagefiles:
1491                    # if a zip file, open and extract
1492                    if os.path.splitext(imagefile)[1].lower() == '.zip':
1493                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
1494                        if extractedfile is not None and extractedfile != imagefile:
1495                            imagefile = extractedfile
1496                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
1497                    if Comments:
1498                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
1499                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1500                        Imax = np.amax(Image)
1501                        Imin = max(0.0,np.amin(Image))          #force positive
1502                        if self.imageDefault:
1503                            Data = copy.copy(self.imageDefault)
1504                            Data['showLines'] = True
1505                            Data['ring'] = []
1506                            Data['rings'] = []
1507                            Data['cutoff'] = 10
1508                            Data['pixLimit'] = 20
1509                            Data['edgemin'] = 100000000
1510                            Data['calibdmin'] = 0.5
1511                            Data['calibskip'] = 0
1512                            Data['ellipses'] = []
1513                            Data['calibrant'] = ''
1514                            Data['GonioAngles'] = [0.,0.,0.]
1515                            Data['DetDepth'] = 0.
1516                            Data['DetDepthRef'] = False
1517                        else:
1518                            Data['type'] = 'PWDR'
1519                            Data['color'] = 'Paired'
1520                            Data['tilt'] = 0.0
1521                            Data['rotation'] = 0.0
1522                            Data['showLines'] = False
1523                            Data['ring'] = []
1524                            Data['rings'] = []
1525                            Data['cutoff'] = 10
1526                            Data['pixLimit'] = 20
1527                            Data['calibdmin'] = 0.5
1528                            Data['calibskip'] = 0
1529                            Data['edgemin'] = 100000000
1530                            Data['ellipses'] = []
1531                            Data['GonioAngles'] = [0.,0.,0.]
1532                            Data['DetDepth'] = 0.
1533                            Data['DetDepthRef'] = False
1534                            Data['calibrant'] = ''
1535                            Data['IOtth'] = [2.0,5.0]
1536                            Data['LRazimuth'] = [135,225]
1537                            Data['azmthOff'] = 0.0
1538                            Data['outChannels'] = 2500
1539                            Data['outAzimuths'] = 1
1540                            Data['centerAzm'] = False
1541                            Data['fullIntegrate'] = False
1542                            Data['setRings'] = False
1543                            Data['background image'] = ['',1.0]                           
1544                        Data['setDefault'] = False
1545                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1546                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
1547                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1548                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1549                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
1550                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'strain':np.zeros((3,3))})
1551                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
1552                        self.PickId = Id
1553                        self.Image = Id
1554                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1555                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
1556        finally:
1557            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
1558            os.chdir(path)
1559            dlg.Destroy()
1560
1561    def CheckNotebook(self):
1562        '''Make sure the data tree has the minimally expected controls.
1563        (BHT) correct?
1564        '''
1565        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
1566            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
1567            self.PatternTree.SetItemPyData(sub,[''])
1568        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
1569            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
1570            self.PatternTree.SetItemPyData(sub,{'deriv type':'analytic Hessian',    #default controls
1571                'min dM/M':0.0001,'shift factor':1.,'max cyc':3,'F**2':True,
1572                'minF/sig':0,})
1573        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
1574            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
1575            self.PatternTree.SetItemPyData(sub,{})
1576        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
1577            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
1578            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
1579        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
1580            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
1581            self.PatternTree.SetItemPyData(sub,{})
1582        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
1583            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
1584            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
1585                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
1586               
1587    class CopyDialog(wx.Dialog):
1588        '''Creates a dialog for copying control settings between
1589        data tree items'''
1590        def __init__(self,parent,title,text,data):
1591            wx.Dialog.__init__(self,parent,-1,title, 
1592                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1593            self.data = data
1594            panel = wx.Panel(self)
1595            mainSizer = wx.BoxSizer(wx.VERTICAL)
1596            topLabl = wx.StaticText(panel,-1,text)
1597            mainSizer.Add((10,10),1)
1598            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1599            mainSizer.Add((10,10),1)
1600            ncols = len(data)/40+1
1601            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=ncols,hgap=2,vgap=2)
1602            for id,item in enumerate(self.data):
1603                ckbox = wx.CheckBox(panel,id,item[1])
1604                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
1605                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
1606            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1607            OkBtn = wx.Button(panel,-1,"Ok")
1608            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1609            cancelBtn = wx.Button(panel,-1,"Cancel")
1610            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1611            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1612            btnSizer.Add((20,20),1)
1613            btnSizer.Add(OkBtn)
1614            btnSizer.Add((20,20),1)
1615            btnSizer.Add(cancelBtn)
1616            btnSizer.Add((20,20),1)
1617           
1618            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1619            panel.SetSizer(mainSizer)
1620            panel.Fit()
1621            self.Fit()
1622       
1623        def OnCopyChange(self,event):
1624            id = event.GetId()
1625            self.data[id][0] = self.FindWindowById(id).GetValue()       
1626           
1627        def OnOk(self,event):
1628            parent = self.GetParent()
1629            parent.Raise()
1630            self.EndModal(wx.ID_OK)             
1631           
1632        def OnCancel(self,event):
1633            parent = self.GetParent()
1634            parent.Raise()
1635            self.EndModal(wx.ID_CANCEL)             
1636           
1637        def GetData(self):
1638            return self.data
1639       
1640    class SumDialog(wx.Dialog):
1641        'Allows user to supply scale factor(s) when summing data'
1642        def __init__(self,parent,title,text,dataType,data):
1643            wx.Dialog.__init__(self,parent,-1,title, 
1644                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1645            self.data = data
1646            panel = wx.Panel(self)
1647            mainSizer = wx.BoxSizer(wx.VERTICAL)
1648            topLabl = wx.StaticText(panel,-1,text)
1649            mainSizer.Add((10,10),1)
1650            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1651            mainSizer.Add((10,10),1)
1652            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1653            for id,item in enumerate(self.data[:-1]):
1654                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
1655                name.SetEditable(False)
1656                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1657                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1658                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1659                dataGridSizer.Add(scale,0,wx.LEFT,10)
1660                dataGridSizer.Add(name,0,wx.RIGHT,10)
1661            if dataType:
1662                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
1663                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
1664                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
1665                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
1666                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
1667                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
1668            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1669            OkBtn = wx.Button(panel,-1,"Ok")
1670            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1671            cancelBtn = wx.Button(panel,-1,"Cancel")
1672            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1673            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1674            btnSizer.Add((20,20),1)
1675            btnSizer.Add(OkBtn)
1676            btnSizer.Add((20,20),1)
1677            btnSizer.Add(cancelBtn)
1678            btnSizer.Add((20,20),1)
1679           
1680            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1681            panel.SetSizer(mainSizer)
1682            panel.Fit()
1683            self.Fit()
1684
1685        def OnScaleChange(self,event):
1686            id = event.GetId()
1687            value = self.FindWindowById(id).GetValue()
1688            try:
1689                self.data[id][0] = float(value)
1690                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1691            except ValueError:
1692                if value and '-' not in value[0]:
1693                    print 'bad input - numbers only'
1694                    self.FindWindowById(id).SetValue('0.000')
1695           
1696        def OnNameChange(self,event):
1697            self.data[-1] = self.name.GetValue() 
1698           
1699        def OnOk(self,event):
1700            parent = self.GetParent()
1701            parent.Raise()
1702            self.EndModal(wx.ID_OK)             
1703           
1704        def OnCancel(self,event):
1705            parent = self.GetParent()
1706            parent.Raise()
1707            self.EndModal(wx.ID_CANCEL)             
1708           
1709        def GetData(self):
1710            return self.data
1711           
1712    class ConstraintDialog(wx.Dialog):
1713        '''Window to edit Constraint values
1714        '''
1715        def __init__(self,parent,title,text,data,separator='*'):
1716            wx.Dialog.__init__(self,parent,-1,title, 
1717                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1718            self.data = data
1719            panel = wx.Panel(self)
1720            mainSizer = wx.BoxSizer(wx.VERTICAL)
1721            topLabl = wx.StaticText(panel,-1,text)
1722            mainSizer.Add((10,10),1)
1723            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1724            mainSizer.Add((10,10),1)
1725            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1726            for id,item in enumerate(self.data[:-1]):
1727                lbl = item[1]
1728                if lbl[-1] != '=': lbl += ' ' + separator + ' '
1729                name = wx.StaticText(panel,-1,lbl,size=wx.Size(200,20),
1730                                     style=wx.ALIGN_RIGHT)
1731                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1732                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1733                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1734                dataGridSizer.Add(name,0,wx.LEFT,10)
1735                dataGridSizer.Add(scale,0,wx.RIGHT,10)
1736            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1737            OkBtn = wx.Button(panel,-1,"Ok")
1738            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1739            cancelBtn = wx.Button(panel,-1,"Cancel")
1740            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1741            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1742            btnSizer.Add((20,20),1)
1743            btnSizer.Add(OkBtn)
1744            btnSizer.Add((20,20),1)
1745            btnSizer.Add(cancelBtn)
1746            btnSizer.Add((20,20),1)
1747           
1748            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1749            panel.SetSizer(mainSizer)
1750            panel.Fit()
1751            self.Fit()
1752            self.CenterOnParent()
1753           
1754        def OnNameChange(self,event):
1755            self.data[-1] = self.name.GetValue() 
1756           
1757        def OnScaleChange(self,event):
1758            id = event.GetId()
1759            value = self.FindWindowById(id).GetValue()
1760            try:
1761                self.data[id][0] = float(value)
1762                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1763            except ValueError:
1764                if value and '-' not in value[0]:
1765                    print 'bad input - numbers only'
1766                    self.FindWindowById(id).SetValue('0.000')
1767           
1768        def OnOk(self,event):
1769            parent = self.GetParent()
1770            parent.Raise()
1771            self.EndModal(wx.ID_OK)             
1772           
1773        def OnCancel(self,event):
1774            parent = self.GetParent()
1775            parent.Raise()
1776            self.EndModal(wx.ID_CANCEL)             
1777           
1778        def GetData(self):
1779            return self.data
1780           
1781    def OnPwdrSum(self,event):
1782        'Sum together powder data(?)'
1783        TextList = []
1784        DataList = []
1785        SumList = []
1786        Names = []
1787        Inst = None
1788        SumItemList = []
1789        Comments = ['Sum equals: \n']
1790        if self.PatternTree.GetCount():
1791            item, cookie = self.PatternTree.GetFirstChild(self.root)
1792            while item:
1793                name = self.PatternTree.GetItemText(item)
1794                Names.append(name)
1795                if 'PWDR' in name:
1796                    TextList.append([0.0,name])
1797                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
1798                    if not Inst:
1799                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
1800                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1801            if len(TextList) < 2:
1802                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
1803                return
1804            TextList.append('default_sum_name')               
1805            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
1806            try:
1807                if dlg.ShowModal() == wx.ID_OK:
1808                    lenX = 0
1809                    Xminmax = [0,0]
1810                    Xsum = []
1811                    Ysum = []
1812                    Vsum = []
1813                    result = dlg.GetData()
1814                    for i,item in enumerate(result[:-1]):
1815                        scale,name = item
1816                        data = DataList[i]
1817                        if scale:
1818                            Comments.append("%10.3f %s" % (scale,' * '+name))
1819                            x,y,w,yc,yb,yd = data   #numpy arrays!
1820                            v = 1./w
1821                            if lenX:
1822                                if lenX != len(x):
1823                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
1824                                        '\nExpected:'+str(lenX)+ \
1825                                        '\nFound:   '+str(len(x))+'\nfor '+name)
1826                                    return
1827                            else:
1828                                lenX = len(x)
1829                            if Xminmax[1]:
1830                                if Xminmax != [x[0],x[-1]]:
1831                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
1832                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
1833                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
1834                                    return
1835                                else:
1836                                    for j,yi in enumerate(y):
1837                                         Ysum[j] += scale*yi
1838                                         Vsum[j] += abs(scale)*v[j]
1839                            else:
1840                                Xminmax = [x[0],x[-1]]
1841                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
1842                                for j,yi in enumerate(y):
1843                                    Xsum.append(x[j])
1844                                    Ysum.append(scale*yi)
1845                                    Vsum.append(abs(scale*v[j]))
1846                    Wsum = 1./np.array(Vsum)
1847                    outname = 'PWDR '+result[-1]
1848                    Id = 0
1849                    if outname in Names:
1850                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1851                        try:
1852                            if dlg2.ShowModal() == wx.ID_OK:
1853                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1854                                self.PatternTree.Delete(Id)
1855                        finally:
1856                            dlg2.Destroy()
1857                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1858                    if Id:
1859                        Sample = G2pdG.SetDefaultSample()
1860                        self.PatternTree.SetItemPyData(Id,[{'wtFactor':1.0,'Dummy':False},[np.array(Xsum),np.array(Ysum),np.array(Wsum),
1861                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
1862                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
1863                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
1864                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
1865                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1866                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
1867                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1868                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
1869                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
1870                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1871                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
1872                        self.PatternTree.SelectItem(Id)
1873                        self.PatternTree.Expand(Id)
1874            finally:
1875                dlg.Destroy()
1876
1877    def OnImageSum(self,event):
1878        'Sum together image data(?)'
1879        TextList = []
1880        DataList = []
1881        SumList = []
1882        Names = []
1883        Inst = []
1884        SumItemList = []
1885        Comments = ['Sum equals: \n']
1886        if self.PatternTree.GetCount():
1887            item, cookie = self.PatternTree.GetFirstChild(self.root)
1888            while item:
1889                name = self.PatternTree.GetItemText(item)
1890                Names.append(name)
1891                if 'IMG' in name:
1892                    TextList.append([0.0,name])
1893                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
1894                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
1895                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1896            if len(TextList) < 2:
1897                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
1898                return
1899            TextList.append('default_sum_name')               
1900            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
1901            try:
1902                if dlg.ShowModal() == wx.ID_OK:
1903                    imSize = 0
1904                    result = dlg.GetData()
1905                    First = True
1906                    Found = False
1907                    for i,item in enumerate(result[:-1]):
1908                        scale,name = item
1909                        data = DataList[i]
1910                        if scale:
1911                            Found = True                               
1912                            Comments.append("%10.3f %s" % (scale,' * '+name))
1913                            Npix,imagefile = data
1914                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
1915                            if First:
1916                                newImage = np.zeros_like(image)
1917                                First = False
1918                            if imSize:
1919                                if imSize != Npix:
1920                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
1921                                        '\nExpected:'+str(imSize)+ \
1922                                        '\nFound:   '+str(Npix)+'\nfor '+name)
1923                                    return
1924                                newImage = newImage+scale*image
1925                            else:
1926                                imSize = Npix
1927                                newImage = newImage+scale*image
1928                            del(image)
1929                    if not Found:
1930                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
1931                        return
1932                       
1933                    newImage = np.asfarray(newImage,dtype=np.float32)                       
1934                    outname = 'IMG '+result[-1]
1935                    Id = 0
1936                    if outname in Names:
1937                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1938                        try:
1939                            if dlg2.ShowModal() == wx.ID_OK:
1940                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1941                        finally:
1942                            dlg2.Destroy()
1943                    else:
1944                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1945                    if Id:
1946                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
1947                            'G2img files (*.G2img)|*.G2img', 
1948                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1949                        if dlg.ShowModal() == wx.ID_OK:
1950                            newimagefile = dlg.GetPath()
1951                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
1952                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
1953                            Imax = np.amax(newImage)
1954                            Imin = np.amin(newImage)
1955                            newImage = []
1956                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
1957                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1958                        del(newImage)
1959                        if self.imageDefault:
1960                            Data = copy.copy(self.imageDefault)
1961                        Data['showLines'] = True
1962                        Data['ring'] = []
1963                        Data['rings'] = []
1964                        Data['cutoff'] = 10
1965                        Data['pixLimit'] = 20
1966                        Data['ellipses'] = []
1967                        Data['calibrant'] = ''
1968                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1969                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
1970                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1971                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1972                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),{})
1973                        self.PatternTree.SelectItem(Id)
1974                        self.PatternTree.Expand(Id)
1975                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
1976                        self.Image = self.PickId
1977            finally:
1978                dlg.Destroy()
1979                     
1980    def OnAddPhase(self,event):
1981        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
1982        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
1983            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
1984        else:
1985            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1986        PhaseName = ''
1987        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
1988            style=wx.OK)
1989        if dlg.ShowModal() == wx.ID_OK:
1990            PhaseName = dlg.GetValue()
1991        dlg.Destroy()
1992        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
1993        E,SGData = G2spc.SpcGroup('P 1')
1994        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
1995       
1996    def OnDeletePhase(self,event):
1997        'Delete a phase from the tree. Called by Data/Delete Phase menu'
1998        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
1999        if self.dataFrame:
2000            self.dataFrame.Clear() 
2001        TextList = []
2002        DelList = []
2003        DelItemList = []
2004        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2005            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2006        else:
2007            return
2008        if sub:
2009            item, cookie = self.PatternTree.GetFirstChild(sub)
2010            while item:
2011                TextList.append(self.PatternTree.GetItemText(item))
2012                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2013            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2014            try:
2015                if dlg.ShowModal() == wx.ID_OK:
2016                    result = dlg.GetSelections()
2017                    for i in result: DelList.append([i,TextList[i]])
2018                    item, cookie = self.PatternTree.GetFirstChild(sub)
2019                    i = 0
2020                    while item:
2021                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2022                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2023                        i += 1
2024                    for item in DelItemList:
2025                        name = self.PatternTree.GetItemText(item)
2026                        self.PatternTree.Delete(item)
2027                        self.G2plotNB.Delete(name)
2028                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2029                    while item:
2030                        name = self.PatternTree.GetItemText(item)
2031                        if 'PWDR' in name:
2032                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2033                            refList = self.PatternTree.GetItemPyData(Id)
2034                            for i,item in DelList:
2035                                del(refList[item])
2036                            self.PatternTree.SetItemPyData(Id,refList)
2037                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2038            finally:
2039                dlg.Destroy()
2040               
2041    def OnRenameData(self,event):
2042        'Renames an existing phase. Called by Data/Rename Phase menu'
2043        name = self.PatternTree.GetItemText(self.PickId)     
2044        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2045            dataType = name[:name.index(' ')+1]                 #includes the ' '
2046            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2047                defaultValue=name[name.index(' ')+1:])
2048            try:
2049                if dlg.ShowModal() == wx.ID_OK:
2050                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2051            finally:
2052                dlg.Destroy()
2053       
2054    def GetFileList(self,fileType,skip=None):        #potentially useful?
2055        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2056        fileList = []
2057        Source = ''
2058        id, cookie = self.PatternTree.GetFirstChild(self.root)
2059        while id:
2060            name = self.PatternTree.GetItemText(id)
2061            if fileType in name:
2062                if id == skip:
2063                    Source = name
2064                else:
2065                    fileList.append([False,name,id])
2066            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2067        if skip:
2068            return fileList,Source
2069        else:
2070            return fileList
2071           
2072    def OnDataDelete(self, event):
2073        '''Delete one or more histograms from data tree. Called by the
2074        Data/DeleteData menu
2075        '''
2076        TextList = ['All Data']
2077        DelList = []
2078        DelItemList = []
2079        ifPWDR = False
2080        ifIMG = False
2081        ifHKLF = False
2082        ifPDF = False
2083        if self.PatternTree.GetCount():
2084            item, cookie = self.PatternTree.GetFirstChild(self.root)
2085            while item:
2086                name = self.PatternTree.GetItemText(item)
2087                if name not in ['Notebook','Controls','Covariance','Constraints','Restraints','Phases']:
2088                    if 'PWDR' in name: ifPWDR = True
2089                    if 'IMG' in name: ifIMG = True
2090                    if 'HKLF' in name: ifHKLF = True
2091                    if 'PDF' in name: ifPDF = True
2092                    TextList.append(name)
2093                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2094            if ifPWDR: TextList.insert(1,'All PWDR')
2095            if ifIMG: TextList.insert(1,'All IMG')
2096            if ifHKLF: TextList.insert(1,'All HKLF')
2097            if ifPDF: TextList.insert(1,'All PDF')               
2098            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2099            try:
2100                if dlg.ShowModal() == wx.ID_OK:
2101                    result = dlg.GetSelections()
2102                    for i in result: DelList.append(TextList[i])
2103                    if 'All Data' in DelList:
2104                        DelList = [item for item in TextList if item[:3] != 'All']
2105                    elif 'All PWDR' in DelList:
2106                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2107                    elif 'All IMG' in DelList:
2108                        DelList = [item for item in TextList if item[:3] == 'IMG']
2109                    elif 'All HKLF' in DelList:
2110                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2111                    elif 'All PDF' in DelList:
2112                        DelList = [item for item in TextList if item[:3] == 'PDF']
2113                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2114                    while item:
2115                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2116                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2117                    for item in DelItemList:
2118                        self.PatternTree.Delete(item)
2119                    self.PickId = 0
2120                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2121            finally:
2122                dlg.Destroy()
2123
2124    def OnFileOpen(self, event, filename=None):
2125        '''Reads in a GSAS-II .gpx project file in response to the
2126        File/Open Project menu button
2127        '''
2128        result = wx.ID_OK
2129        Id = 0
2130        if self.PatternTree.GetChildrenCount(self.root,False):
2131            if self.dataFrame:
2132                self.dataFrame.Clear() 
2133            dlg = wx.MessageDialog(
2134                self,
2135                'Do you want to overwrite the current project? '
2136                'Any unsaved changes will be lost. Press OK to continue.',
2137                'Overwrite?',  wx.OK | wx.CANCEL)
2138            try:
2139                result = dlg.ShowModal()
2140                if result == wx.ID_OK:
2141                    self.PatternTree.DeleteChildren(self.root)
2142                    self.GSASprojectfile = ''
2143                    if self.HKL: self.HKL = []
2144                    if self.G2plotNB.plotList:
2145                        self.G2plotNB.clear()
2146            finally:
2147                dlg.Destroy()
2148        if result != wx.ID_OK: return
2149
2150        if not filename:
2151            if self.dataDisplay: self.dataDisplay.Destroy()
2152            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2153                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2154            try:
2155                if dlg.ShowModal() != wx.ID_OK: return
2156                self.GSASprojectfile = dlg.GetPath()
2157                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2158                self.dirname = dlg.GetDirectory()
2159            finally:
2160                dlg.Destroy()
2161        else:
2162            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2163            self.dirname = os.path.split(filename)[0]
2164
2165        G2IO.ProjFileOpen(self)
2166        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2167        self.PatternTree.Expand(self.root)
2168        self.HKL = []
2169        item, cookie = self.PatternTree.GetFirstChild(self.root)
2170        while item and not Id:
2171            name = self.PatternTree.GetItemText(item)
2172            if name[:4] in ['PWDR','HKLF','IMG ','PDF ']:
2173                Id = item
2174            elif name == 'Controls':
2175                data = self.PatternTree.GetItemPyData(item)
2176                if data:
2177                    for item in self.Refine: item.Enable(True)
2178                    for item in self.SeqRefine: item.Enable(True)
2179            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2180        if Id:
2181            self.PatternTree.SelectItem(Id)
2182        self.CheckNotebook()
2183        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2184
2185    def OnFileClose(self, event):
2186        '''Clears the data tree in response to the
2187        File/Close Project menu button. User is given option to save
2188        the project.
2189        '''
2190        if self.dataFrame:
2191            self.dataFrame.Clear()
2192            self.dataFrame.SetLabel('GSAS-II data display') 
2193        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2194        try:
2195            result = dlg.ShowModal()
2196            if result == wx.ID_OK:
2197                self.OnFileSaveMenu(event)
2198            if result != wx.ID_CANCEL:
2199                self.GSASprojectfile = ''
2200                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2201                self.PatternTree.DeleteChildren(self.root)
2202                if self.HKL: self.HKL = []
2203                if self.G2plotNB.plotList:
2204                    self.G2plotNB.clear()
2205        finally:
2206            dlg.Destroy()
2207
2208    def OnFileSave(self, event):
2209        '''Save the current project in response to the
2210        File/Save Project menu button
2211        '''
2212       
2213        if self.GSASprojectfile: 
2214            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2215            G2IO.ProjFileSave(self)
2216        else:
2217            self.OnFileSaveas(event)
2218
2219    def OnFileSaveas(self, event):
2220        '''Save the current project in response to the
2221        File/Save as menu button
2222        '''
2223        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2224            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2225        try:
2226            if dlg.ShowModal() == wx.ID_OK:
2227                self.GSASprojectfile = dlg.GetPath()
2228                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2229                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2230                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2231                G2IO.ProjFileSave(self)
2232                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2233        finally:
2234            dlg.Destroy()
2235
2236    def ExitMain(self, event):
2237        '''Called if the main window is closed'''
2238        if self.undofile:
2239            os.remove(self.undofile)
2240        sys.exit()
2241       
2242    def OnFileExit(self, event):
2243        '''Called in response to the File/Quit menu button'''
2244        if self.dataFrame:
2245            self.dataFrame.Clear() 
2246            self.dataFrame.Destroy()
2247        self.Close()
2248       
2249    def OnExportPatterns(self,event):
2250        names = ['All']
2251        exports = []
2252        item, cookie = self.PatternTree.GetFirstChild(self.root)
2253        while item:
2254            name = self.PatternTree.GetItemText(item)
2255            if 'PWDR' in name:
2256                names.append(name)
2257            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2258        if names:
2259            dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2260            if dlg.ShowModal() == wx.ID_OK:
2261                sel = dlg.GetSelections()
2262                if sel[0] == 0:
2263                    exports = names[1:]
2264                else:
2265                    for x in sel:
2266                        exports.append(names[x])
2267            dlg.Destroy()
2268        if exports:
2269            dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '', 
2270                'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2271                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2272            try:
2273                if dlg.ShowModal() == wx.ID_OK:
2274                    powderfile = dlg.GetPath()
2275                    powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2276                    if 'fxye' in powderfile:
2277                        G2IO.powderFxyeSave(self,exports,powderfile)
2278                    else:       #just xye
2279                        G2IO.powderXyeSave(self,exports,powderfile)
2280            finally:
2281                dlg.Destroy()
2282       
2283    def OnExportPeakList(self,event):
2284        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2285            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2286        try:
2287            if dlg.ShowModal() == wx.ID_OK:
2288                self.peaklistfile = dlg.GetPath()
2289                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2290                file = open(self.peaklistfile,'w')               
2291                item, cookie = self.PatternTree.GetFirstChild(self.root)
2292                while item:
2293                    name = self.PatternTree.GetItemText(item)
2294                    if 'PWDR' in name:
2295                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2296                        while item2:
2297                            name2 = self.PatternTree.GetItemText(item2)
2298                            if name2 == 'Peak List':
2299                                peaks = self.PatternTree.GetItemPyData(item2)
2300                                file.write("%s \n" % (name+' Peak List'))               
2301                                for peak in peaks:
2302                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2303                                        (peak[0],peak[2],peak[4],peak[6]))
2304                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2305                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2306                file.close()
2307        finally:
2308            dlg.Destroy()
2309       
2310    def OnExportHKL(self,event):
2311        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2312            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2313        try:
2314            if dlg.ShowModal() == wx.ID_OK:
2315                self.peaklistfile = dlg.GetPath()
2316                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2317                file = open(self.peaklistfile,'w')               
2318                item, cookie = self.PatternTree.GetFirstChild(self.root)
2319                while item:
2320                    name = self.PatternTree.GetItemText(item)
2321                    if 'PWDR' in name:
2322                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2323                        while item2:
2324                            name2 = self.PatternTree.GetItemText(item2)
2325                            if name2 == 'Reflection Lists':
2326                                data = self.PatternTree.GetItemPyData(item2)
2327                                phases = data.keys()
2328                                for phase in phases:
2329                                    peaks = data[phase]
2330                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2331                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2332                                    for peak in peaks:
2333                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2334                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2335                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2336                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2337                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2338                file.close()
2339        finally:
2340            dlg.Destroy()
2341       
2342    def OnExportPDF(self,event):
2343        #need S(Q) and G(R) to be saved here - probably best from selection?
2344        names = ['All']
2345        exports = []
2346        item, cookie = self.PatternTree.GetFirstChild(self.root)
2347        while item:
2348            name = self.PatternTree.GetItemText(item)
2349            if 'PDF' in name:
2350                names.append(name)
2351            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2352        if names:
2353            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2354            if dlg.ShowModal() == wx.ID_OK:
2355                sel = dlg.GetSelections()
2356                if sel[0] == 0:
2357                    exports = names[1:]
2358                else:
2359                    for x in sel:
2360                        exports.append(names[x])
2361            dlg.Destroy()
2362        if exports:
2363            G2IO.PDFSave(self,exports)
2364       
2365    def OnMakePDFs(self,event):
2366        '''Calculates PDFs
2367        '''
2368        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2369        TextList = ['All PWDR']
2370        PDFlist = []
2371        Names = []
2372        if self.PatternTree.GetCount():
2373            id, cookie = self.PatternTree.GetFirstChild(self.root)
2374            while id:
2375                name = self.PatternTree.GetItemText(id)
2376                Names.append(name)
2377                if 'PWDR' in name:
2378                    TextList.append(name)
2379                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2380            if len(TextList) == 1:
2381                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2382                return
2383            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2384            try:
2385                if dlg.ShowModal() == wx.ID_OK:
2386                    result = dlg.GetSelections()
2387                    for i in result: PDFlist.append(TextList[i])
2388                    if 0 in result:
2389                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2390                    for item in PDFlist:
2391                        PWDRname = item[4:]
2392                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2393                        Data = {
2394                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2395                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2396                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2397                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2398                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2399                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2400                            'Lorch':True,}
2401                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2402                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2403                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2404                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2405                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2406                for item in self.ExportPDF: item.Enable(True)
2407            finally:
2408                dlg.Destroy()
2409               
2410    def GetPWDRdatafromTree(self,PWDRname):
2411        ''' Returns powder data from GSASII tree
2412
2413        :param str PWDRname: a powder histogram name as obtained from
2414          :meth:`GSASIIstruct.GetHistogramNames`
2415
2416        :returns: PWDRdata = powder data dictionary with
2417          Powder data arrays, Limits, Instrument Parameters,
2418          Sample Parameters           
2419        '''
2420        PWDRdata = {}
2421        try:
2422            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2423        except ValueError:
2424            PWDRdata['wtFactor'] = 1.0
2425        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2426        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2427        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2428        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2429        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2430        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2431        if 'ranId' not in PWDRdata['Sample Parameters']:
2432            PWDRdata['Sample Parameters']['ranId'] = ran.randint(0,sys.maxint)
2433        PWDRdata['ranId'] = PWDRdata['Sample Parameters']['ranId']
2434        return PWDRdata
2435
2436    def GetHKLFdatafromTree(self,HKLFname):
2437        ''' Returns single crystal data from GSASII tree
2438
2439        :param str HKLFname: a single crystal histogram name as obtained
2440          from
2441          :meth:`GSASIIstruct.GetHistogramNames`
2442
2443        :returns: HKLFdata = single crystal data list of reflections
2444
2445        '''
2446        HKLFdata = {}
2447        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2448#        try:
2449#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2450#        except ValueError:
2451#            HKLFdata['wtFactor'] = 1.0
2452        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2453        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2454        return HKLFdata
2455       
2456    def GetPhaseData(self):
2457        '''Returns a list of defined phases. Used only in GSASIIgrid
2458        Note routine :meth:`GSASIIstruct.GetPhaseData` also exists.
2459        '''
2460        phaseData = {}
2461        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2462            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2463        else:
2464            print 'no phases to be refined'
2465            return
2466        if sub:
2467            item, cookie = self.PatternTree.GetFirstChild(sub)
2468            while item:
2469                phaseName = self.PatternTree.GetItemText(item)
2470                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2471                if 'ranId' not in phaseData[phaseName]:
2472                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2473                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2474        return phaseData               
2475                   
2476    def GetUsedHistogramsAndPhasesfromTree(self):
2477        ''' Returns all histograms that are found in any phase
2478        and any phase that uses a histogram
2479        :returns: two dicts:
2480
2481            * Histograms = dictionary of histograms as {name:data,...}
2482            * Phases = dictionary of phases that use histograms
2483        '''
2484        phaseData = self.GetPhaseData()
2485        if not phaseData:
2486            return {},{}
2487        Histograms = {}
2488        Phases = {}
2489        for phase in phaseData:
2490            Phase = phaseData[phase]
2491            if Phase['Histograms']:
2492                if phase not in Phases:
2493                    Phases[phase] = Phase
2494                for hist in Phase['Histograms']:
2495                    if hist not in Histograms:
2496                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
2497                        if 'PWDR' in hist[:4]: 
2498                            Histograms[hist] = self.GetPWDRdatafromTree(item)
2499                        elif 'HKLF' in hist[:4]:
2500                            Histograms[hist] = self.GetHKLFdatafromTree(item)
2501                        #future restraint, etc. histograms here
2502        return Histograms,Phases
2503       
2504    class ViewParmDialog(wx.Dialog):
2505        '''Window to show all parameters in the refinement.
2506        Called from :meth:`OnViewLSParms`
2507        '''
2508        def __init__(self,parent,title,parmDict):
2509            wx.Dialog.__init__(self,parent,-1,title,size=(300,430),
2510                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2511            panel = wx.Panel(self,size=(300,430))
2512            parmNames = parmDict.keys()
2513            parmNames.sort()
2514            parmText = ' p:h:Parameter       refine?              value\n'
2515            for name in parmNames:
2516                parmData = parmDict[name]
2517                try:
2518                    parmText += ' %s \t%12.4g \n'%(name.ljust(19)+'\t'+parmData[1],parmData[0])
2519                except TypeError:
2520                    pass
2521            parmTable = wx.TextCtrl(panel,-1,parmText,
2522                style=wx.TE_MULTILINE|wx.TE_READONLY,size=(290,400))
2523            mainSizer = wx.BoxSizer(wx.VERTICAL)
2524            mainSizer.Add(parmTable)
2525            panel.SetSizer(mainSizer)
2526                           
2527    def OnViewLSParms(self,event):
2528        '''Displays a window showing all parameters in the refinement.
2529        Called from the Calculate/View LS Parms menu.
2530        '''
2531        parmDict = {}
2532        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
2533        for phase in Phases:
2534            if 'pId' not in Phases[phase]:
2535                self.ErrorDialog('View parameter error','You must run least squares at least once')
2536                return
2537        rigidbodyDict = self.PatternTree.GetItemPyData(   
2538            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
2539        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2540        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2541        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
2542        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
2543        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
2544        varyList = rbVary+phaseVary+hapVary+histVary
2545        parmDict.update(rbDict)
2546        parmDict.update(phaseDict)
2547        parmDict.update(hapDict)
2548        parmDict.update(histDict)
2549        for parm in parmDict:
2550            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
2551                'Omega','Chi','Phi','nDebye','nPeaks']:
2552                parmDict[parm] = [parmDict[parm],' ']
2553            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
2554                parmDict[parm] = [parmDict[parm],' ']
2555            elif parm in varyList:
2556                parmDict[parm] = [parmDict[parm],'True']
2557            else:
2558                parmDict[parm] = [parmDict[parm],'False']
2559        parmDict[' Num refined'] = [len(varyList),'']
2560        dlg = self.ViewParmDialog(self,'Parameters for least squares',parmDict)
2561        try:
2562            if dlg.ShowModal() == wx.ID_OK:
2563                print 'do something with changes?? - No!'
2564        finally:
2565            dlg.Destroy()
2566       
2567    def OnRefine(self,event):
2568        '''Perform a refinement.
2569        Called from the Calculate/Refine menu.
2570        '''       
2571        self.OnFileSave(event)
2572        # check that constraints are OK here
2573        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2574        if errmsg:
2575            print('Error in constraints:\n'+errmsg+
2576                  '\nRefinement not possible')
2577            self.ErrorDialog('Constraint Error',
2578                             'Error in constraints:\n'+errmsg+
2579                             '\nRefinement not possible')
2580            return
2581        if warnmsg:
2582            print('Conflict between refinment flag settings and constraints:\n'+
2583                  warnmsg+'\nRefinement not possible')
2584            self.ErrorDialog('Refinement Flag Error',
2585                             'Conflict between refinment flag settings and constraints:\n'+
2586                             warnmsg+
2587                             '\nRefinement not possible')
2588            return
2589        #works - but it'd be better if it could restore plots
2590        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
2591            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2592        screenSize = wx.ClientDisplayRect()
2593        Size = dlg.GetSize()
2594        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2595        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2596        dlg.SetSize(Size)
2597        Rw = 100.00
2598        try:
2599            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
2600        finally:
2601            dlg.Destroy()
2602        oldId =  self.PatternTree.GetSelection()
2603        oldName = self.PatternTree.GetItemText(oldId)
2604        parentId = self.PatternTree.GetItemParent(oldId)
2605        parentName = ''
2606        if parentId:
2607            parentName = self.PatternTree.GetItemText(parentId)
2608        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
2609        try:
2610            if dlg.ShowModal() == wx.ID_OK:
2611                Id = 0
2612                self.PatternTree.DeleteChildren(self.root)
2613                if self.HKL: self.HKL = []
2614                if self.G2plotNB.plotList:
2615                    self.G2plotNB.clear()
2616                G2IO.ProjFileOpen(self)
2617                item, cookie = self.PatternTree.GetFirstChild(self.root)
2618                while item and not Id:
2619                    name = self.PatternTree.GetItemText(item)
2620                    if name[:4] in ['PWDR','HKLF']:
2621                        Id = item
2622                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2623                if parentName:
2624                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
2625                    if parentId:
2626                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
2627                    else:
2628                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
2629                    self.PatternTree.SelectItem(itemId)
2630                elif Id:
2631                    self.PatternTree.SelectItem(Id)
2632        finally:
2633            dlg.Destroy()
2634
2635    def OnSeqRefine(self,event):
2636        '''Perform a sequential refinement.
2637        Called from the Calculate/Sequential refine menu.
2638        '''       
2639        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequental results')
2640        if not Id:
2641            Id = self.PatternTree.AppendItem(self.root,text='Sequental results')
2642            self.PatternTree.SetItemPyData(Id,{})           
2643        self.OnFileSave(event)
2644        # check that constraints are OK here
2645        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2646        if errmsg:
2647            print('Error in constraints:\n'+errmsg+
2648                  '\nRefinement not possible')
2649            self.ErrorDialog('Constraint Error',
2650                             'Error in constraints:\n'+errmsg+
2651                             '\nRefinement not possible')
2652            return
2653        if warnmsg:
2654            print('Conflict between refinment flag settings and constraints:\n'+
2655                  warnmsg+'\nRefinement not possible')
2656            self.ErrorDialog('Refinement Flag Error',
2657                             'Conflict between refinment flag settings and constraints:\n'+
2658                             warnmsg+'\nRefinement not possible')
2659            return
2660        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
2661            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2662        screenSize = wx.ClientDisplayRect()
2663        Size = dlg.GetSize()
2664        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2665        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2666        dlg.SetSize(Size)
2667        try:
2668            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
2669        finally:
2670            dlg.Destroy()       
2671        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
2672        try:
2673            if dlg.ShowModal() == wx.ID_OK:
2674                Id = 0
2675                self.PatternTree.DeleteChildren(self.root)
2676                if self.HKL: self.HKL = []
2677                if self.G2plotNB.plotList:
2678                    self.G2plotNB.clear()
2679                G2IO.ProjFileOpen(self)
2680                item, cookie = self.PatternTree.GetFirstChild(self.root)
2681                while item and not Id:
2682                    name = self.PatternTree.GetItemText(item)
2683                    if name[:4] in ['PWDR','HKLF']:
2684                        Id = item
2685                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2686                if Id:
2687                    self.PatternTree.SelectItem(Id)
2688        finally:
2689            dlg.Destroy()
2690       
2691    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
2692        'Display an error message'
2693        result = None
2694        if parent is None:
2695            dlg = wx.MessageDialog(self, message, title,  wtype)
2696        else:
2697            dlg = wx.MessageDialog(parent, message, title,  wtype)
2698            dlg.CenterOnParent() # not working on Mac
2699        try:
2700            result = dlg.ShowModal()
2701        finally:
2702            dlg.Destroy()
2703        return result
2704
2705class GSASIImain(wx.App):
2706    '''Defines a wxApp for GSAS-II
2707
2708    Creates a wx frame (self.main) which contains the display of the
2709    data tree.
2710    '''
2711    def OnInit(self):
2712        '''Called automatically when the app is created.'''
2713        self.main = GSASII(None)
2714        self.main.Show()
2715        self.SetTopWindow(self.main)
2716        return True
2717    def MacOpenFile(self, filename):
2718        '''Called on Mac every time a file is dropped on the app when it is running,
2719        treat this like a File/Open project menu action.
2720        Should be ignored on other platforms
2721        '''
2722        self.main.OnFileOpen(None,filename)
2723
2724def main():
2725    '''Start up the GSAS-II application'''
2726    #application = GSASIImain() # don't redirect output, someday we
2727    # may want to do this if we can
2728    application = GSASIImain(0)
2729    if wxInspector: wxeye.InspectionTool().Show()
2730
2731    #application.main.OnRefine(None)
2732    application.MainLoop()
2733   
2734if __name__ == '__main__':
2735    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.