source: trunk/GSASII.py @ 903

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

Fix problem with large constraints; Change binding of data item menus so they can be used from any window (Mac only); Start on AUI notebook (not in use); fix periodic table on Mac; capture error if argument file is not found; start on sphinx documentation formatting

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