source: trunk/GSASII.py @ 938

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

Add instrument name support; add export menu & CIF testing

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 114.5 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2013-05-31 23:14:01 +0000 (Fri, 31 May 2013) $
6# $Author: toby $
7# $Revision: 938 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 938 2013-05-31 23:14:01Z toby $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32import wx.lib.inspection as wxeye
33try:
34    import OpenGL as ogl
35except ImportError:
36    print('*******************************************************')
37    print('PyOpenGL is missing from your python installation')
38    print('     - we will try to install it')
39    print('*******************************************************')
40    def install_with_easyinstall(package):
41        try: 
42            print "trying a system-wide PyOpenGl install"
43            easy_install.main(['-f',os.path.split(__file__)[0],package])
44            return
45        except:
46            pass
47        try: 
48            print "trying a user level PyOpenGl install"
49            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
50            return
51        except:
52            print "Install of '+package+' failed. Please report this information:"
53            import traceback
54            print traceback.format_exc()
55            sys.exit()
56    from setuptools.command import easy_install
57    install_with_easyinstall('PyOpenGl')
58    print('*******************************************************')         
59    print('OpenGL has been installed. Please restart GSAS-II')
60    print('*******************************************************')         
61    sys.exit()
62
63# load the GSAS routines
64import GSASIIpath
65GSASIIpath.SetVersionNumber("$Revision: 938 $")
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','sig-0','sig-1','X','Y','Azimuth']
733                codes = [0,0,0,0,0,0,0,0,0,0,0,0,0]
734                azm = 0.
735                if 'INS  1DETAZM' in Iparm:
736                    azm = float(Iparm['INS  1DETAZM'])
737                s = Iparm['INS  1BNKPAR'].split()
738                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
739                s = Iparm['INS  1 ICONS'].split()
740                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])    #difC, difA, Zero
741                s = Iparm['INS  1PRCF1 '].split()
742                pfType = int(s[0])
743                s = Iparm['INS  1PRCF11'].split()
744                if abs(pfType) == 1:
745                    data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])])
746                    s = Iparm['INS  1PRCF12'].split()
747                    data.extend([0.0,G2IO.sfloat(s[1]),0.0,0.0,azm])
748                elif abs(pfType) in [3,4,5]:
749                    data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])
750                    if abs(pfType) == 4:
751                        data.extend([0.0,G2IO.sfloat(s[3]),0.0,0.0,azm])
752                    else:
753                        s = Iparm['INS  1PRCF12'].split()
754                        data.extend([0.0,G2IO.sfloat(s[0]),0.0,0.0,azm])                       
755                Inst1 = G2IO.makeInstDict(names,data,codes)
756                Inst2 = {}
757                if pfType < 0:
758                    Ipab = 'INS  1PAB'+str(-pfType)
759                    Npab = int(Iparm[Ipab+'  '].strip())
760                    Inst2['Pdabc'] = []
761                    for i in range(Npab):
762                        k = Ipab+str(i+1).rjust(2)
763                        s = Iparm[k].split()
764                        Inst2['Pdabc'].append([float(t) for t in s])
765                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
766                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
767                if 'INS  1I ITYP' in Iparm:
768                    s = Iparm['INS  1I ITYP'].split()
769                    Ityp = int(s[0])
770                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
771                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
772                    if Ityp in [1,2,4]:
773                        Inst2['Itype'] = Itypes[Ityp-1]
774                        Inst2['Tminmax'] = Tminmax
775                        Icoeff = []
776                        Iesd = []
777                        Icovar = []                   
778                        for i in range(3):
779                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
780                            Icoeff += [float(S) for S in s]
781                            s = Iparm['INS  1IECOF'+str(i+1)].split()
782                            Iesd += [float(S) for S in s]
783                        for i in range(8):
784                            s = Iparm['INS  1IECOR'+str(i+1)].split()
785                            Icovar += [float(S) for S in s]
786                        Inst2['Icoeff'] = Icoeff
787                        Inst2['Iesd'] = Iesd
788                        Inst2['Icovar'] = Icovar
789                return [Inst1,Inst2]
790
791        # stuff we might need from the reader
792        filename = rd.powderentry[0]
793        bank = rd.powderentry[2]
794        numbanks = rd.numbanks
795        # is there an instrument parameter file defined for the current data set?
796        if rd.instparm or (lastdatafile == filename and lastIparmfile):
797            if rd.instparm:
798                instfile = os.path.join(os.path.split(filename)[0],
799                                    rd.instparm)
800            else:
801                # for multiple reads of one data file, reuse the inst parm file
802                instfile = lastIparmfile
803            if os.path.exists(instfile):
804                #print 'debug: try read',instfile
805                instParmList = self.ReadPowderInstprm(instfile)
806                if instParmList is not None:
807                    rd.instfile = instfile
808                    rd.instmsg = 'GSAS-II file '+instfile
809                    return instParmList
810                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
811                if Iparm:
812                    #print 'debug: success'
813                    rd.instfile = instfile
814                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
815                    return SetPowderInstParms(Iparm,rd)
816            else:
817                self.ErrorDialog('Open Error','Error opening instrument parameter file '
818                    +str(instfile)+' requested by file '+ filename)
819        # is there an instrument parameter file matching the current file
820        # with extension .inst or .prm? If so read it
821        basename = os.path.splitext(filename)[0]
822        for ext in '.instprm','.prm','.inst','.ins':
823            instfile = basename + ext
824            instParmList = self.ReadPowderInstprm(instfile)
825            if instParmList is not None:
826                rd.instfile = instfile
827                rd.instmsg = 'GSAS-II file '+instfile
828                return instParmList
829            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
830            if Iparm:
831                #print 'debug: success'
832                rd.instfile = instfile
833                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
834                return SetPowderInstParms(Iparm,rd)
835            else:
836                #print 'debug: open/read failed',instfile
837                pass # fail silently
838
839        # did we read the data file from a zip? If so, look there for a
840        # instrument parameter file
841        if self.zipfile:
842            for ext in '.instprm','.prm','.inst','.ins':
843                instfile = G2IO.ExtractFileFromZip(
844                    self.zipfile,
845                    selection=os.path.split(basename + ext)[1],
846                    parent=self)
847                if instfile is not None and instfile != self.zipfile:
848                    print 'debug:',instfile,'created from ',self.zipfile
849                    instParmList = self.ReadPowderInstprm(instfile)
850                    if instParmList is not None:
851                        rd.instfile = instfile
852                        rd.instmsg = 'GSAS-II file '+instfile
853                        return instParmList
854                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
855                    if Iparm:
856                        rd.instfile = instfile
857                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
858                        return SetPowderInstParms(Iparm,rd)
859                    else:
860                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
861                        pass # fail silently
862
863        while True: # loop until we get a file that works or we get a cancel
864            instfile = ''
865            dlg = wx.FileDialog(
866                self,
867                'Choose inst. param file for "'
868                +rd.idstring
869                +'" (or Cancel for default)',
870                '.', '',
871                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
872                'GSAS-II iparm file (*.instprm)|*.instprm|'
873                'All files (*.*)|*.*', 
874                wx.OPEN|wx.CHANGE_DIR)
875            if os.path.exists(lastIparmfile):
876                dlg.SetFilename(lastIparmfile)
877            if dlg.ShowModal() == wx.ID_OK:
878                instfile = dlg.GetPath()
879            dlg.Destroy()
880            if not instfile: break
881            instParmList = self.ReadPowderInstprm(instfile)
882            if instParmList is not None:
883                rd.instfile = instfile
884                rd.instmsg = 'GSAS-II file '+instfile
885                return instParmList
886            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
887            if Iparm:
888                #print 'debug: success with',instfile
889                rd.instfile = instfile
890                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
891                return SetPowderInstParms(Iparm,rd)
892            else:
893                self.ErrorDialog('Read Error',
894                                 'Error opening/reading file '+str(instfile))
895       
896        # still no success: offer user choice of defaults
897        while True: # loop until we get a choice
898            choices = []
899            head = 'Select from default instrument parameters for '+rd.idstring
900
901            for l in rd.defaultIparm_lbl:
902                choices.append('Defaults for '+l)
903            res = rd.BlockSelector(
904                choices,
905                ParentFrame=self,
906                title=head,
907                header='Select default inst parms',
908                useCancel=False)
909            if res is None: continue
910            rd.instfile = ''
911            rd.instmsg = 'default: '+rd.defaultIparm_lbl[res]
912            return SetPowderInstParms(rd.defaultIparms[res],rd)
913
914    def OnImportPowder(self,event):
915        '''Called in response to an Import/Powder Data/... menu item
916        to read a powder diffraction data set.
917        dict self.ImportMenuId is used to look up the specific
918        reader item associated with the menu item, which will be
919        None for the last menu item, which is the "guess" option
920        where all appropriate formats will be tried.
921
922        Also reads an instrument parameter file for each dataset.
923        '''
924        reqrdr = self.ImportMenuId.get(event.GetId())  # look up which format was requested
925        rdlist = self.OnImportGeneric(
926            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
927        if len(rdlist) == 0: return
928        self.CheckNotebook()
929        Iparm = None
930        lastIparmfile = ''
931        lastdatafile = ''
932        for rd in rdlist:
933            # get instrument parameters for each dataset
934            Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
935            if rd.repeat_instparm: 
936                lastIparmfile = rd.instfile
937            lastdatafile = rd.powderentry[0]
938            print 'Read powder data '+str(rd.idstring)+ \
939                ' from file '+str(self.lastimport) + \
940                ' with parameters from '+str(rd.instmsg)
941            # data are read, now store them in the tree
942            Id = self.PatternTree.AppendItem(parent=self.root,
943                text='PWDR '+rd.idstring)
944            if 'T' in Iparm1['Type'][0]:
945                if not rd.clockWd and rd.GSAS:
946                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
947                cw = np.diff(rd.powderdata[0])
948                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
949                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
950                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
951                if 'Itype' in Iparm2:
952                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
953                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
954                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
955                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
956                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
957                    var = 1./rd.powderdata[2][Ibeg:Ifin]
958                    var += WYI*rd.powderdata[1]**2
959                    var /= YI**2
960                    rd.powderdata[2] = 1./var
961                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
962                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
963                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
964            Tmin = min(rd.powderdata[0])
965            Tmax = max(rd.powderdata[0])
966            self.PatternTree.SetItemPyData(Id,[{'wtFactor':1.0},rd.powderdata])
967            self.PatternTree.SetItemPyData(
968                self.PatternTree.AppendItem(Id,text='Comments'),
969                rd.comments)
970            self.PatternTree.SetItemPyData(
971                self.PatternTree.AppendItem(Id,text='Limits'),
972                [(Tmin,Tmax),[Tmin,Tmax]])
973            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
974            self.PatternTree.SetItemPyData(
975                self.PatternTree.AppendItem(Id,text='Background'),
976                [['chebyschev',True,3,1.0,0.0,0.0],
977                 {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
978            self.PatternTree.SetItemPyData(
979                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
980                [Iparm1,Iparm2])
981            self.PatternTree.SetItemPyData(
982                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
983                rd.Sample)
984            self.PatternTree.SetItemPyData(
985                self.PatternTree.AppendItem(Id,text='Peak List')
986                ,[])
987            self.PatternTree.SetItemPyData(
988                self.PatternTree.AppendItem(Id,text='Index Peak List'),
989                [])
990            self.PatternTree.SetItemPyData(
991                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
992                [])
993            self.PatternTree.SetItemPyData(
994                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
995                {})
996            self.PatternTree.Expand(Id)
997        self.PatternTree.SelectItem(Id)
998        return # success
999
1000    def _init_Exports(self,menu):
1001        '''This is a place holder for when exports are handled in a manner similar to imports
1002        '''
1003#        submenu = wx.Menu()
1004#        item = menu.AppendMenu(
1005#            wx.ID_ANY, 'entire project',
1006#            submenu, help='Export entire project')
1007
1008        # for now hard-code CIF testing here
1009        item = menu.Append(
1010            wx.ID_ANY,
1011            help='CIF development',
1012            kind=wx.ITEM_NORMAL,
1013            text='full CIF test')
1014        self.Bind(wx.EVT_MENU, self.OnTestCIF, id=item.GetId())
1015        item = menu.Append(
1016            wx.ID_ANY,
1017            help='CIF development',
1018            kind=wx.ITEM_NORMAL,
1019            text='quick CIF test')
1020        self.Bind(wx.EVT_MENU, self.OnTestCIF, id=item.GetId())
1021
1022    def OnTestCIF(self,event):
1023        # hard-code CIF testing here
1024        if event.EventObject.GetLabelText(event.Id).split()[0].lower() == "quick":
1025            mode = 'simple'
1026        else:
1027            mode = 'full'
1028        path2GSAS2 = os.path.join(
1029            os.path.dirname(os.path.realpath(__file__)), # location of this file
1030            'exports')
1031        if path2GSAS2 not in sys.path: sys.path.append(path2GSAS2)
1032        #reload(G2IO)
1033        import G2cif
1034        reload(G2cif)
1035        exp = G2cif.ExportCIF(self)
1036        exp.export(mode)
1037
1038    def _Add_ExportMenuItems(self,parent):
1039        item = parent.Append(
1040            help='Select PWDR item to enable',id=wx.ID_ANY,
1041            kind=wx.ITEM_NORMAL,
1042            text='Export Powder Patterns...')
1043        self.ExportPattern.append(item)
1044        item.Enable(False)
1045        self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1046
1047        item = parent.Append(
1048            help='',id=wx.ID_ANY,
1049            kind=wx.ITEM_NORMAL,
1050            text='Export All Peak Lists...')
1051        self.ExportPeakList.append(item)
1052        item.Enable(True)
1053        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1054
1055        item = parent.Append(
1056            help='',id=wx.ID_ANY,
1057            kind=wx.ITEM_NORMAL,
1058            text='Export HKLs...')
1059        self.ExportHKL.append(item)
1060        item.Enable(False)
1061        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1062
1063        item = parent.Append(
1064            help='Select PDF item to enable',
1065            id=wx.ID_ANY,
1066            kind=wx.ITEM_NORMAL,
1067            text='Export PDF...')
1068        self.ExportPDF.append(item)
1069        item.Enable(False)
1070        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1071
1072        item = parent.Append(
1073            help='',id=wx.ID_ANY,
1074            kind=wx.ITEM_NORMAL,
1075            text='Export Phase...')
1076        self.ExportPhase.append(item)
1077        item.Enable(False)
1078        self.Bind(wx.EVT_MENU, self.OnExportPhase, id=item.GetId())
1079
1080        item = parent.Append(
1081            help='',id=wx.ID_ANY,
1082            kind=wx.ITEM_NORMAL,
1083            text='Export CIF...')
1084        self.ExportCIF.append(item)
1085        item.Enable(False)
1086        self.Bind(wx.EVT_MENU, self.OnExportCIF, id=item.GetId())
1087               
1088    def FillMainMenu(self,menubar):
1089        '''Define contents of the main GSAS-II menu for the (main) data tree window
1090        in the mac, used also for the data item windows as well.
1091        '''
1092        File = wx.Menu(title='')
1093        menubar.Append(menu=File, title='&File')
1094        self._Add_FileMenuItems(File)
1095        Data = wx.Menu(title='')
1096        menubar.Append(menu=Data, title='Data')
1097        self._Add_DataMenuItems(Data)
1098        Calculate = wx.Menu(title='')       
1099        menubar.Append(menu=Calculate, title='&Calculate')
1100        self._Add_CalculateMenuItems(Calculate)
1101        Import = wx.Menu(title='')       
1102        menubar.Append(menu=Import, title='Import')
1103        self._Add_ImportMenu_Phase(Import)
1104        self._Add_ImportMenu_powder(Import)
1105        self._Add_ImportMenu_Sfact(Import)
1106        Export = wx.Menu(title='')       
1107        menubar.Append(menu=Export, title='Export')
1108        self._init_Exports(Export)
1109        self._Add_ExportMenuItems(Export)
1110        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1111            morehelpitems=[('&Tutorials','Tutorials')])
1112        menubar.Append(menu=HelpMenu,title='&Help')
1113
1114    def _init_ctrls(self, parent):
1115        wx.Frame.__init__(self, name='GSASII', parent=parent,
1116            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1117        clientSize = wx.ClientDisplayRect()
1118        Size = self.GetSize()
1119        xPos = clientSize[2]-Size[0]
1120        self.SetPosition(wx.Point(xPos,clientSize[1]))
1121        self._init_Imports()
1122        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1123        self.MakePDF = []
1124        self.Refine = []
1125        self.SeqRefine = []
1126        self.ExportPattern = []
1127        self.ExportPeakList = []
1128        self.ExportHKL = []
1129        self.ExportPDF = []
1130        self.ExportPhase = []
1131        self.ExportCIF = []
1132        #
1133        self.GSASIIMenu = wx.MenuBar()
1134        self.FillMainMenu(self.GSASIIMenu)
1135        self.SetMenuBar(self.GSASIIMenu)
1136        self.Bind(wx.EVT_SIZE, self.OnSize)
1137        self.CreateStatusBar()
1138        self.mainPanel = wx.Panel(self,-1)
1139       
1140        wxID_PATTERNTREE = wx.NewId()
1141        self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE,
1142            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1143        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,
1144            self.OnPatternTreeSelChanged, id=wxID_PATTERNTREE)
1145        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1146            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1147        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1148            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1149        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1150            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1151        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1152            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1153        self.root = self.PatternTree.AddRoot('Loaded Data: ')
1154       
1155        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1156            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1157        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1158        plotFrame.Show()
1159       
1160        self.dataDisplay = None
1161       
1162    def __init__(self, parent):
1163        self._init_ctrls(parent)
1164        self.Bind(wx.EVT_CLOSE, self.ExitMain)
1165        # various defaults
1166        self.oldFocus = None
1167        self.GSASprojectfile = ''
1168        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
1169        self.undofile = ''
1170        self.TreeItemDelete = False
1171        self.Offset = [0.0,0.0]
1172        self.delOffset = .02
1173        self.refOffset = -100.0
1174        self.refDelt = .01
1175        self.Weight = False
1176        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
1177        self.IfPlot = False
1178        self.PatternId = 0
1179        self.PickId = 0
1180        self.PeakTable = []
1181        self.LimitsTable = []
1182        self.HKL = []
1183        self.Lines = []
1184        self.itemPicked = None
1185        self.dataFrame = None
1186        self.Interpolate = 'nearest'
1187        self.ContourColor = 'Paired'
1188        self.VcovColor = 'RdYlGn'
1189        self.RamaColor = 'Blues'
1190        self.Projection = 'equal area'
1191        self.logPlot = False
1192        self.qPlot = False
1193        self.Contour = False
1194        self.Legend = False
1195        self.SinglePlot = False
1196        self.SubBack = False
1197        self.plotView = 0
1198        self.Image = 0
1199        self.oldImagefile = ''
1200        self.ImageZ = []
1201        self.Integrate = 0
1202        self.imageDefault = {}
1203        self.Sngl = 0
1204        self.ifGetRing = False
1205        self.setPoly = False
1206        arg = sys.argv
1207        if len(arg) > 1:
1208            self.GSASprojectfile = arg[1]
1209            self.dirname = os.path.dirname(arg[1])
1210            if self.dirname: os.chdir(self.dirname)
1211            try:
1212                G2IO.ProjFileOpen(self)
1213                self.PatternTree.Expand(self.root)
1214                for item in self.Refine: item.Enable(True)
1215                for item in self.SeqRefine: item.Enable(True)
1216            except:
1217                print 'Error opening file',arg[1]
1218
1219    def OnSize(self,event):
1220        'Called when the main window is resized. Not sure why'
1221        w,h = self.GetClientSizeTuple()
1222        self.mainPanel.SetSize(wx.Size(w,h))
1223        self.PatternTree.SetSize(wx.Size(w,h))
1224                       
1225    def OnPatternTreeSelChanged(self, event):
1226        '''Called when a data tree item is selected'''
1227        if self.TreeItemDelete:
1228            self.TreeItemDelete = False
1229        else:
1230            pltNum = self.G2plotNB.nb.GetSelection()
1231            if pltNum >= 0:                         #to avoid the startup with no plot!
1232                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1233                pltPlot = pltPage.figure
1234            item = event.GetItem()
1235            G2gd.MovePatternTreeToGrid(self,item)
1236            if self.oldFocus:
1237                self.oldFocus.SetFocus()
1238       
1239    def OnPatternTreeItemCollapsed(self, event):
1240        'Called when a tree item is collapsed'
1241        event.Skip()
1242
1243    def OnPatternTreeItemExpanded(self, event):
1244        'Called when a tree item is expanded'
1245        event.Skip()
1246       
1247    def OnPatternTreeItemDelete(self, event):
1248        'Called when a tree item is deleted -- not sure what this does'
1249        self.TreeItemDelete = True
1250
1251    def OnPatternTreeItemActivated(self, event):
1252        'Called when a tree item is activated'
1253        event.Skip()
1254       
1255    def OnPatternTreeKeyDown(self,event):
1256        'Not sure what this does'
1257        key = event.GetKeyCode()
1258        item = self.PickId
1259        if type(item) is int: return # is this the toplevel in tree?
1260        if key == wx.WXK_UP:
1261            self.oldFocus = wx.Window.FindFocus()
1262            self.PatternTree.GetPrevSibling(item)
1263        elif key == wx.WXK_DOWN:
1264            self.oldFocus = wx.Window.FindFocus()
1265            self.PatternTree.GetNextSibling(item)
1266               
1267    def OnReadPowderPeaks(self,event):
1268        'Bound to menu Data/Read Powder Peaks -- still needed?'
1269        Cuka = 1.54052
1270        self.CheckNotebook()
1271        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
1272            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
1273        try:
1274            if dlg.ShowModal() == wx.ID_OK:
1275                self.HKL = []
1276                self.powderfile = dlg.GetPath()
1277                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
1278                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
1279                data = ['PKS',Cuka,0.0]
1280                names = ['Type','Lam','Zero'] 
1281                codes = [0,0,0]
1282                inst = [G2IO.makeInstDict(names,data,codes),{}]
1283                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1284                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
1285                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),peaks)
1286                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1287                self.PatternTree.Expand(Id)
1288                self.PatternTree.SelectItem(Id)
1289                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1290        finally:
1291            dlg.Destroy()
1292           
1293    def OnImageRead(self,event):
1294        'Called to read in an image in any known format'
1295        self.CheckNotebook()
1296        dlg = wx.FileDialog(
1297            self, 'Choose image files', '.', '',
1298            'Any image file (*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img)|'
1299            '*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.zip|'
1300            'European detector file (*.edf)|*.edf|'
1301            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
1302            'MAR file (*.mar*)|*.mar*|'
1303            'GE Image (*.avg;*.sum)|*.avg;*.sum|'
1304            'ADSC Image (*.img)|*.img|'
1305            'GSAS-II Image (*.G2img)|*.G2img|'
1306            'Zip archive (*.zip)|*.zip|'
1307            'All files (*.*)|*.*',
1308            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
1309        try:
1310            if dlg.ShowModal() == wx.ID_OK:
1311                imagefiles = dlg.GetPaths()
1312                imagefiles.sort()
1313                for imagefile in imagefiles:
1314                    # if a zip file, open and extract
1315                    if os.path.splitext(imagefile)[1].lower() == '.zip':
1316                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
1317                        if extractedfile is not None and extractedfile != imagefile:
1318                            imagefile = extractedfile
1319                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
1320                    if Comments:
1321                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
1322                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1323                        Imax = np.amax(Image)
1324                        Imin = max(0.0,np.amin(Image))          #force positive
1325                        if self.imageDefault:
1326                            Data = copy.copy(self.imageDefault)
1327                            Data['showLines'] = True
1328                            Data['ring'] = []
1329                            Data['rings'] = []
1330                            Data['cutoff'] = 10
1331                            Data['pixLimit'] = 20
1332                            Data['edgemin'] = 100000000
1333                            Data['calibdmin'] = 0.5
1334                            Data['calibskip'] = 0
1335                            Data['ellipses'] = []
1336                            Data['calibrant'] = ''
1337                            Data['GonioAngles'] = [0.,0.,0.]
1338                        else:
1339                            Data['type'] = 'PWDR'
1340                            Data['color'] = 'Paired'
1341                            Data['tilt'] = 0.0
1342                            Data['rotation'] = 0.0
1343                            Data['showLines'] = False
1344                            Data['ring'] = []
1345                            Data['rings'] = []
1346                            Data['cutoff'] = 10
1347                            Data['pixLimit'] = 20
1348                            Data['calibdmin'] = 0.5
1349                            Data['calibskip'] = 0
1350                            Data['edgemin'] = 100000000
1351                            Data['ellipses'] = []
1352                            Data['GonioAngles'] = [0.,0.,0.]
1353                            Data['calibrant'] = ''
1354                            Data['IOtth'] = [2.0,5.0]
1355                            Data['LRazimuth'] = [135,225]
1356                            Data['azmthOff'] = 0.0
1357                            Data['outChannels'] = 2500
1358                            Data['outAzimuths'] = 1
1359                            Data['centerAzm'] = False
1360                            Data['fullIntegrate'] = False
1361                            Data['setRings'] = False
1362                            Data['background image'] = ['',1.0]                           
1363                        Data['setDefault'] = False
1364                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1365                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
1366                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1367                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1368                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
1369                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'strain':np.zeros((3,3))})
1370                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
1371                        self.PickId = Id
1372                        self.Image = Id
1373                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1374                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
1375        finally:
1376            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
1377            os.chdir(path)
1378            dlg.Destroy()
1379
1380    def CheckNotebook(self):
1381        '''Make sure the data tree has the minimally expected controls.
1382        (BHT) correct?
1383        '''
1384        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
1385            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
1386            self.PatternTree.SetItemPyData(sub,[''])
1387        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
1388            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
1389            self.PatternTree.SetItemPyData(sub,{'deriv type':'analytic Hessian',    #default controls
1390                'min dM/M':0.0001,'shift factor':1.,'max cyc':3,'F**2':True,
1391                'minF/sig':0,})
1392        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
1393            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
1394            self.PatternTree.SetItemPyData(sub,{})
1395        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
1396            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
1397            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
1398        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
1399            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
1400            self.PatternTree.SetItemPyData(sub,{})
1401        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
1402            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
1403            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
1404                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
1405               
1406    class CopyDialog(wx.Dialog):
1407        '''Creates a dialog for copying control settings between
1408        data tree items'''
1409        def __init__(self,parent,title,text,data):
1410            wx.Dialog.__init__(self,parent,-1,title, 
1411                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1412            self.data = data
1413            panel = wx.Panel(self)
1414            mainSizer = wx.BoxSizer(wx.VERTICAL)
1415            topLabl = wx.StaticText(panel,-1,text)
1416            mainSizer.Add((10,10),1)
1417            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1418            mainSizer.Add((10,10),1)
1419            ncols = len(data)/40+1
1420            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=ncols,hgap=2,vgap=2)
1421            for id,item in enumerate(self.data):
1422                ckbox = wx.CheckBox(panel,id,item[1])
1423                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
1424                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
1425            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1426            OkBtn = wx.Button(panel,-1,"Ok")
1427            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1428            cancelBtn = wx.Button(panel,-1,"Cancel")
1429            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1430            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1431            btnSizer.Add((20,20),1)
1432            btnSizer.Add(OkBtn)
1433            btnSizer.Add((20,20),1)
1434            btnSizer.Add(cancelBtn)
1435            btnSizer.Add((20,20),1)
1436           
1437            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1438            panel.SetSizer(mainSizer)
1439            panel.Fit()
1440            self.Fit()
1441       
1442        def OnCopyChange(self,event):
1443            id = event.GetId()
1444            self.data[id][0] = self.FindWindowById(id).GetValue()       
1445           
1446        def OnOk(self,event):
1447            parent = self.GetParent()
1448            parent.Raise()
1449            self.EndModal(wx.ID_OK)             
1450           
1451        def OnCancel(self,event):
1452            parent = self.GetParent()
1453            parent.Raise()
1454            self.EndModal(wx.ID_CANCEL)             
1455           
1456        def GetData(self):
1457            return self.data
1458       
1459    class SumDialog(wx.Dialog):
1460        'Allows user to supply scale factor(s) when summing data'
1461        def __init__(self,parent,title,text,dataType,data):
1462            wx.Dialog.__init__(self,parent,-1,title, 
1463                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1464            self.data = data
1465            panel = wx.Panel(self)
1466            mainSizer = wx.BoxSizer(wx.VERTICAL)
1467            topLabl = wx.StaticText(panel,-1,text)
1468            mainSizer.Add((10,10),1)
1469            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1470            mainSizer.Add((10,10),1)
1471            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1472            for id,item in enumerate(self.data[:-1]):
1473                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
1474                name.SetEditable(False)
1475                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1476                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1477                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1478                dataGridSizer.Add(scale,0,wx.LEFT,10)
1479                dataGridSizer.Add(name,0,wx.RIGHT,10)
1480            if dataType:
1481                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
1482                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
1483                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
1484                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
1485                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
1486                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
1487            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1488            OkBtn = wx.Button(panel,-1,"Ok")
1489            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1490            cancelBtn = wx.Button(panel,-1,"Cancel")
1491            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1492            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1493            btnSizer.Add((20,20),1)
1494            btnSizer.Add(OkBtn)
1495            btnSizer.Add((20,20),1)
1496            btnSizer.Add(cancelBtn)
1497            btnSizer.Add((20,20),1)
1498           
1499            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1500            panel.SetSizer(mainSizer)
1501            panel.Fit()
1502            self.Fit()
1503
1504        def OnScaleChange(self,event):
1505            id = event.GetId()
1506            value = self.FindWindowById(id).GetValue()
1507            try:
1508                self.data[id][0] = float(value)
1509                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1510            except ValueError:
1511                if value and '-' not in value[0]:
1512                    print 'bad input - numbers only'
1513                    self.FindWindowById(id).SetValue('0.000')
1514           
1515        def OnNameChange(self,event):
1516            self.data[-1] = self.name.GetValue() 
1517           
1518        def OnOk(self,event):
1519            parent = self.GetParent()
1520            parent.Raise()
1521            self.EndModal(wx.ID_OK)             
1522           
1523        def OnCancel(self,event):
1524            parent = self.GetParent()
1525            parent.Raise()
1526            self.EndModal(wx.ID_CANCEL)             
1527           
1528        def GetData(self):
1529            return self.data
1530           
1531    class ConstraintDialog(wx.Dialog):
1532        '''Window to edit Constraint values
1533        '''
1534        def __init__(self,parent,title,text,data,separator='*'):
1535            wx.Dialog.__init__(self,parent,-1,title, 
1536                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1537            self.data = data
1538            panel = wx.Panel(self)
1539            mainSizer = wx.BoxSizer(wx.VERTICAL)
1540            topLabl = wx.StaticText(panel,-1,text)
1541            mainSizer.Add((10,10),1)
1542            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1543            mainSizer.Add((10,10),1)
1544            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
1545            for id,item in enumerate(self.data[:-1]):
1546                lbl = item[1]
1547                if lbl[-1] != '=': lbl += ' ' + separator + ' '
1548                name = wx.StaticText(panel,-1,lbl,size=wx.Size(200,20),
1549                                     style=wx.ALIGN_RIGHT)
1550                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
1551                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
1552                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
1553                dataGridSizer.Add(name,0,wx.LEFT,10)
1554                dataGridSizer.Add(scale,0,wx.RIGHT,10)
1555            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1556            OkBtn = wx.Button(panel,-1,"Ok")
1557            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1558            cancelBtn = wx.Button(panel,-1,"Cancel")
1559            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1560            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1561            btnSizer.Add((20,20),1)
1562            btnSizer.Add(OkBtn)
1563            btnSizer.Add((20,20),1)
1564            btnSizer.Add(cancelBtn)
1565            btnSizer.Add((20,20),1)
1566           
1567            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1568            panel.SetSizer(mainSizer)
1569            panel.Fit()
1570            self.Fit()
1571            self.CenterOnParent()
1572           
1573        def OnNameChange(self,event):
1574            self.data[-1] = self.name.GetValue() 
1575           
1576        def OnScaleChange(self,event):
1577            id = event.GetId()
1578            value = self.FindWindowById(id).GetValue()
1579            try:
1580                self.data[id][0] = float(value)
1581                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
1582            except ValueError:
1583                if value and '-' not in value[0]:
1584                    print 'bad input - numbers only'
1585                    self.FindWindowById(id).SetValue('0.000')
1586           
1587        def OnOk(self,event):
1588            parent = self.GetParent()
1589            parent.Raise()
1590            self.EndModal(wx.ID_OK)             
1591           
1592        def OnCancel(self,event):
1593            parent = self.GetParent()
1594            parent.Raise()
1595            self.EndModal(wx.ID_CANCEL)             
1596           
1597        def GetData(self):
1598            return self.data
1599           
1600    def OnPwdrSum(self,event):
1601        'Sum together powder data(?)'
1602        TextList = []
1603        DataList = []
1604        SumList = []
1605        Names = []
1606        Inst = None
1607        SumItemList = []
1608        Comments = ['Sum equals: \n']
1609        if self.PatternTree.GetCount():
1610            item, cookie = self.PatternTree.GetFirstChild(self.root)
1611            while item:
1612                name = self.PatternTree.GetItemText(item)
1613                Names.append(name)
1614                if 'PWDR' in name:
1615                    TextList.append([0.0,name])
1616                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
1617                    if not Inst:
1618                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
1619                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1620            if len(TextList) < 2:
1621                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
1622                return
1623            TextList.append('default_sum_name')               
1624            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
1625            try:
1626                if dlg.ShowModal() == wx.ID_OK:
1627                    lenX = 0
1628                    Xminmax = [0,0]
1629                    Xsum = []
1630                    Ysum = []
1631                    Vsum = []
1632                    result = dlg.GetData()
1633                    for i,item in enumerate(result[:-1]):
1634                        scale,name = item
1635                        data = DataList[i]
1636                        if scale:
1637                            Comments.append("%10.3f %s" % (scale,' * '+name))
1638                            x,y,w,yc,yb,yd = data   #numpy arrays!
1639                            v = 1./w
1640                            if lenX:
1641                                if lenX != len(x):
1642                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
1643                                        '\nExpected:'+str(lenX)+ \
1644                                        '\nFound:   '+str(len(x))+'\nfor '+name)
1645                                    return
1646                            else:
1647                                lenX = len(x)
1648                            if Xminmax[1]:
1649                                if Xminmax != [x[0],x[-1]]:
1650                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
1651                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
1652                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
1653                                    return
1654                                else:
1655                                    for j,yi in enumerate(y):
1656                                         Ysum[j] += scale*yi
1657                                         Vsum[j] += abs(scale)*v[j]
1658                            else:
1659                                Xminmax = [x[0],x[-1]]
1660                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
1661                                for j,yi in enumerate(y):
1662                                    Xsum.append(x[j])
1663                                    Ysum.append(scale*yi)
1664                                    Vsum.append(abs(scale*v[j]))
1665                    Wsum = 1./np.array(Vsum)
1666                    outname = 'PWDR '+result[-1]
1667                    Id = 0
1668                    if outname in Names:
1669                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1670                        try:
1671                            if dlg2.ShowModal() == wx.ID_OK:
1672                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1673                                self.PatternTree.Delete(Id)
1674                        finally:
1675                            dlg2.Destroy()
1676                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1677                    if Id:
1678                        Sample = G2pdG.SetDefaultSample()
1679                        self.PatternTree.SetItemPyData(Id,[{'wtFactor':1.0},[np.array(Xsum),np.array(Ysum),np.array(Wsum),
1680                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
1681                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
1682                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
1683                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
1684                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1685                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
1686                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
1687                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
1688                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
1689                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1690                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
1691                        self.PatternTree.SelectItem(Id)
1692                        self.PatternTree.Expand(Id)
1693                   
1694            finally:
1695                dlg.Destroy()
1696
1697    def OnImageSum(self,event):
1698        'Sum together image data(?)'
1699        TextList = []
1700        DataList = []
1701        SumList = []
1702        Names = []
1703        Inst = []
1704        SumItemList = []
1705        Comments = ['Sum equals: \n']
1706        if self.PatternTree.GetCount():
1707            item, cookie = self.PatternTree.GetFirstChild(self.root)
1708            while item:
1709                name = self.PatternTree.GetItemText(item)
1710                Names.append(name)
1711                if 'IMG' in name:
1712                    TextList.append([0.0,name])
1713                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
1714                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
1715                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1716            if len(TextList) < 2:
1717                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
1718                return
1719            TextList.append('default_sum_name')               
1720            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
1721            try:
1722                if dlg.ShowModal() == wx.ID_OK:
1723                    imSize = 0
1724                    result = dlg.GetData()
1725                    First = True
1726                    Found = False
1727                    for i,item in enumerate(result[:-1]):
1728                        scale,name = item
1729                        data = DataList[i]
1730                        if scale:
1731                            Found = True                               
1732                            Comments.append("%10.3f %s" % (scale,' * '+name))
1733                            Npix,imagefile = data
1734                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
1735                            if First:
1736                                newImage = np.zeros_like(image)
1737                                First = False
1738                            if imSize:
1739                                if imSize != Npix:
1740                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
1741                                        '\nExpected:'+str(imSize)+ \
1742                                        '\nFound:   '+str(Npix)+'\nfor '+name)
1743                                    return
1744                                newImage = newImage+scale*image
1745                            else:
1746                                imSize = Npix
1747                                newImage = newImage+scale*image
1748                            del(image)
1749                    if not Found:
1750                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
1751                        return
1752                       
1753                    newImage = np.asfarray(newImage,dtype=np.float32)                       
1754                    outname = 'IMG '+result[-1]
1755                    Id = 0
1756                    if outname in Names:
1757                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
1758                        try:
1759                            if dlg2.ShowModal() == wx.ID_OK:
1760                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
1761                        finally:
1762                            dlg2.Destroy()
1763                    else:
1764                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
1765                    if Id:
1766                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
1767                            'G2img files (*.G2img)|*.G2img', 
1768                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1769                        if dlg.ShowModal() == wx.ID_OK:
1770                            newimagefile = dlg.GetPath()
1771                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
1772                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
1773                            Imax = np.amax(newImage)
1774                            Imin = np.amin(newImage)
1775                            newImage = []
1776                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
1777                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1778                        del(newImage)
1779                        if self.imageDefault:
1780                            Data = copy.copy(self.imageDefault)
1781                        Data['showLines'] = True
1782                        Data['ring'] = []
1783                        Data['rings'] = []
1784                        Data['cutoff'] = 10
1785                        Data['pixLimit'] = 20
1786                        Data['ellipses'] = []
1787                        Data['calibrant'] = ''
1788                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1789                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
1790                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1791                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1792                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),{})
1793                        self.PatternTree.SelectItem(Id)
1794                        self.PatternTree.Expand(Id)
1795                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
1796                        self.Image = self.PickId
1797            finally:
1798                dlg.Destroy()
1799                     
1800    def OnAddPhase(self,event):
1801        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
1802        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
1803            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
1804        else:
1805            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1806        PhaseName = ''
1807        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
1808            style=wx.OK)
1809        if dlg.ShowModal() == wx.ID_OK:
1810            PhaseName = dlg.GetValue()
1811        dlg.Destroy()
1812        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
1813        E,SGData = G2spc.SpcGroup('P 1')
1814        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
1815       
1816    def OnDeletePhase(self,event):
1817        'Delete a phase from the tree. Called by Data/Delete Phase menu'
1818        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
1819        if self.dataFrame:
1820            self.dataFrame.Clear() 
1821        TextList = []
1822        DelList = []
1823        DelItemList = []
1824        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
1825            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1826        else:
1827            return
1828        if sub:
1829            item, cookie = self.PatternTree.GetFirstChild(sub)
1830            while item:
1831                TextList.append(self.PatternTree.GetItemText(item))
1832                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
1833            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
1834            try:
1835                if dlg.ShowModal() == wx.ID_OK:
1836                    result = dlg.GetSelections()
1837                    for i in result: DelList.append([i,TextList[i]])
1838                    item, cookie = self.PatternTree.GetFirstChild(sub)
1839                    i = 0
1840                    while item:
1841                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
1842                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1843                        i += 1
1844                    for item in DelItemList:
1845                        name = self.PatternTree.GetItemText(item)
1846                        self.PatternTree.Delete(item)
1847                        self.G2plotNB.Delete(name)
1848                    item, cookie = self.PatternTree.GetFirstChild(self.root)
1849                    while item:
1850                        name = self.PatternTree.GetItemText(item)
1851                        if 'PWDR' in name:
1852                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
1853                            refList = self.PatternTree.GetItemPyData(Id)
1854                            for i,item in DelList:
1855                                del(refList[item])
1856                            self.PatternTree.SetItemPyData(Id,refList)
1857                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1858            finally:
1859                dlg.Destroy()
1860               
1861    def OnRenameData(self,event):
1862        'Renames an existing phase. Called by Data/Rename Phase menu'
1863        name = self.PatternTree.GetItemText(self.PickId)     
1864        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
1865            dataType = name[:name.index(' ')+1]                 #includes the ' '
1866            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
1867                defaultValue=name[name.index(' ')+1:])
1868            try:
1869                if dlg.ShowModal() == wx.ID_OK:
1870                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
1871            finally:
1872                dlg.Destroy()
1873       
1874    def GetFileList(self,fileType,skip=None):        #potentially useful?
1875        'Appears unused. Note routine of same name in GSASIIpwdGUI'
1876        fileList = []
1877        Source = ''
1878        id, cookie = self.PatternTree.GetFirstChild(self.root)
1879        while id:
1880            name = self.PatternTree.GetItemText(id)
1881            if fileType in name:
1882                if id == skip:
1883                    Source = name
1884                else:
1885                    fileList.append([False,name,id])
1886            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1887        if skip:
1888            return fileList,Source
1889        else:
1890            return fileList
1891           
1892    def OnDataDelete(self, event):
1893        '''Delete one or more histograms from data tree. Called by the
1894        Data/DeleteData menu
1895        '''
1896        TextList = ['All Data']
1897        DelList = []
1898        DelItemList = []
1899        ifPWDR = False
1900        ifIMG = False
1901        ifHKLF = False
1902        ifPDF = False
1903        if self.PatternTree.GetCount():
1904            item, cookie = self.PatternTree.GetFirstChild(self.root)
1905            while item:
1906                name = self.PatternTree.GetItemText(item)
1907                if name not in ['Notebook','Controls','Covariance','Constraints','Restraints','Phases']:
1908                    if 'PWDR' in name: ifPWDR = True
1909                    if 'IMG' in name: ifIMG = True
1910                    if 'HKLF' in name: ifHKLF = True
1911                    if 'PDF' in name: ifPDF = True
1912                    TextList.append(name)
1913                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1914            if ifPWDR: TextList.insert(1,'All PWDR')
1915            if ifIMG: TextList.insert(1,'All IMG')
1916            if ifHKLF: TextList.insert(1,'All HKLF')
1917            if ifPDF: TextList.insert(1,'All PDF')               
1918            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
1919            try:
1920                if dlg.ShowModal() == wx.ID_OK:
1921                    result = dlg.GetSelections()
1922                    for i in result: DelList.append(TextList[i])
1923                    if 'All Data' in DelList:
1924                        DelList = [item for item in TextList if item[:3] != 'All']
1925                    elif 'All PWDR' in DelList:
1926                        DelList = [item for item in TextList if item[:4] == 'PWDR']
1927                    elif 'All IMG' in DelList:
1928                        DelList = [item for item in TextList if item[:3] == 'IMG']
1929                    elif 'All HKLF' in DelList:
1930                        DelList = [item for item in TextList if item[:4] == 'HKLF']
1931                    elif 'All PDF' in DelList:
1932                        DelList = [item for item in TextList if item[:3] == 'PDF']
1933                    item, cookie = self.PatternTree.GetFirstChild(self.root)
1934                    while item:
1935                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
1936                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1937                    for item in DelItemList:
1938                        self.PatternTree.Delete(item)
1939                    self.PickId = 0
1940                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
1941            finally:
1942                dlg.Destroy()
1943
1944    def OnFileOpen(self, event):
1945        '''Reads in a GSAS-II .gpx project file in response to the
1946        File/Open Project menu button
1947        '''
1948        result = ''
1949        Id = 0
1950        if self.PatternTree.GetChildrenCount(self.root,False):
1951            if self.dataFrame:
1952                self.dataFrame.Clear() 
1953            dlg = wx.MessageDialog(
1954                self,
1955                'Do you want to overwrite the current project? '
1956                'Any unsaved changes will be lost. Press OK to continue.',
1957                'Overwrite?',  wx.OK | wx.CANCEL)
1958            try:
1959                result = dlg.ShowModal()
1960                if result == wx.ID_OK:
1961                    self.PatternTree.DeleteChildren(self.root)
1962                    self.GSASprojectfile = ''
1963                    if self.HKL: self.HKL = []
1964                    if self.G2plotNB.plotList:
1965                        self.G2plotNB.clear()
1966            finally:
1967                dlg.Destroy()
1968        if result != wx.ID_CANCEL:   
1969            if self.dataDisplay: self.dataDisplay.Destroy()
1970            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
1971                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
1972            try:
1973                if dlg.ShowModal() == wx.ID_OK:
1974                    self.GSASprojectfile = dlg.GetPath()
1975                    self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
1976                    self.dirname = dlg.GetDirectory()
1977                    G2IO.ProjFileOpen(self)
1978                    self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
1979                    self.PatternTree.Expand(self.root)
1980                    self.HKL = []
1981                    item, cookie = self.PatternTree.GetFirstChild(self.root)
1982                    while item and not Id:
1983                        name = self.PatternTree.GetItemText(item)
1984                        if name[:4] in ['PWDR','HKLF','IMG ','PDF ']:
1985                            Id = item
1986                        elif name == 'Controls':
1987                            data = self.PatternTree.GetItemPyData(item)
1988                            if data:
1989                                for item in self.Refine: item.Enable(True)
1990                                for item in self.SeqRefine: item.Enable(True)
1991                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
1992                    if Id:
1993                        self.PatternTree.SelectItem(Id)
1994                    self.CheckNotebook()
1995                    os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1996            finally:
1997                dlg.Destroy()
1998
1999    def OnFileClose(self, event):
2000        '''Clears the data tree in response to the
2001        File/Close Project menu button. User is given option to save
2002        the project.
2003        '''
2004        if self.dataFrame:
2005            self.dataFrame.Clear()
2006            self.dataFrame.SetLabel('GSAS-II data display') 
2007        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2008        try:
2009            result = dlg.ShowModal()
2010            if result == wx.ID_OK:
2011                self.OnFileSaveMenu(event)
2012            if result != wx.ID_CANCEL:
2013                self.GSASprojectfile = ''
2014                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2015                self.PatternTree.DeleteChildren(self.root)
2016                if self.HKL: self.HKL = []
2017                if self.G2plotNB.plotList:
2018                    self.G2plotNB.clear()
2019        finally:
2020            dlg.Destroy()
2021
2022    def OnFileSave(self, event):
2023        '''Save the current project in response to the
2024        File/Save Project menu button
2025        '''
2026       
2027        if self.GSASprojectfile: 
2028            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2029            G2IO.ProjFileSave(self)
2030        else:
2031            self.OnFileSaveas(event)
2032
2033    def OnFileSaveas(self, event):
2034        '''Save the current project in response to the
2035        File/Save as menu button
2036        '''
2037        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2038            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2039        try:
2040            if dlg.ShowModal() == wx.ID_OK:
2041                self.GSASprojectfile = dlg.GetPath()
2042                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2043                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2044                self.SetTitle("GSAS-II data tree: "+
2045                              os.path.split(self.GSASprojectfile)[1])
2046                G2IO.ProjFileSave(self)
2047                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2048        finally:
2049            dlg.Destroy()
2050
2051    def ExitMain(self, event):
2052        '''Called if the main window is closed'''
2053        if self.undofile:
2054            os.remove(self.undofile)
2055        sys.exit()
2056       
2057    def OnFileExit(self, event):
2058        '''Called in response to the File/Quit menu button'''
2059        if self.dataFrame:
2060            self.dataFrame.Clear() 
2061            self.dataFrame.Destroy()
2062        self.Close()
2063       
2064    def OnExportPatterns(self,event):
2065        names = ['All']
2066        exports = []
2067        item, cookie = self.PatternTree.GetFirstChild(self.root)
2068        while item:
2069            name = self.PatternTree.GetItemText(item)
2070            if 'PWDR' in name:
2071                names.append(name)
2072            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2073        if names:
2074            dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2075            if dlg.ShowModal() == wx.ID_OK:
2076                sel = dlg.GetSelections()
2077                if sel[0] == 0:
2078                    exports = names[1:]
2079                else:
2080                    for x in sel:
2081                        exports.append(names[x])
2082            dlg.Destroy()
2083        if exports:
2084            dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '', 
2085                'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2086                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2087            try:
2088                if dlg.ShowModal() == wx.ID_OK:
2089                    powderfile = dlg.GetPath()
2090                    powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2091                    if 'fxye' in powderfile:
2092                        G2IO.powderFxyeSave(self,exports,powderfile)
2093                    else:       #just xye
2094                        G2IO.powderXyeSave(self,exports,powderfile)
2095            finally:
2096                dlg.Destroy()
2097       
2098    def OnExportPeakList(self,event):
2099        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2100            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2101        try:
2102            if dlg.ShowModal() == wx.ID_OK:
2103                self.peaklistfile = dlg.GetPath()
2104                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2105                file = open(self.peaklistfile,'w')               
2106                item, cookie = self.PatternTree.GetFirstChild(self.root)
2107                while item:
2108                    name = self.PatternTree.GetItemText(item)
2109                    if 'PWDR' in name:
2110                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2111                        while item2:
2112                            name2 = self.PatternTree.GetItemText(item2)
2113                            if name2 == 'Peak List':
2114                                peaks = self.PatternTree.GetItemPyData(item2)
2115                                file.write("%s \n" % (name+' Peak List'))               
2116                                for peak in peaks:
2117                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2118                                        (peak[0],peak[2],peak[4],peak[6]))
2119                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2120                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2121                file.close()
2122        finally:
2123            dlg.Destroy()
2124       
2125    def OnExportHKL(self,event):
2126        event.Skip()
2127       
2128    def OnExportPDF(self,event):
2129        #need S(Q) and G(R) to be saved here - probably best from selection?
2130        names = ['All']
2131        exports = []
2132        item, cookie = self.PatternTree.GetFirstChild(self.root)
2133        while item:
2134            name = self.PatternTree.GetItemText(item)
2135            if 'PDF' in name:
2136                names.append(name)
2137            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2138        if names:
2139            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2140            if dlg.ShowModal() == wx.ID_OK:
2141                sel = dlg.GetSelections()
2142                if sel[0] == 0:
2143                    exports = names[1:]
2144                else:
2145                    for x in sel:
2146                        exports.append(names[x])
2147            dlg.Destroy()
2148        if exports:
2149            G2IO.PDFSave(self,exports)
2150       
2151    def OnExportPhase(self,event):
2152        event.Skip()
2153       
2154    def OnExportCIF(self,event):
2155        event.Skip()
2156
2157    def OnMakePDFs(self,event):
2158        '''Calculates PDFs
2159        '''
2160        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2161        TextList = ['All PWDR']
2162        PDFlist = []
2163        Names = []
2164        if self.PatternTree.GetCount():
2165            id, cookie = self.PatternTree.GetFirstChild(self.root)
2166            while id:
2167                name = self.PatternTree.GetItemText(id)
2168                Names.append(name)
2169                if 'PWDR' in name:
2170                    TextList.append(name)
2171                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2172            if len(TextList) == 1:
2173                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2174                return
2175            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2176            try:
2177                if dlg.ShowModal() == wx.ID_OK:
2178                    result = dlg.GetSelections()
2179                    for i in result: PDFlist.append(TextList[i])
2180                    if 0 in result:
2181                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2182                    for item in PDFlist:
2183                        PWDRname = item[4:]
2184                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2185                        Data = {
2186                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2187                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2188                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2189                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2190                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2191                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2192                            'Lorch':True,}
2193                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2194                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2195                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2196                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2197                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2198                for item in self.ExportPDF: item.Enable(True)
2199            finally:
2200                dlg.Destroy()
2201               
2202    def GetPWDRdatafromTree(self,PWDRname):
2203        ''' Returns powder data from GSASII tree
2204
2205        :param str PWDRname: a powder histogram name as obtained from
2206          :mod:`GSASIIstruct.GetHistogramNames`
2207
2208        :returns: PWDRdata = powder data dictionary with
2209          Powder data arrays, Limits, Instrument Parameters,
2210          Sample Parameters           
2211        '''
2212        PWDRdata = {}
2213        try:
2214            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2215        except ValueError:
2216            PWDRdata['wtFactor'] = 1.0
2217        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2218        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2219        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2220        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2221        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2222        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2223        if 'ranId' not in PWDRdata['Sample Parameters']:
2224            PWDRdata['Sample Parameters']['ranId'] = ran.randint(0,sys.maxint)
2225        PWDRdata['ranId'] = PWDRdata['Sample Parameters']['ranId']
2226        return PWDRdata
2227
2228    def GetHKLFdatafromTree(self,HKLFname):
2229        ''' Returns single crystal data from GSASII tree
2230
2231        :param str HKLFname: a single crystal histogram name as obtained
2232          from
2233          :mod:`GSASIIstruct.GetHistogramNames`
2234
2235        :returns: HKLFdata = single crystal data list of reflections
2236
2237        '''
2238        HKLFdata = {}
2239        try:
2240            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2241        except ValueError:
2242            HKLFdata['wtFactor'] = 1.0
2243        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2244        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2245        return HKLFdata
2246       
2247    def GetPhaseData(self):
2248        '''Returns a list of defined phases. Used only in GSASIIgrid
2249        Note routine :mod:`GSASIIstruct.GetPhaseData` also exists.
2250        '''
2251        phaseData = {}
2252        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2253            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2254        else:
2255            print 'no phases to be refined'
2256            return
2257        if sub:
2258            item, cookie = self.PatternTree.GetFirstChild(sub)
2259            while item:
2260                phaseName = self.PatternTree.GetItemText(item)
2261                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2262                if 'ranId' not in phaseData[phaseName]:
2263                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2264                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2265        return phaseData               
2266                   
2267    def GetUsedHistogramsAndPhasesfromTree(self):
2268        ''' Returns all histograms that are found in any phase
2269        and any phase that uses a histogram
2270        :returns: two dicts:
2271
2272            * Histograms = dictionary of histograms as {name:data,...}
2273            * Phases = dictionary of phases that use histograms
2274        '''
2275        phaseData = self.GetPhaseData()
2276        if not phaseData:
2277            return {},{}
2278        Histograms = {}
2279        Phases = {}
2280        pId = 0
2281        hId = 0
2282        for phase in phaseData:
2283            Phase = phaseData[phase]
2284            if Phase['Histograms']:
2285                if phase not in Phases:
2286                    Phase['pId'] = pId
2287                    pId += 1
2288                    Phases[phase] = Phase
2289                for hist in Phase['Histograms']:
2290                    if hist not in Histograms:
2291                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
2292                        if 'PWDR' in hist[:4]: 
2293                            Histograms[hist] = self.GetPWDRdatafromTree(item)
2294                        elif 'HKLF' in hist[:4]:
2295                            Histograms[hist] = self.GetHKLFdatafromTree(item)
2296                        #future restraint, etc. histograms here           
2297                        Histograms[hist]['hId'] = hId
2298                        hId += 1
2299        return Histograms,Phases
2300       
2301    class ViewParmDialog(wx.Dialog):
2302        '''Window to show all parameters in the refinement.
2303        Called from :mod:`OnViewLSParms`
2304        '''
2305        def __init__(self,parent,title,parmDict):
2306            wx.Dialog.__init__(self,parent,-1,title,size=(300,430),
2307                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2308            panel = wx.Panel(self,size=(300,430))
2309            parmNames = parmDict.keys()
2310            parmNames.sort()
2311            parmText = ' p:h:Parameter       refine?              value\n'
2312            for name in parmNames:
2313                parmData = parmDict[name]
2314                try:
2315                    parmText += ' %s \t%12.4g \n'%(name.ljust(19)+'\t'+parmData[1],parmData[0])
2316                except TypeError:
2317                    pass
2318            parmTable = wx.TextCtrl(panel,-1,parmText,
2319                style=wx.TE_MULTILINE|wx.TE_READONLY,size=(290,400))
2320            mainSizer = wx.BoxSizer(wx.VERTICAL)
2321            mainSizer.Add(parmTable)
2322            panel.SetSizer(mainSizer)
2323                           
2324    def OnViewLSParms(self,event):
2325        '''Displays a window showing all parameters in the refinement.
2326        Called from the Calculate/View LS Parms menu.
2327        '''
2328        parmDict = {}
2329        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
2330        rigidbodyDict = self.PatternTree.GetItemPyData(   
2331            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
2332        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2333        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2334        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
2335        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
2336        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
2337        varyList = rbVary+phaseVary+hapVary+histVary
2338        parmDict.update(rbDict)
2339        parmDict.update(phaseDict)
2340        parmDict.update(hapDict)
2341        parmDict.update(histDict)
2342        for parm in parmDict:
2343            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
2344                'Omega','Chi','Phi','nDebye','nPeaks']:
2345                parmDict[parm] = [parmDict[parm],' ']
2346            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
2347                parmDict[parm] = [parmDict[parm],' ']
2348            elif parm in varyList:
2349                parmDict[parm] = [parmDict[parm],'True']
2350            else:
2351                parmDict[parm] = [parmDict[parm],'False']
2352        parmDict[' Num refined'] = [len(varyList),'']
2353        dlg = self.ViewParmDialog(self,'Parameters for least squares',parmDict)
2354        try:
2355            if dlg.ShowModal() == wx.ID_OK:
2356                print 'do something with changes?? - No!'
2357        finally:
2358            dlg.Destroy()
2359       
2360    def OnRefine(self,event):
2361        '''Perform a refinement.
2362        Called from the Calculate/Refine menu.
2363        '''       
2364        self.OnFileSave(event)
2365        # check that constraints are OK here
2366        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2367        if errmsg:
2368            print('Error in constraints:\n'+errmsg+
2369                  '\nRefinement not possible')
2370            self.ErrorDialog('Constraint Error',
2371                             'Error in constraints:\n'+errmsg+
2372                             '\nRefinement not possible')
2373            return
2374        if warnmsg:
2375            print('Conflict between refinment flag settings and constraints:\n'+
2376                  warnmsg+'\nRefinement not possible')
2377            self.ErrorDialog('Refinement Flag Error',
2378                             'Conflict between refinment flag settings and constraints:\n'+
2379                             warnmsg+
2380                             '\nRefinement not possible')
2381            return
2382        #works - but it'd be better if it could restore plots
2383        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
2384            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2385        screenSize = wx.ClientDisplayRect()
2386        Size = dlg.GetSize()
2387        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2388        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2389        dlg.SetSize(Size)
2390        Rw = 100.00
2391        try:
2392            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
2393        finally:
2394            dlg.Destroy()
2395        oldId =  self.PatternTree.GetSelection()
2396        oldName = self.PatternTree.GetItemText(oldId)
2397        parentId = self.PatternTree.GetItemParent(oldId)
2398        parentName = ''
2399        if parentId:
2400            parentName = self.PatternTree.GetItemText(parentId)
2401        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
2402        try:
2403            if dlg.ShowModal() == wx.ID_OK:
2404                Id = 0
2405                self.PatternTree.DeleteChildren(self.root)
2406                if self.HKL: self.HKL = []
2407                if self.G2plotNB.plotList:
2408                    self.G2plotNB.clear()
2409                G2IO.ProjFileOpen(self)
2410                item, cookie = self.PatternTree.GetFirstChild(self.root)
2411                while item and not Id:
2412                    name = self.PatternTree.GetItemText(item)
2413                    if name[:4] in ['PWDR','HKLF']:
2414                        Id = item
2415                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2416                if parentName:
2417                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
2418                    if parentId:
2419                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
2420                    else:
2421                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
2422                    self.PatternTree.SelectItem(itemId)
2423                elif Id:
2424                    self.PatternTree.SelectItem(Id)
2425        finally:
2426            dlg.Destroy()
2427
2428    def OnSeqRefine(self,event):
2429        '''Perform a sequential refinement.
2430        Called from the Calculate/Sequential refine menu.
2431        '''       
2432        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequental results')
2433        if not Id:
2434            Id = self.PatternTree.AppendItem(self.root,text='Sequental results')
2435            self.PatternTree.SetItemPyData(Id,{})           
2436        self.OnFileSave(event)
2437        # check that constraints are OK here
2438        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2439        if errmsg:
2440            print('Error in constraints:\n'+errmsg+
2441                  '\nRefinement not possible')
2442            self.ErrorDialog('Constraint Error',
2443                             'Error in constraints:\n'+errmsg+
2444                             '\nRefinement not possible')
2445            return
2446        if warnmsg:
2447            print('Conflict between refinment flag settings and constraints:\n'+
2448                  warnmsg+'\nRefinement not possible')
2449            self.ErrorDialog('Refinement Flag Error',
2450                             'Conflict between refinment flag settings and constraints:\n'+
2451                             warnmsg+'\nRefinement not possible')
2452            return
2453        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
2454            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
2455        screenSize = wx.ClientDisplayRect()
2456        Size = dlg.GetSize()
2457        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
2458        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
2459        dlg.SetSize(Size)
2460        try:
2461            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
2462        finally:
2463            dlg.Destroy()       
2464        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
2465        try:
2466            if dlg.ShowModal() == wx.ID_OK:
2467                Id = 0
2468                self.PatternTree.DeleteChildren(self.root)
2469                if self.HKL: self.HKL = []
2470                if self.G2plotNB.plotList:
2471                    self.G2plotNB.clear()
2472                G2IO.ProjFileOpen(self)
2473                item, cookie = self.PatternTree.GetFirstChild(self.root)
2474                while item and not Id:
2475                    name = self.PatternTree.GetItemText(item)
2476                    if name[:4] in ['PWDR','HKLF']:
2477                        Id = item
2478                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2479                if Id:
2480                    self.PatternTree.SelectItem(Id)
2481        finally:
2482            dlg.Destroy()
2483       
2484    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
2485        'Display an error message'
2486        result = None
2487        if parent is None:
2488            dlg = wx.MessageDialog(self, message, title,  wtype)
2489        else:
2490            dlg = wx.MessageDialog(parent, message, title,  wtype)
2491            dlg.CenterOnParent() # not working on Mac
2492        try:
2493            result = dlg.ShowModal()
2494        finally:
2495            dlg.Destroy()
2496        return result
2497
2498class GSASIImain(wx.App):
2499    '''Defines a wxApp for GSAS-II
2500
2501    Creates a wx frame (self.main) which contains the display of the
2502    data tree.
2503    '''
2504    def OnInit(self):
2505        '''Called automatically when the app is created.'''
2506        self.main = GSASII(None)
2507        self.main.Show()
2508        self.SetTopWindow(self.main)
2509        return True
2510
2511def main():
2512    '''Start up the GSAS-II application'''
2513    application = GSASIImain(0)
2514    if wxInspector: wxeye.InspectionTool().Show()
2515
2516    #application.main.OnRefine(None)
2517    application.MainLoop()
2518   
2519if __name__ == '__main__':
2520    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.