source: trunk/GSASII.py @ 1115

Last change on this file since 1115 was 1115, checked in by toby, 8 years ago

rework exports for new types

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