source: trunk/GSASII.py @ 982

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

missed file: new Mac app with drag & drop for open

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