source: trunk/GSASII.py @ 1191

Last change on this file since 1191 was 1191, checked in by vondreele, 8 years ago

CSV exporter for strain results

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