source: trunk/GSASII.py @ 1092

Last change on this file since 1092 was 1092, checked in by vondreele, 10 years ago

remove GSASIIsolve - never will be used
put back matrix rescaling for v-cov matrix
add detail for penalty fxn output - still something wrong with this

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