source: trunk/GSASII.py @ 1086

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

finish dummy (simulated) histogram

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