source: trunk/GSASII.py @ 998

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

make widths correct in Export HKLs from PWD data

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