source: trunk/GSASII.py @ 1117

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

update some menu labels; Add new multi-item selector; Add map export; misc export cleanups

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