source: trunk/GSASII.py @ 1143

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

rework constraints to handle names and refine flag for new var (input linear constraints); redo powder 'Sample Parameters'

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 126.4 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2013-11-11 02:32:26 +0000 (Mon, 11 Nov 2013) $
6# $Author: toby $
7# $Revision: 1143 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1143 2013-11-11 02:32:26Z 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: 1143 $")
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,copy.copy(G2gd.DefaultControls))
1616        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
1617            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
1618            self.PatternTree.SetItemPyData(sub,{})
1619        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
1620            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
1621            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
1622        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
1623            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
1624            self.PatternTree.SetItemPyData(sub,{})
1625        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
1626            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
1627            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
1628                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
1629               
1630    class CopyDialog(wx.Dialog):
1631        '''Creates a dialog for copying control settings between
1632        data tree items'''
1633        def __init__(self,parent,title,text,data):
1634            wx.Dialog.__init__(self,parent,-1,title, 
1635                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1636            self.data = data
1637            panel = wx.Panel(self)
1638            mainSizer = wx.BoxSizer(wx.VERTICAL)
1639            topLabl = wx.StaticText(panel,-1,text)
1640            mainSizer.Add((10,10),1)
1641            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1642            mainSizer.Add((10,10),1)
1643            ncols = len(data)/40+1
1644            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=ncols,hgap=2,vgap=2)
1645            for id,item in enumerate(self.data):
1646                ckbox = wx.CheckBox(panel,id,item[1])
1647                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
1648                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
1649            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1650            OkBtn = wx.Button(panel,-1,"Ok")
1651            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1652            cancelBtn = wx.Button(panel,-1,"Cancel")
1653            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1654            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1655            btnSizer.Add((20,20),1)
1656            btnSizer.Add(OkBtn)
1657            btnSizer.Add((20,20),1)
1658            btnSizer.Add(cancelBtn)
1659            btnSizer.Add((20,20),1)
1660           
1661            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1662            panel.SetSizer(mainSizer)
1663            panel.Fit()
1664            self.Fit()
1665       
1666        def OnCopyChange(self,event):
1667            id = event.GetId()
1668            self.data[id][0] = self.FindWindowById(id).GetValue()       
1669           
1670        def OnOk(self,event):
1671            parent = self.GetParent()
1672            parent.Raise()
1673            self.EndModal(wx.ID_OK)             
1674           
1675        def OnCancel(self,event):
1676            parent = self.GetParent()
1677            parent.Raise()
1678            self.EndModal(wx.ID_CANCEL)             
1679           
1680        def GetData(self):
1681            return self.data
1682       
1683    class SumDialog(wx.Dialog):
1684        'Allows user to supply scale factor(s) when summing data'
1685        def __init__(self,parent,title,text,dataType,data):
1686            wx.Dialog.__init__(self,parent,-1,title, 
1687                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1688            self.data = data
1689            panel = wx.Panel(self)
1690            mainSizer = wx.BoxSizer(wx.VERTICAL)
1691            topLabl = wx.StaticText(panel,-1,text)
1692            mainSizer.Add((10,10),1)
1693            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1694            mainSizer.Add((10,10),1)
1695            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1696            for id,item in enumerate(self.data[:-1]):
1697                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
1698                name.SetEditable(False)
1699                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1700                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1701                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1702                dataGridSizer.Add(scale,0,wx.LEFT,10)
1703                dataGridSizer.Add(name,0,wx.RIGHT,10)
1704            if dataType:
1705                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
1706                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
1707                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
1708                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
1709                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
1710                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
1711            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1712            OkBtn = wx.Button(panel,-1,"Ok")
1713            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1714            cancelBtn = wx.Button(panel,-1,"Cancel")
1715            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1716            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1717            btnSizer.Add((20,20),1)
1718            btnSizer.Add(OkBtn)
1719            btnSizer.Add((20,20),1)
1720            btnSizer.Add(cancelBtn)
1721            btnSizer.Add((20,20),1)
1722           
1723            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1724            panel.SetSizer(mainSizer)
1725            panel.Fit()
1726            self.Fit()
1727
1728        def OnScaleChange(self,event):
1729            id = event.GetId()
1730            value = self.FindWindowById(id).GetValue()
1731            try:
1732                self.data[id][0] = float(value)
1733                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1734            except ValueError:
1735                if value and '-' not in value[0]:
1736                    print 'bad input - numbers only'
1737                    self.FindWindowById(id).SetValue('0.000')
1738           
1739        def OnNameChange(self,event):
1740            self.data[-1] = self.name.GetValue() 
1741           
1742        def OnOk(self,event):
1743            parent = self.GetParent()
1744            parent.Raise()
1745            self.EndModal(wx.ID_OK)             
1746           
1747        def OnCancel(self,event):
1748            parent = self.GetParent()
1749            parent.Raise()
1750            self.EndModal(wx.ID_CANCEL)             
1751           
1752        def GetData(self):
1753            return self.data
1754                       
1755    def OnPwdrSum(self,event):
1756        'Sum together powder data(?)'
1757        TextList = []
1758        DataList = []
1759        SumList = []
1760        Names = []
1761        Inst = None
1762        SumItemList = []
1763        Comments = ['Sum equals: \n']
1764        if self.PatternTree.GetCount():
1765            item, cookie = self.PatternTree.GetFirstChild(self.root)
1766            while item:
1767                name = self.PatternTree.GetItemText(item)
1768                Names.append(name)
1769                if 'PWDR' in name:
1770                    TextList.append([0.0,name])
1771                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
1772                    if not Inst:
1773                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
1774                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1775            if len(TextList) < 2:
1776                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
1777                return
1778            TextList.append('default_sum_name')               
1779            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
1780            try:
1781                if dlg.ShowModal() == wx.ID_OK:
1782                    lenX = 0
1783                    Xminmax = [0,0]
1784                    Xsum = []
1785                    Ysum = []
1786                    Vsum = []
1787                    result = dlg.GetData()
1788                    for i,item in enumerate(result[:-1]):
1789                        scale,name = item
1790                        data = DataList[i]
1791                        if scale:
1792                            Comments.append("%10.3f %s" % (scale,' * '+name))
1793                            x,y,w,yc,yb,yd = data   #numpy arrays!
1794                            v = 1./w
1795                            if lenX:
1796                                if lenX != len(x):
1797                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
1798                                        '\nExpected:'+str(lenX)+ \
1799                                        '\nFound:   '+str(len(x))+'\nfor '+name)
1800                                    return
1801                            else:
1802                                lenX = len(x)
1803                            if Xminmax[1]:
1804                                if Xminmax != [x[0],x[-1]]:
1805                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
1806                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
1807                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
1808                                    return
1809                                else:
1810                                    for j,yi in enumerate(y):
1811                                         Ysum[j] += scale*yi
1812                                         Vsum[j] += abs(scale)*v[j]
1813                            else:
1814                                Xminmax = [x[0],x[-1]]
1815                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
1816                                for j,yi in enumerate(y):
1817                                    Xsum.append(x[j])
1818                                    Ysum.append(scale*yi)
1819                                    Vsum.append(abs(scale*v[j]))
1820                    Wsum = 1./np.array(Vsum)
1821                    outname = 'PWDR '+result[-1]
1822                    Id = 0
1823                    if outname in Names:
1824                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1825                        try:
1826                            if dlg2.ShowModal() == wx.ID_OK:
1827                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1828                                self.PatternTree.Delete(Id)
1829                        finally:
1830                            dlg2.Destroy()
1831                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1832                    if Id:
1833                        Sample = G2pdG.SetDefaultSample()
1834                        valuesdict = {
1835                            'wtFactor':1.0,
1836                            'Dummy':False,
1837                            'ranId':ran.randint(0,sys.maxint),
1838                            }
1839                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
1840                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
1841                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
1842                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
1843                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
1844                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1845                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
1846                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1847                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
1848                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
1849                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1850                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
1851                        self.PatternTree.SelectItem(Id)
1852                        self.PatternTree.Expand(Id)
1853            finally:
1854                dlg.Destroy()
1855
1856    def OnImageSum(self,event):
1857        'Sum together image data(?)'
1858        TextList = []
1859        DataList = []
1860        SumList = []
1861        Names = []
1862        Inst = []
1863        SumItemList = []
1864        Comments = ['Sum equals: \n']
1865        if self.PatternTree.GetCount():
1866            item, cookie = self.PatternTree.GetFirstChild(self.root)
1867            while item:
1868                name = self.PatternTree.GetItemText(item)
1869                Names.append(name)
1870                if 'IMG' in name:
1871                    TextList.append([0.0,name])
1872                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
1873                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
1874                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1875            if len(TextList) < 2:
1876                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
1877                return
1878            TextList.append('default_sum_name')               
1879            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
1880            try:
1881                if dlg.ShowModal() == wx.ID_OK:
1882                    imSize = 0
1883                    result = dlg.GetData()
1884                    First = True
1885                    Found = False
1886                    for i,item in enumerate(result[:-1]):
1887                        scale,name = item
1888                        data = DataList[i]
1889                        if scale:
1890                            Found = True                               
1891                            Comments.append("%10.3f %s" % (scale,' * '+name))
1892                            Npix,imagefile = data
1893                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
1894                            if First:
1895                                newImage = np.zeros_like(image)
1896                                First = False
1897                            if imSize:
1898                                if imSize != Npix:
1899                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
1900                                        '\nExpected:'+str(imSize)+ \
1901                                        '\nFound:   '+str(Npix)+'\nfor '+name)
1902                                    return
1903                                newImage = newImage+scale*image
1904                            else:
1905                                imSize = Npix
1906                                newImage = newImage+scale*image
1907                            del(image)
1908                    if not Found:
1909                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
1910                        return
1911                       
1912                    newImage = np.asfarray(newImage,dtype=np.float32)                       
1913                    outname = 'IMG '+result[-1]
1914                    Id = 0
1915                    if outname in Names:
1916                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1917                        try:
1918                            if dlg2.ShowModal() == wx.ID_OK:
1919                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1920                        finally:
1921                            dlg2.Destroy()
1922                    else:
1923                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1924                    if Id:
1925                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
1926                            'G2img files (*.G2img)|*.G2img', 
1927                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1928                        if dlg.ShowModal() == wx.ID_OK:
1929                            newimagefile = dlg.GetPath()
1930                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
1931                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
1932                            Imax = np.amax(newImage)
1933                            Imin = np.amin(newImage)
1934                            newImage = []
1935                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
1936                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1937                        del(newImage)
1938                        if self.imageDefault:
1939                            Data = copy.copy(self.imageDefault)
1940                        Data['showLines'] = True
1941                        Data['ring'] = []
1942                        Data['rings'] = []
1943                        Data['cutoff'] = 10
1944                        Data['pixLimit'] = 20
1945                        Data['ellipses'] = []
1946                        Data['calibrant'] = ''
1947                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1948                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
1949                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1950                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1951                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),{})
1952                        self.PatternTree.SelectItem(Id)
1953                        self.PatternTree.Expand(Id)
1954                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
1955                        self.Image = self.PickId
1956            finally:
1957                dlg.Destroy()
1958                     
1959    def OnAddPhase(self,event):
1960        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
1961        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
1962            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
1963        else:
1964            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1965        PhaseName = ''
1966        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
1967            style=wx.OK)
1968        if dlg.ShowModal() == wx.ID_OK:
1969            PhaseName = dlg.GetValue()
1970        dlg.Destroy()
1971        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
1972        E,SGData = G2spc.SpcGroup('P 1')
1973        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
1974       
1975    def OnDeletePhase(self,event):
1976        'Delete a phase from the tree. Called by Data/Delete Phase menu'
1977        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
1978        if self.dataFrame:
1979            self.dataFrame.Clear() 
1980        TextList = []
1981        DelList = []
1982        DelItemList = []
1983        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
1984            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1985        else:
1986            return
1987        if sub:
1988            item, cookie = self.PatternTree.GetFirstChild(sub)
1989            while item:
1990                TextList.append(self.PatternTree.GetItemText(item))
1991                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
1992            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
1993            try:
1994                if dlg.ShowModal() == wx.ID_OK:
1995                    result = dlg.GetSelections()
1996                    for i in result: DelList.append([i,TextList[i]])
1997                    item, cookie = self.PatternTree.GetFirstChild(sub)
1998                    i = 0
1999                    while item:
2000                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2001                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2002                        i += 1
2003                    for item in DelItemList:
2004                        name = self.PatternTree.GetItemText(item)
2005                        self.PatternTree.Delete(item)
2006                        self.G2plotNB.Delete(name)
2007                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2008                    while item:
2009                        name = self.PatternTree.GetItemText(item)
2010                        if 'PWDR' in name:
2011                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2012                            refList = self.PatternTree.GetItemPyData(Id)
2013                            for i,item in DelList:
2014                                del(refList[item])
2015                            self.PatternTree.SetItemPyData(Id,refList)
2016                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2017            finally:
2018                dlg.Destroy()
2019               
2020    def OnRenameData(self,event):
2021        'Renames an existing phase. Called by Data/Rename Phase menu'
2022        name = self.PatternTree.GetItemText(self.PickId)     
2023        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2024            dataType = name[:name.index(' ')+1]                 #includes the ' '
2025            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2026                defaultValue=name[name.index(' ')+1:])
2027            try:
2028                if dlg.ShowModal() == wx.ID_OK:
2029                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2030            finally:
2031                dlg.Destroy()
2032       
2033    def GetFileList(self,fileType,skip=None):        #potentially useful?
2034        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2035        fileList = []
2036        Source = ''
2037        id, cookie = self.PatternTree.GetFirstChild(self.root)
2038        while id:
2039            name = self.PatternTree.GetItemText(id)
2040            if fileType in name:
2041                if id == skip:
2042                    Source = name
2043                else:
2044                    fileList.append([False,name,id])
2045            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2046        if skip:
2047            return fileList,Source
2048        else:
2049            return fileList
2050           
2051    def OnDataDelete(self, event):
2052        '''Delete one or more histograms from data tree. Called by the
2053        Data/DeleteData menu
2054        '''
2055        TextList = ['All Data']
2056        DelList = []
2057        DelItemList = []
2058        ifPWDR = False
2059        ifIMG = False
2060        ifHKLF = False
2061        ifPDF = False
2062        if self.PatternTree.GetCount():
2063            item, cookie = self.PatternTree.GetFirstChild(self.root)
2064            while item:
2065                name = self.PatternTree.GetItemText(item)
2066                if name not in ['Notebook','Controls','Covariance','Constraints','Restraints','Phases']:
2067                    if 'PWDR' in name: ifPWDR = True
2068                    if 'IMG' in name: ifIMG = True
2069                    if 'HKLF' in name: ifHKLF = True
2070                    if 'PDF' in name: ifPDF = True
2071                    TextList.append(name)
2072                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2073            if ifPWDR: TextList.insert(1,'All PWDR')
2074            if ifIMG: TextList.insert(1,'All IMG')
2075            if ifHKLF: TextList.insert(1,'All HKLF')
2076            if ifPDF: TextList.insert(1,'All PDF')               
2077            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2078            try:
2079                if dlg.ShowModal() == wx.ID_OK:
2080                    result = dlg.GetSelections()
2081                    for i in result: DelList.append(TextList[i])
2082                    if 'All Data' in DelList:
2083                        DelList = [item for item in TextList if item[:3] != 'All']
2084                    elif 'All PWDR' in DelList:
2085                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2086                    elif 'All IMG' in DelList:
2087                        DelList = [item for item in TextList if item[:3] == 'IMG']
2088                    elif 'All HKLF' in DelList:
2089                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2090                    elif 'All PDF' in DelList:
2091                        DelList = [item for item in TextList if item[:3] == 'PDF']
2092                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2093                    while item:
2094                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2095                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2096                    for item in DelItemList:
2097                        self.PatternTree.Delete(item)
2098                    self.PickId = 0
2099                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2100            finally:
2101                dlg.Destroy()
2102
2103    def OnFileOpen(self, event, filename=None):
2104        '''Reads in a GSAS-II .gpx project file in response to the
2105        File/Open Project menu button
2106        '''
2107        result = wx.ID_OK
2108        Id = 0
2109        if self.PatternTree.GetChildrenCount(self.root,False):
2110            if self.dataFrame:
2111                self.dataFrame.Clear() 
2112            dlg = wx.MessageDialog(
2113                self,
2114                'Do you want to overwrite the current project? '
2115                'Any unsaved changes will be lost. Press OK to continue.',
2116                'Overwrite?',  wx.OK | wx.CANCEL)
2117            try:
2118                result = dlg.ShowModal()
2119                if result == wx.ID_OK:
2120                    self.PatternTree.DeleteChildren(self.root)
2121                    self.GSASprojectfile = ''
2122                    if self.HKL: self.HKL = []
2123                    if self.G2plotNB.plotList:
2124                        self.G2plotNB.clear()
2125            finally:
2126                dlg.Destroy()
2127        if result != wx.ID_OK: return
2128
2129        if not filename:
2130            if self.dataDisplay: self.dataDisplay.Destroy()
2131            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2132                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2133            try:
2134                if dlg.ShowModal() != wx.ID_OK: return
2135                self.GSASprojectfile = dlg.GetPath()
2136                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2137                self.dirname = dlg.GetDirectory()
2138            finally:
2139                dlg.Destroy()
2140        else:
2141            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2142            self.dirname = os.path.split(filename)[0]
2143
2144        G2IO.ProjFileOpen(self)
2145        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2146        self.PatternTree.Expand(self.root)
2147        self.HKL = []
2148        item, cookie = self.PatternTree.GetFirstChild(self.root)
2149        while item and not Id:
2150            name = self.PatternTree.GetItemText(item)
2151            if name[:4] in ['PWDR','HKLF','IMG ','PDF ']:
2152                Id = item
2153            elif name == 'Controls':
2154                data = self.PatternTree.GetItemPyData(item)
2155                if data:
2156                    for item in self.Refine: item.Enable(True)
2157                    for item in self.SeqRefine: item.Enable(True)
2158            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2159        if Id:
2160            self.PatternTree.SelectItem(Id)
2161        self.CheckNotebook()
2162        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2163
2164    def OnFileClose(self, event):
2165        '''Clears the data tree in response to the
2166        File/New Project menu button. User is given option to save
2167        the project.
2168        '''
2169        if self.dataFrame:
2170            self.dataFrame.Clear()
2171            self.dataFrame.SetLabel('GSAS-II data display') 
2172        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2173        try:
2174            result = dlg.ShowModal()
2175            if result == wx.ID_OK:
2176                self.OnFileSaveMenu(event)
2177            if result != wx.ID_CANCEL:
2178                self.GSASprojectfile = ''
2179                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2180                self.PatternTree.DeleteChildren(self.root)
2181                if self.HKL: self.HKL = []
2182                if self.G2plotNB.plotList:
2183                    self.G2plotNB.clear()
2184        finally:
2185            dlg.Destroy()
2186
2187    def OnFileSave(self, event):
2188        '''Save the current project in response to the
2189        File/Save Project menu button
2190        '''
2191       
2192        if self.GSASprojectfile: 
2193            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2194            G2IO.ProjFileSave(self)
2195        else:
2196            self.OnFileSaveas(event)
2197
2198    def OnFileSaveas(self, event):
2199        '''Save the current project in response to the
2200        File/Save as menu button
2201        '''
2202        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2203            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2204        try:
2205            if dlg.ShowModal() == wx.ID_OK:
2206                self.GSASprojectfile = dlg.GetPath()
2207                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2208                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2209                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2210                G2IO.ProjFileSave(self)
2211                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2212        finally:
2213            dlg.Destroy()
2214
2215    def ExitMain(self, event):
2216        '''Called if the main window is closed'''
2217        if self.undofile:
2218            os.remove(self.undofile)
2219        sys.exit()
2220       
2221    def OnFileExit(self, event):
2222        '''Called in response to the File/Quit menu button'''
2223        if self.dataFrame:
2224            self.dataFrame.Clear() 
2225            self.dataFrame.Destroy()
2226        self.Close()
2227       
2228    def OnExportPatterns(self,event):
2229        names = ['All']
2230        exports = []
2231        item, cookie = self.PatternTree.GetFirstChild(self.root)
2232        while item:
2233            name = self.PatternTree.GetItemText(item)
2234            if 'PWDR' in name:
2235                names.append(name)
2236            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2237        if names:
2238            dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2239            if dlg.ShowModal() == wx.ID_OK:
2240                sel = dlg.GetSelections()
2241                if sel[0] == 0:
2242                    exports = names[1:]
2243                else:
2244                    for x in sel:
2245                        exports.append(names[x])
2246            dlg.Destroy()
2247        if exports:
2248            dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '', 
2249                'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2250                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2251            try:
2252                if dlg.ShowModal() == wx.ID_OK:
2253                    powderfile = dlg.GetPath()
2254                    powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2255                    if 'fxye' in powderfile:
2256                        G2IO.powderFxyeSave(self,exports,powderfile)
2257                    else:       #just xye
2258                        G2IO.powderXyeSave(self,exports,powderfile)
2259            finally:
2260                dlg.Destroy()
2261       
2262    def OnExportPeakList(self,event):
2263        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2264            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2265        try:
2266            if dlg.ShowModal() == wx.ID_OK:
2267                self.peaklistfile = dlg.GetPath()
2268                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2269                file = open(self.peaklistfile,'w')               
2270                item, cookie = self.PatternTree.GetFirstChild(self.root)
2271                while item:
2272                    name = self.PatternTree.GetItemText(item)
2273                    if 'PWDR' in name:
2274                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2275                        while item2:
2276                            name2 = self.PatternTree.GetItemText(item2)
2277                            if name2 == 'Peak List':
2278                                peaks = self.PatternTree.GetItemPyData(item2)
2279                                file.write("%s \n" % (name+' Peak List'))               
2280                                for peak in peaks:
2281                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2282                                        (peak[0],peak[2],peak[4],peak[6]))
2283                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2284                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2285                file.close()
2286        finally:
2287            dlg.Destroy()
2288       
2289    def OnExportHKL(self,event):
2290        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2291            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2292        try:
2293            if dlg.ShowModal() == wx.ID_OK:
2294                self.peaklistfile = dlg.GetPath()
2295                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2296                file = open(self.peaklistfile,'w')               
2297                item, cookie = self.PatternTree.GetFirstChild(self.root)
2298                while item:
2299                    name = self.PatternTree.GetItemText(item)
2300                    if 'PWDR' in name:
2301                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2302                        while item2:
2303                            name2 = self.PatternTree.GetItemText(item2)
2304                            if name2 == 'Reflection Lists':
2305                                data = self.PatternTree.GetItemPyData(item2)
2306                                phases = data.keys()
2307                                for phase in phases:
2308                                    peaks = data[phase]
2309                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2310                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2311                                    for peak in peaks:
2312                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2313                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2314                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2315                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2316                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2317                file.close()
2318        finally:
2319            dlg.Destroy()
2320       
2321    def OnExportPDF(self,event):
2322        #need S(Q) and G(R) to be saved here - probably best from selection?
2323        names = ['All']
2324        exports = []
2325        item, cookie = self.PatternTree.GetFirstChild(self.root)
2326        while item:
2327            name = self.PatternTree.GetItemText(item)
2328            if 'PDF' in name:
2329                names.append(name)
2330            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2331        if names:
2332            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2333            if dlg.ShowModal() == wx.ID_OK:
2334                sel = dlg.GetSelections()
2335                if sel[0] == 0:
2336                    exports = names[1:]
2337                else:
2338                    for x in sel:
2339                        exports.append(names[x])
2340            dlg.Destroy()
2341        if exports:
2342            G2IO.PDFSave(self,exports)
2343       
2344    def OnMakePDFs(self,event):
2345        '''Calculates PDFs
2346        '''
2347        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2348        TextList = ['All PWDR']
2349        PDFlist = []
2350        Names = []
2351        if self.PatternTree.GetCount():
2352            id, cookie = self.PatternTree.GetFirstChild(self.root)
2353            while id:
2354                name = self.PatternTree.GetItemText(id)
2355                Names.append(name)
2356                if 'PWDR' in name:
2357                    TextList.append(name)
2358                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2359            if len(TextList) == 1:
2360                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2361                return
2362            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2363            try:
2364                if dlg.ShowModal() == wx.ID_OK:
2365                    result = dlg.GetSelections()
2366                    for i in result: PDFlist.append(TextList[i])
2367                    if 0 in result:
2368                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2369                    for item in PDFlist:
2370                        PWDRname = item[4:]
2371                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2372                        Data = {
2373                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2374                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2375                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2376                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2377                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2378                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2379                            'Lorch':True,}
2380                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2381                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2382                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2383                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2384                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2385                for item in self.ExportPDF: item.Enable(True)
2386            finally:
2387                dlg.Destroy()
2388               
2389    def GetPWDRdatafromTree(self,PWDRname):
2390        ''' Returns powder data from GSASII tree
2391
2392        :param str PWDRname: a powder histogram name as obtained from
2393          :meth:`GSASIIstruct.GetHistogramNames`
2394
2395        :returns: PWDRdata = powder data dictionary with
2396          Powder data arrays, Limits, Instrument Parameters,
2397          Sample Parameters           
2398        '''
2399        PWDRdata = {}
2400        try:
2401            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2402        except ValueError:
2403            PWDRdata['wtFactor'] = 1.0
2404        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2405        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2406        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2407        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2408        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2409        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2410        if 'ranId' not in PWDRdata:  # patch, add a random Id
2411            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
2412        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
2413            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
2414        return PWDRdata
2415
2416    def GetHKLFdatafromTree(self,HKLFname):
2417        ''' Returns single crystal data from GSASII tree
2418
2419        :param str HKLFname: a single crystal histogram name as obtained
2420          from
2421          :meth:`GSASIIstruct.GetHistogramNames`
2422
2423        :returns: HKLFdata = single crystal data list of reflections
2424
2425        '''
2426        HKLFdata = {}
2427        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2428#        try:
2429#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2430#        except ValueError:
2431#            HKLFdata['wtFactor'] = 1.0
2432        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2433        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2434        return HKLFdata
2435       
2436    def GetPhaseData(self):
2437        '''Returns a dict with defined phases.
2438        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
2439        get same info from GPX file.
2440        '''
2441        phaseData = {}
2442        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2443            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2444        else:
2445            print 'no phases found in GetPhaseData'
2446            sub = None
2447        if sub:
2448            item, cookie = self.PatternTree.GetFirstChild(sub)
2449            while item:
2450                phaseName = self.PatternTree.GetItemText(item)
2451                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2452                if 'ranId' not in phaseData[phaseName]:
2453                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2454                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2455        return phaseData
2456
2457    def GetPhaseNames(self):
2458        '''Returns a list of defined phases.
2459        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
2460        get same info from GPX file.
2461        '''
2462        phaseNames = []
2463        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2464            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2465        else:
2466            print 'no phases found in GetPhaseNames'
2467            sub = None
2468        if sub:
2469            item, cookie = self.PatternTree.GetFirstChild(sub)
2470            while item:
2471                phase = self.PatternTree.GetItemText(item)
2472                phaseNames.append(phase)
2473                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2474        return phaseNames
2475   
2476    def GetHistogramNames(self,hType):
2477        """ Returns a list of histogram names found in the GSASII data tree
2478        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
2479        get same info from GPX file.
2480       
2481        :param str hType: list of histogram types
2482        :return: list of histogram names
2483       
2484        """
2485        HistogramNames = []
2486        if self.PatternTree.GetCount():
2487            item, cookie = self.PatternTree.GetFirstChild(self.root)
2488            while item:
2489                name = self.PatternTree.GetItemText(item)
2490                if name[:4] in hType:
2491                    HistogramNames.append(name)       
2492                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2493
2494        return HistogramNames
2495
2496                   
2497    def GetUsedHistogramsAndPhasesfromTree(self):
2498        ''' Returns all histograms that are found in any phase
2499        and any phase that uses a histogram.
2500        This also assigns numbers to used phases and histograms by the
2501        order they appear in the file.
2502        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
2503        get same info from GPX file.
2504
2505        :returns: (Histograms,Phases)
2506
2507            * Histograms = dictionary of histograms as {name:data,...}
2508            * Phases = dictionary of phases that use histograms
2509        '''
2510        Histograms = {}
2511        Phases = {}
2512        phaseNames = self.GetPhaseNames()
2513        phaseData = self.GetPhaseData()
2514        histoList = self.GetHistogramNames(['PWDR','HKLF'])
2515
2516        for phase in phaseData:
2517            Phase = phaseData[phase]
2518            if Phase['Histograms']:
2519                if phase not in Phases:
2520                    pId = phaseNames.index(phase)
2521                    Phase['pId'] = pId
2522                    Phases[phase] = Phase
2523                for hist in Phase['Histograms']:
2524                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
2525                        Phase['Histograms'][hist]['Use'] = True         
2526                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
2527                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
2528                        if item:
2529                            if 'PWDR' in hist[:4]: 
2530                                Histograms[hist] = self.GetPWDRdatafromTree(item)
2531                            elif 'HKLF' in hist[:4]:
2532                                Histograms[hist] = self.GetHKLFdatafromTree(item)
2533                            hId = histoList.index(hist)
2534                            Histograms[hist]['hId'] = hId
2535                        else: # would happen if a referenced histogram were renamed or deleted
2536                            print('For phase "'+str(phase)+
2537                                  '" unresolved reference to histogram "'+str(hist)+'"')
2538        G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
2539        return Histograms,Phases
2540       
2541    class ViewParmDialog(wx.Dialog):
2542        '''Window to show all parameters in the refinement.
2543        Called from :meth:`OnViewLSParms`
2544        '''
2545        def __init__(self,parent,title,parmDict):
2546            wx.Dialog.__init__(self,parent,-1,title,size=(300,430),
2547                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2548            panel = wx.Panel(self,size=(300,430))
2549            parmNames = parmDict.keys()
2550            parmNames.sort()
2551            parmText = ' p:h:Parameter       refine?              value\n'
2552            for name in parmNames:
2553                parmData = parmDict[name]
2554                try:
2555                    parmText += ' %s \t%12.4g \n'%(name.ljust(19)+'\t'+parmData[1],parmData[0])
2556                except TypeError:
2557                    pass
2558            parmTable = wx.TextCtrl(panel,-1,parmText,
2559                style=wx.TE_MULTILINE|wx.TE_READONLY,size=(290,400))
2560            mainSizer = wx.BoxSizer(wx.VERTICAL)
2561            mainSizer.Add(parmTable)
2562            panel.SetSizer(mainSizer)
2563                           
2564    def OnViewLSParms(self,event):
2565        '''Displays a window showing all parameters in the refinement.
2566        Called from the Calculate/View LS Parms menu.
2567        '''
2568        parmDict = {}
2569        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
2570        for phase in Phases:
2571            if 'pId' not in Phases[phase]:
2572                self.ErrorDialog('View parameter error','You must run least squares at least once')
2573                return
2574        rigidbodyDict = self.PatternTree.GetItemPyData(   
2575            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
2576        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2577        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2578        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
2579        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
2580        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
2581        varyList = rbVary+phaseVary+hapVary+histVary
2582        parmDict.update(rbDict)
2583        parmDict.update(phaseDict)
2584        parmDict.update(hapDict)
2585        parmDict.update(histDict)
2586        for parm in parmDict:
2587            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
2588                'Omega','Chi','Phi','nDebye','nPeaks']:
2589                parmDict[parm] = [parmDict[parm],' ']
2590            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
2591                parmDict[parm] = [parmDict[parm],' ']
2592            elif parm in varyList:
2593                parmDict[parm] = [parmDict[parm],'True']
2594            else:
2595                parmDict[parm] = [parmDict[parm],'False']
2596        parmDict[' Num refined'] = [len(varyList),'']
2597        dlg = self.ViewParmDialog(self,'Parameters for least squares',parmDict)
2598        try:
2599            if dlg.ShowModal() == wx.ID_OK:
2600                print 'do something with changes?? - No!'
2601        finally:
2602            dlg.Destroy()
2603       
2604    def OnRefine(self,event):
2605        '''Perform a refinement.
2606        Called from the Calculate/Refine menu.
2607        '''       
2608        self.OnFileSave(event)
2609        # check that constraints are OK here
2610        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2611        if errmsg:
2612            print('Error in constraints:\n'+errmsg+
2613                  '\nRefinement not possible')
2614            self.ErrorDialog('Constraint Error',
2615                             'Error in constraints:\n'+errmsg+
2616                             '\nRefinement not possible')
2617            return
2618        if warnmsg:
2619            print('Conflict between refinment flag settings and constraints:\n'+
2620                  warnmsg+'\nRefinement not possible')
2621            self.ErrorDialog('Refinement Flag Error',
2622                             'Conflict between refinment flag settings and constraints:\n'+
2623                             warnmsg+
2624                             '\nRefinement not possible')
2625            return
2626        #works - but it'd be better if it could restore plots
2627        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
2628            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2629        screenSize = wx.ClientDisplayRect()
2630        Size = dlg.GetSize()
2631        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2632        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2633        dlg.SetSize(Size)
2634        Rw = 100.00
2635        try:
2636            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
2637        finally:
2638            dlg.Destroy()
2639        oldId =  self.PatternTree.GetSelection()
2640        oldName = self.PatternTree.GetItemText(oldId)
2641        parentId = self.PatternTree.GetItemParent(oldId)
2642        parentName = ''
2643        if parentId:
2644            parentName = self.PatternTree.GetItemText(parentId)
2645        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
2646        try:
2647            if dlg.ShowModal() == wx.ID_OK:
2648                Id = 0
2649                self.PatternTree.DeleteChildren(self.root)
2650                if self.HKL: self.HKL = []
2651                if self.G2plotNB.plotList:
2652                    self.G2plotNB.clear()
2653                G2IO.ProjFileOpen(self)
2654                item, cookie = self.PatternTree.GetFirstChild(self.root)
2655                while item and not Id:
2656                    name = self.PatternTree.GetItemText(item)
2657                    if name[:4] in ['PWDR','HKLF']:
2658                        Id = item
2659                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2660                if parentName:
2661                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
2662                    if parentId:
2663                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
2664                    else:
2665                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
2666                    self.PatternTree.SelectItem(itemId)
2667                elif Id:
2668                    self.PatternTree.SelectItem(Id)
2669        finally:
2670            dlg.Destroy()
2671
2672    def OnSeqRefine(self,event):
2673        '''Perform a sequential refinement.
2674        Called from the Calculate/Sequential refine menu.
2675        '''       
2676        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequental results')
2677        if not Id:
2678            Id = self.PatternTree.AppendItem(self.root,text='Sequental results')
2679            self.PatternTree.SetItemPyData(Id,{})           
2680        self.OnFileSave(event)
2681        # check that constraints are OK here
2682        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2683        if errmsg:
2684            print('Error in constraints:\n'+errmsg+
2685                  '\nRefinement not possible')
2686            self.ErrorDialog('Constraint Error',
2687                             'Error in constraints:\n'+errmsg+
2688                             '\nRefinement not possible')
2689            return
2690        if warnmsg:
2691            print('Conflict between refinment flag settings and constraints:\n'+
2692                  warnmsg+'\nRefinement not possible')
2693            self.ErrorDialog('Refinement Flag Error',
2694                             'Conflict between refinment flag settings and constraints:\n'+
2695                             warnmsg+'\nRefinement not possible')
2696            return
2697        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
2698            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2699        screenSize = wx.ClientDisplayRect()
2700        Size = dlg.GetSize()
2701        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2702        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2703        dlg.SetSize(Size)
2704        try:
2705            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
2706        finally:
2707            dlg.Destroy()       
2708        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
2709        try:
2710            if dlg.ShowModal() == wx.ID_OK:
2711                Id = 0
2712                self.PatternTree.DeleteChildren(self.root)
2713                if self.HKL: self.HKL = []
2714                if self.G2plotNB.plotList:
2715                    self.G2plotNB.clear()
2716                G2IO.ProjFileOpen(self)
2717                item, cookie = self.PatternTree.GetFirstChild(self.root)
2718                while item and not Id:
2719                    name = self.PatternTree.GetItemText(item)
2720                    if name[:4] in ['PWDR','HKLF']:
2721                        Id = item
2722                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2723                if Id:
2724                    self.PatternTree.SelectItem(Id)
2725        finally:
2726            dlg.Destroy()
2727       
2728    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
2729        'Display an error message'
2730        result = None
2731        if parent is None:
2732            dlg = wx.MessageDialog(self, message, title,  wtype)
2733        else:
2734            dlg = wx.MessageDialog(parent, message, title,  wtype)
2735            dlg.CenterOnParent() # not working on Mac
2736        try:
2737            result = dlg.ShowModal()
2738        finally:
2739            dlg.Destroy()
2740        return result
2741
2742class GSASIImain(wx.App):
2743    '''Defines a wxApp for GSAS-II
2744
2745    Creates a wx frame (self.main) which contains the display of the
2746    data tree.
2747    '''
2748    def OnInit(self):
2749        '''Called automatically when the app is created.'''
2750        self.main = GSASII(None)
2751        self.main.Show()
2752        self.SetTopWindow(self.main)
2753        return True
2754    def MacOpenFile(self, filename):
2755        '''Called on Mac every time a file is dropped on the app when it is running,
2756        treat this like a File/Open project menu action.
2757        Should be ignored on other platforms
2758        '''
2759        self.main.OnFileOpen(None,filename)
2760
2761def main():
2762    '''Start up the GSAS-II application'''
2763    #application = GSASIImain() # don't redirect output, someday we
2764    # may want to do this if we can
2765    application = GSASIImain(0)
2766    if wxInspector:
2767        import wx.lib.inspection as wxeye
2768        wxeye.InspectionTool().Show()
2769
2770    #application.main.OnRefine(None)
2771    application.MainLoop()
2772   
2773if __name__ == '__main__':
2774    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.