source: trunk/GSASII.py @ 1138

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

major constraints revision

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