source: trunk/GSASII.py @ 933

Last change on this file since 933 was 926, checked in by vondreele, 12 years ago

split GSASIIstruct.py into 3 pieces

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