source: trunk/GSASII.py @ 1123

Last change on this file since 1123 was 1121, checked in by vondreele, 12 years ago

reload binwin2.7 & binwin64-2.7 after fix to polymask.for
fix integration problem for polygon masks
now have 'frame' mask - outside of frame is excluded from integration

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