source: trunk/GSASII.py @ 1141

Last change on this file since 1141 was 1141, checked in by toby, 9 years ago

minor tweaks: move indexing of Ids to histogram load, remove Type from HAP var list

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