source: trunk/GSASII.py @ 1125

Last change on this file since 1125 was 1125, checked in by vondreele, 9 years ago

StructureFactor2 does SF calculation for entire block of reflection - see substantial speed gains in some cases.
The old StructureFactor? routine is left in as a reference - modified form factor stuff

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