source: trunk/GSASII.py @ 964

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