source: trunk/GSASII.py @ 1087

Last change on this file since 1087 was 1087, checked in by vondreele, 10 years ago

simulated CW powder data now complete with Poisson errors

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