source: trunk/GSASII.py @ 945

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

some mods for POWGEN data for single peak fitting
unify profile parm calcs into G2mth

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