source: trunk/GSASII.py @ 906

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

switch to aui.notebook for data display window; work on phase data display window - quicker initial draw, fix horizontal lines (needs more work); more sphinx documentation

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