source: trunk/GSASII.py @ 1176

Last change on this file since 1176 was 1176, checked in by vondreele, 9 years ago

add a bit of Doc to surface roughness
remove deselect tree line - fails in Windows

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 146.1 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2013-12-19 17:20:27 +0000 (Thu, 19 Dec 2013) $
6# $Author: vondreele $
7# $Revision: 1176 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 1176 2013-12-19 17:20:27Z 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: 1176 $")
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, 'Images as',
1484            imagemenu, help='Export powder image(s)')
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        arg = sys.argv
1773        if len(arg) > 1:
1774            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
1775            self.dirname = os.path.dirname(arg[1])
1776            if self.dirname: os.chdir(self.dirname)
1777            try:
1778                G2IO.ProjFileOpen(self)
1779                self.PatternTree.Expand(self.root)
1780                for item in self.Refine: item.Enable(True)
1781                for item in self.SeqRefine: item.Enable(True)
1782            except:
1783                print 'Error opening file',arg[1]
1784
1785    def OnSize(self,event):
1786        'Called when the main window is resized. Not sure why'
1787        w,h = self.GetClientSizeTuple()
1788        self.mainPanel.SetSize(wx.Size(w,h))
1789        self.PatternTree.SetSize(wx.Size(w,h))
1790                       
1791    def OnPatternTreeSelChanged(self, event):
1792        '''Called when a data tree item is selected'''
1793        if self.TreeItemDelete:
1794            self.TreeItemDelete = False
1795        else:
1796            pltNum = self.G2plotNB.nb.GetSelection()
1797            if pltNum >= 0:                         #to avoid the startup with no plot!
1798                pltPage = self.G2plotNB.nb.GetPage(pltNum)
1799                pltPlot = pltPage.figure
1800            item = event.GetItem()
1801            G2gd.MovePatternTreeToGrid(self,item)
1802            if self.oldFocus:
1803                self.oldFocus.SetFocus()
1804       
1805    def OnPatternTreeItemCollapsed(self, event):
1806        'Called when a tree item is collapsed'
1807        event.Skip()
1808
1809    def OnPatternTreeItemExpanded(self, event):
1810        'Called when a tree item is expanded'
1811        event.Skip()
1812       
1813    def OnPatternTreeItemDelete(self, event):
1814        'Called when a tree item is deleted -- not sure what this does'
1815        self.TreeItemDelete = True
1816
1817    def OnPatternTreeItemActivated(self, event):
1818        'Called when a tree item is activated'
1819        event.Skip()
1820       
1821    def OnPatternTreeKeyDown(self,event):
1822        'Not sure what this does'
1823        key = event.GetKeyCode()
1824        item = self.PickId
1825        if type(item) is int: return # is this the toplevel in tree?
1826        if key == wx.WXK_UP:
1827            self.oldFocus = wx.Window.FindFocus()
1828            self.PatternTree.GetPrevSibling(item)
1829        elif key == wx.WXK_DOWN:
1830            self.oldFocus = wx.Window.FindFocus()
1831            self.PatternTree.GetNextSibling(item)
1832               
1833    def OnReadPowderPeaks(self,event):
1834        'Bound to menu Data/Read Powder Peaks -- still needed?'
1835        Cuka = 1.54052
1836        self.CheckNotebook()
1837        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
1838            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
1839        try:
1840            if dlg.ShowModal() == wx.ID_OK:
1841                self.HKL = []
1842                self.powderfile = dlg.GetPath()
1843                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
1844                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
1845                data = ['PKS',Cuka,0.0]
1846                names = ['Type','Lam','Zero'] 
1847                codes = [0,0,0]
1848                inst = [G2IO.makeInstDict(names,data,codes),{}]
1849                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
1850                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
1851                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),peaks)
1852                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
1853                self.PatternTree.Expand(Id)
1854                self.PatternTree.SelectItem(Id)
1855                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1856        finally:
1857            dlg.Destroy()
1858                       
1859    def OnImageRead(self,event):
1860        'Called to read in an image in any known format'
1861        self.CheckNotebook()
1862        dlg = wx.FileDialog(
1863            self, 'Choose image files', '.', '',
1864            'Any image file (*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img)|'
1865            '*.edf;*.tif;*.tiff;*.mar*;*.avg;*.sum;*.img;*.G2img;*.zip|'
1866            'European detector file (*.edf)|*.edf|'
1867            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
1868            'MAR file (*.mar*)|*.mar*|'
1869            'GE Image (*.avg;*.sum)|*.avg;*.sum|'
1870            'ADSC Image (*.img)|*.img|'
1871            'GSAS-II Image (*.G2img)|*.G2img|'
1872            'Zip archive (*.zip)|*.zip|'
1873            'All files (*.*)|*.*',
1874            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
1875        try:
1876            if dlg.ShowModal() == wx.ID_OK:
1877                imagefiles = dlg.GetPaths()
1878                imagefiles.sort()
1879                for imagefile in imagefiles:
1880                    # if a zip file, open and extract
1881                    if os.path.splitext(imagefile)[1].lower() == '.zip':
1882                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
1883                        if extractedfile is not None and extractedfile != imagefile:
1884                            imagefile = extractedfile
1885                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
1886                    if Comments:
1887                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
1888                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
1889                        Imax = np.amax(Image)
1890                        Imin = max(0.0,np.amin(Image))          #force positive
1891                        if self.imageDefault:
1892                            Data = copy.copy(self.imageDefault)
1893                            Data['showLines'] = True
1894                            Data['ring'] = []
1895                            Data['rings'] = []
1896                            Data['cutoff'] = 10
1897                            Data['pixLimit'] = 20
1898                            Data['edgemin'] = 100000000
1899                            Data['calibdmin'] = 0.5
1900                            Data['calibskip'] = 0
1901                            Data['ellipses'] = []
1902                            Data['calibrant'] = ''
1903                            Data['GonioAngles'] = [0.,0.,0.]
1904                            Data['DetDepth'] = 0.
1905                            Data['DetDepthRef'] = False
1906                        else:
1907                            Data['type'] = 'PWDR'
1908                            Data['color'] = 'Paired'
1909                            Data['tilt'] = 0.0
1910                            Data['rotation'] = 0.0
1911                            Data['showLines'] = False
1912                            Data['ring'] = []
1913                            Data['rings'] = []
1914                            Data['cutoff'] = 10
1915                            Data['pixLimit'] = 20
1916                            Data['calibdmin'] = 0.5
1917                            Data['calibskip'] = 0
1918                            Data['edgemin'] = 100000000
1919                            Data['ellipses'] = []
1920                            Data['GonioAngles'] = [0.,0.,0.]
1921                            Data['DetDepth'] = 0.
1922                            Data['DetDepthRef'] = False
1923                            Data['calibrant'] = ''
1924                            Data['IOtth'] = [2.0,5.0]
1925                            Data['LRazimuth'] = [135,225]
1926                            Data['azmthOff'] = 0.0
1927                            Data['outChannels'] = 2500
1928                            Data['outAzimuths'] = 1
1929                            Data['centerAzm'] = False
1930                            Data['fullIntegrate'] = False
1931                            Data['setRings'] = False
1932                            Data['background image'] = ['',1.0]                           
1933                        Data['setDefault'] = False
1934                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
1935                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
1936                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
1937                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
1938                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
1939                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'strain':np.zeros((3,3))})
1940                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
1941                        self.PickId = Id
1942                        self.Image = Id
1943                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
1944                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
1945        finally:
1946            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
1947            os.chdir(path)
1948            dlg.Destroy()
1949
1950    def CheckNotebook(self):
1951        '''Make sure the data tree has the minimally expected controls.
1952        (BHT) correct?
1953        '''
1954        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
1955            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
1956            self.PatternTree.SetItemPyData(sub,[''])
1957        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
1958            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
1959            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
1960        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
1961            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
1962            self.PatternTree.SetItemPyData(sub,{})
1963        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
1964            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
1965            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
1966        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
1967            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
1968            self.PatternTree.SetItemPyData(sub,{})
1969        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
1970            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
1971            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
1972                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
1973               
1974    class CopyDialog(wx.Dialog):
1975        '''Creates a dialog for copying control settings between
1976        data tree items'''
1977        def __init__(self,parent,title,text,data):
1978            wx.Dialog.__init__(self,parent,-1,title, 
1979                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1980            self.data = data
1981            panel = wx.Panel(self)
1982            mainSizer = wx.BoxSizer(wx.VERTICAL)
1983            topLabl = wx.StaticText(panel,-1,text)
1984            mainSizer.Add((10,10),1)
1985            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
1986            mainSizer.Add((10,10),1)
1987            ncols = len(data)/40+1
1988            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=ncols,hgap=2,vgap=2)
1989            for id,item in enumerate(self.data):
1990                ckbox = wx.CheckBox(panel,id,item[1])
1991                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
1992                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
1993            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
1994            OkBtn = wx.Button(panel,-1,"Ok")
1995            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1996            cancelBtn = wx.Button(panel,-1,"Cancel")
1997            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1998            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1999            btnSizer.Add((20,20),1)
2000            btnSizer.Add(OkBtn)
2001            btnSizer.Add((20,20),1)
2002            btnSizer.Add(cancelBtn)
2003            btnSizer.Add((20,20),1)
2004           
2005            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2006            panel.SetSizer(mainSizer)
2007            panel.Fit()
2008            self.Fit()
2009       
2010        def OnCopyChange(self,event):
2011            id = event.GetId()
2012            self.data[id][0] = self.FindWindowById(id).GetValue()       
2013           
2014        def OnOk(self,event):
2015            parent = self.GetParent()
2016            parent.Raise()
2017            self.EndModal(wx.ID_OK)             
2018           
2019        def OnCancel(self,event):
2020            parent = self.GetParent()
2021            parent.Raise()
2022            self.EndModal(wx.ID_CANCEL)             
2023           
2024        def GetData(self):
2025            return self.data
2026       
2027    class SumDialog(wx.Dialog):
2028        'Allows user to supply scale factor(s) when summing data'
2029        def __init__(self,parent,title,text,dataType,data):
2030            wx.Dialog.__init__(self,parent,-1,title, 
2031                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2032            self.data = data
2033            panel = wx.Panel(self)
2034            mainSizer = wx.BoxSizer(wx.VERTICAL)
2035            topLabl = wx.StaticText(panel,-1,text)
2036            mainSizer.Add((10,10),1)
2037            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2038            mainSizer.Add((10,10),1)
2039            dataGridSizer = wx.FlexGridSizer(rows=len(data),cols=2,hgap=2,vgap=2)
2040            for id,item in enumerate(self.data[:-1]):
2041                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2042                name.SetEditable(False)
2043                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2044                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2045                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2046                dataGridSizer.Add(scale,0,wx.LEFT,10)
2047                dataGridSizer.Add(name,0,wx.RIGHT,10)
2048            if dataType:
2049                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2050                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2051                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2052                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2053                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2054                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2055            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2056            OkBtn = wx.Button(panel,-1,"Ok")
2057            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2058            cancelBtn = wx.Button(panel,-1,"Cancel")
2059            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2060            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2061            btnSizer.Add((20,20),1)
2062            btnSizer.Add(OkBtn)
2063            btnSizer.Add((20,20),1)
2064            btnSizer.Add(cancelBtn)
2065            btnSizer.Add((20,20),1)
2066           
2067            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2068            panel.SetSizer(mainSizer)
2069            panel.Fit()
2070            self.Fit()
2071
2072        def OnScaleChange(self,event):
2073            id = event.GetId()
2074            value = self.FindWindowById(id).GetValue()
2075            try:
2076                self.data[id][0] = float(value)
2077                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2078            except ValueError:
2079                if value and '-' not in value[0]:
2080                    print 'bad input - numbers only'
2081                    self.FindWindowById(id).SetValue('0.000')
2082           
2083        def OnNameChange(self,event):
2084            self.data[-1] = self.name.GetValue() 
2085           
2086        def OnOk(self,event):
2087            parent = self.GetParent()
2088            parent.Raise()
2089            self.EndModal(wx.ID_OK)             
2090           
2091        def OnCancel(self,event):
2092            parent = self.GetParent()
2093            parent.Raise()
2094            self.EndModal(wx.ID_CANCEL)             
2095           
2096        def GetData(self):
2097            return self.data
2098                       
2099    def OnPwdrSum(self,event):
2100        'Sum together powder data(?)'
2101        TextList = []
2102        DataList = []
2103        SumList = []
2104        Names = []
2105        Inst = None
2106        SumItemList = []
2107        Comments = ['Sum equals: \n']
2108        if self.PatternTree.GetCount():
2109            item, cookie = self.PatternTree.GetFirstChild(self.root)
2110            while item:
2111                name = self.PatternTree.GetItemText(item)
2112                Names.append(name)
2113                if 'PWDR' in name:
2114                    TextList.append([0.0,name])
2115                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2116                    if not Inst:
2117                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2118                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2119            if len(TextList) < 2:
2120                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2121                return
2122            TextList.append('default_sum_name')               
2123            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2124            try:
2125                if dlg.ShowModal() == wx.ID_OK:
2126                    lenX = 0
2127                    Xminmax = [0,0]
2128                    Xsum = []
2129                    Ysum = []
2130                    Vsum = []
2131                    result = dlg.GetData()
2132                    for i,item in enumerate(result[:-1]):
2133                        scale,name = item
2134                        data = DataList[i]
2135                        if scale:
2136                            Comments.append("%10.3f %s" % (scale,' * '+name))
2137                            x,y,w,yc,yb,yd = data   #numpy arrays!
2138                            v = 1./w
2139                            if lenX:
2140                                if lenX != len(x):
2141                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2142                                        '\nExpected:'+str(lenX)+ \
2143                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2144                                    return
2145                            else:
2146                                lenX = len(x)
2147                            if Xminmax[1]:
2148                                if Xminmax != [x[0],x[-1]]:
2149                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2150                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2151                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2152                                    return
2153                                else:
2154                                    for j,yi in enumerate(y):
2155                                         Ysum[j] += scale*yi
2156                                         Vsum[j] += abs(scale)*v[j]
2157                            else:
2158                                Xminmax = [x[0],x[-1]]
2159                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2160                                for j,yi in enumerate(y):
2161                                    Xsum.append(x[j])
2162                                    Ysum.append(scale*yi)
2163                                    Vsum.append(abs(scale*v[j]))
2164                    Wsum = 1./np.array(Vsum)
2165                    outname = 'PWDR '+result[-1]
2166                    Id = 0
2167                    if outname in Names:
2168                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2169                        try:
2170                            if dlg2.ShowModal() == wx.ID_OK:
2171                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2172                                self.PatternTree.Delete(Id)
2173                        finally:
2174                            dlg2.Destroy()
2175                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2176                    if Id:
2177                        Sample = G2pdG.SetDefaultSample()
2178                        valuesdict = {
2179                            'wtFactor':1.0,
2180                            'Dummy':False,
2181                            'ranId':ran.randint(0,sys.maxint),
2182                            }
2183                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2184                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2185                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2186                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2187                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2188                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2189                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2190                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2191                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),[])
2192                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[])
2193                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2194                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2195                        self.PatternTree.SelectItem(Id)
2196                        self.PatternTree.Expand(Id)
2197            finally:
2198                dlg.Destroy()
2199
2200    def OnImageSum(self,event):
2201        'Sum together image data(?)'
2202        TextList = []
2203        DataList = []
2204        SumList = []
2205        Names = []
2206        Inst = []
2207        SumItemList = []
2208        Comments = ['Sum equals: \n']
2209        if self.PatternTree.GetCount():
2210            item, cookie = self.PatternTree.GetFirstChild(self.root)
2211            while item:
2212                name = self.PatternTree.GetItemText(item)
2213                Names.append(name)
2214                if 'IMG' in name:
2215                    TextList.append([0.0,name])
2216                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2217                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2218                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2219            if len(TextList) < 2:
2220                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2221                return
2222            TextList.append('default_sum_name')               
2223            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2224            try:
2225                if dlg.ShowModal() == wx.ID_OK:
2226                    imSize = 0
2227                    result = dlg.GetData()
2228                    First = True
2229                    Found = False
2230                    for i,item in enumerate(result[:-1]):
2231                        scale,name = item
2232                        data = DataList[i]
2233                        if scale:
2234                            Found = True                               
2235                            Comments.append("%10.3f %s" % (scale,' * '+name))
2236                            Npix,imagefile = data
2237                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2238                            if First:
2239                                newImage = np.zeros_like(image)
2240                                First = False
2241                            if imSize:
2242                                if imSize != Npix:
2243                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2244                                        '\nExpected:'+str(imSize)+ \
2245                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2246                                    return
2247                                newImage = newImage+scale*image
2248                            else:
2249                                imSize = Npix
2250                                newImage = newImage+scale*image
2251                            del(image)
2252                    if not Found:
2253                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2254                        return
2255                       
2256                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2257                    outname = 'IMG '+result[-1]
2258                    Id = 0
2259                    if outname in Names:
2260                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2261                        try:
2262                            if dlg2.ShowModal() == wx.ID_OK:
2263                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2264                        finally:
2265                            dlg2.Destroy()
2266                    else:
2267                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2268                    if Id:
2269                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2270                            'G2img files (*.G2img)|*.G2img', 
2271                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2272                        if dlg.ShowModal() == wx.ID_OK:
2273                            newimagefile = dlg.GetPath()
2274                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2275                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2276                            Imax = np.amax(newImage)
2277                            Imin = np.amin(newImage)
2278                            newImage = []
2279                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2280                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2281                        del(newImage)
2282                        if self.imageDefault:
2283                            Data = copy.copy(self.imageDefault)
2284                        Data['showLines'] = True
2285                        Data['ring'] = []
2286                        Data['rings'] = []
2287                        Data['cutoff'] = 10
2288                        Data['pixLimit'] = 20
2289                        Data['ellipses'] = []
2290                        Data['calibrant'] = ''
2291                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2292                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2293                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2294                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2295                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),{})
2296                        self.PatternTree.SelectItem(Id)
2297                        self.PatternTree.Expand(Id)
2298                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2299                        self.Image = self.PickId
2300            finally:
2301                dlg.Destroy()
2302                     
2303    def OnAddPhase(self,event):
2304        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2305        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2306            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2307        else:
2308            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2309        PhaseName = ''
2310        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2311            style=wx.OK)
2312        if dlg.ShowModal() == wx.ID_OK:
2313            PhaseName = dlg.GetValue()
2314        dlg.Destroy()
2315        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2316        E,SGData = G2spc.SpcGroup('P 1')
2317        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2318       
2319    def OnDeletePhase(self,event):
2320        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2321        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2322        if self.dataFrame:
2323            self.dataFrame.Clear() 
2324        TextList = []
2325        DelList = []
2326        DelItemList = []
2327        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2328            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2329        else:
2330            return
2331        if sub:
2332            item, cookie = self.PatternTree.GetFirstChild(sub)
2333            while item:
2334                TextList.append(self.PatternTree.GetItemText(item))
2335                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2336            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2337            try:
2338                if dlg.ShowModal() == wx.ID_OK:
2339                    result = dlg.GetSelections()
2340                    for i in result: DelList.append([i,TextList[i]])
2341                    item, cookie = self.PatternTree.GetFirstChild(sub)
2342                    i = 0
2343                    while item:
2344                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2345                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2346                        i += 1
2347                    for item in DelItemList:
2348                        name = self.PatternTree.GetItemText(item)
2349                        self.PatternTree.Delete(item)
2350                        self.G2plotNB.Delete(name)
2351                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2352                    while item:
2353                        name = self.PatternTree.GetItemText(item)
2354                        if 'PWDR' in name:
2355                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2356                            refList = self.PatternTree.GetItemPyData(Id)
2357                            for i,item in DelList:
2358                                del(refList[item])
2359                            self.PatternTree.SetItemPyData(Id,refList)
2360                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2361            finally:
2362                dlg.Destroy()
2363               
2364    def OnRenameData(self,event):
2365        'Renames an existing phase. Called by Data/Rename Phase menu'
2366        name = self.PatternTree.GetItemText(self.PickId)     
2367        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2368            dataType = name[:name.index(' ')+1]                 #includes the ' '
2369            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2370                defaultValue=name[name.index(' ')+1:])
2371            try:
2372                if dlg.ShowModal() == wx.ID_OK:
2373                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2374            finally:
2375                dlg.Destroy()
2376       
2377    def GetFileList(self,fileType,skip=None):        #potentially useful?
2378        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2379        fileList = []
2380        Source = ''
2381        id, cookie = self.PatternTree.GetFirstChild(self.root)
2382        while id:
2383            name = self.PatternTree.GetItemText(id)
2384            if fileType in name:
2385                if id == skip:
2386                    Source = name
2387                else:
2388                    fileList.append([False,name,id])
2389            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2390        if skip:
2391            return fileList,Source
2392        else:
2393            return fileList
2394           
2395    def OnDataDelete(self, event):
2396        '''Delete one or more histograms from data tree. Called by the
2397        Data/DeleteData menu
2398        '''
2399        TextList = ['All Data']
2400        DelList = []
2401        DelItemList = []
2402        ifPWDR = False
2403        ifIMG = False
2404        ifHKLF = False
2405        ifPDF = False
2406        if self.PatternTree.GetCount():
2407            item, cookie = self.PatternTree.GetFirstChild(self.root)
2408            while item:
2409                name = self.PatternTree.GetItemText(item)
2410                if name not in ['Notebook','Controls','Covariance','Constraints',
2411                    'Restraints','Phases','Rigid bodies']:
2412                    if 'PWDR' in name: ifPWDR = True
2413                    if 'IMG' in name: ifIMG = True
2414                    if 'HKLF' in name: ifHKLF = True
2415                    if 'PDF' in name: ifPDF = True
2416                    TextList.append(name)
2417                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2418            if ifPWDR: TextList.insert(1,'All PWDR')
2419            if ifIMG: TextList.insert(1,'All IMG')
2420            if ifHKLF: TextList.insert(1,'All HKLF')
2421            if ifPDF: TextList.insert(1,'All PDF')               
2422            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2423            try:
2424                if dlg.ShowModal() == wx.ID_OK:
2425                    result = dlg.GetSelections()
2426                    for i in result: DelList.append(TextList[i])
2427                    if 'All Data' in DelList:
2428                        DelList = [item for item in TextList if item[:3] != 'All']
2429                    elif 'All PWDR' in DelList:
2430                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2431                    elif 'All IMG' in DelList:
2432                        DelList = [item for item in TextList if item[:3] == 'IMG']
2433                    elif 'All HKLF' in DelList:
2434                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2435                    elif 'All PDF' in DelList:
2436                        DelList = [item for item in TextList if item[:3] == 'PDF']
2437                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2438                    while item:
2439                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2440                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2441                    for item in DelItemList:
2442                        self.PatternTree.Delete(item)
2443                    self.PickId = 0
2444                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2445            finally:
2446                dlg.Destroy()
2447
2448    def OnFileOpen(self, event, filename=None):
2449        '''Reads in a GSAS-II .gpx project file in response to the
2450        File/Open Project menu button
2451        '''
2452        result = wx.ID_OK
2453        Id = 0
2454        if self.PatternTree.GetChildrenCount(self.root,False):
2455            if self.dataFrame:
2456                self.dataFrame.Clear() 
2457            dlg = wx.MessageDialog(
2458                self,
2459                'Do you want to overwrite the current project? '
2460                'Any unsaved changes will be lost. Press OK to continue.',
2461                'Overwrite?',  wx.OK | wx.CANCEL)
2462            try:
2463                result = dlg.ShowModal()
2464                if result == wx.ID_OK:
2465                    self.PatternTree.DeleteChildren(self.root)
2466                    self.GSASprojectfile = ''
2467                    if self.HKL: self.HKL = []
2468                    if self.G2plotNB.plotList:
2469                        self.G2plotNB.clear()
2470            finally:
2471                dlg.Destroy()
2472        if result != wx.ID_OK: return
2473
2474        if not filename:
2475            if self.dataDisplay: self.dataDisplay.Destroy()
2476            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2477                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2478            try:
2479                if dlg.ShowModal() != wx.ID_OK: return
2480                self.GSASprojectfile = dlg.GetPath()
2481                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2482                self.dirname = dlg.GetDirectory()
2483            finally:
2484                dlg.Destroy()
2485        else:
2486            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2487            self.dirname = os.path.split(filename)[0]
2488
2489        G2IO.ProjFileOpen(self)
2490        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2491        self.PatternTree.Expand(self.root)
2492        self.HKL = []
2493        item, cookie = self.PatternTree.GetFirstChild(self.root)
2494        while item and not Id:
2495            name = self.PatternTree.GetItemText(item)
2496            if name[:4] in ['PWDR','HKLF','IMG ','PDF ']:
2497                Id = item
2498            elif name == 'Controls':
2499                data = self.PatternTree.GetItemPyData(item)
2500                if data:
2501                    for item in self.Refine: item.Enable(True)
2502                    for item in self.SeqRefine: item.Enable(True)
2503            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2504        if Id:
2505            self.PatternTree.SelectItem(Id)
2506        self.CheckNotebook()
2507        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2508
2509    def OnFileClose(self, event):
2510        '''Clears the data tree in response to the
2511        File/New Project menu button. User is given option to save
2512        the project.
2513        '''
2514        if self.dataFrame:
2515            self.dataFrame.Clear()
2516            self.dataFrame.SetLabel('GSAS-II data display') 
2517        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2518        try:
2519            result = dlg.ShowModal()
2520            if result == wx.ID_OK:
2521                self.OnFileSaveMenu(event)
2522            if result != wx.ID_CANCEL:
2523                self.GSASprojectfile = ''
2524                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2525                self.PatternTree.DeleteChildren(self.root)
2526                if self.HKL: self.HKL = []
2527                if self.G2plotNB.plotList:
2528                    self.G2plotNB.clear()
2529        finally:
2530            dlg.Destroy()
2531
2532    def OnFileSave(self, event):
2533        '''Save the current project in response to the
2534        File/Save Project menu button
2535        '''
2536       
2537        if self.GSASprojectfile: 
2538            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2539            G2IO.ProjFileSave(self)
2540        else:
2541            self.OnFileSaveas(event)
2542
2543    def OnFileSaveas(self, event):
2544        '''Save the current project in response to the
2545        File/Save as menu button
2546        '''
2547        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2548            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2549        try:
2550            if dlg.ShowModal() == wx.ID_OK:
2551                self.GSASprojectfile = dlg.GetPath()
2552                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2553                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2554                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2555                G2IO.ProjFileSave(self)
2556                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2557        finally:
2558            dlg.Destroy()
2559
2560    def ExitMain(self, event):
2561        '''Called if the main window is closed'''
2562        if self.undofile:
2563            os.remove(self.undofile)
2564        sys.exit()
2565       
2566    def OnFileExit(self, event):
2567        '''Called in response to the File/Quit menu button'''
2568        if self.dataFrame:
2569            self.dataFrame.Clear() 
2570            self.dataFrame.Destroy()
2571        self.Close()
2572       
2573    def OnExportPatterns(self,event):
2574        names = ['All']
2575        exports = []
2576        item, cookie = self.PatternTree.GetFirstChild(self.root)
2577        while item:
2578            name = self.PatternTree.GetItemText(item)
2579            if 'PWDR' in name:
2580                names.append(name)
2581            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2582        if names:
2583            dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2584            if dlg.ShowModal() == wx.ID_OK:
2585                sel = dlg.GetSelections()
2586                if sel[0] == 0:
2587                    exports = names[1:]
2588                else:
2589                    for x in sel:
2590                        exports.append(names[x])
2591            dlg.Destroy()
2592        if exports:
2593            dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '', 
2594                'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2595                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2596            try:
2597                if dlg.ShowModal() == wx.ID_OK:
2598                    powderfile = dlg.GetPath()
2599                    powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2600                    if 'fxye' in powderfile:
2601                        G2IO.powderFxyeSave(self,exports,powderfile)
2602                    else:       #just xye
2603                        G2IO.powderXyeSave(self,exports,powderfile)
2604            finally:
2605                dlg.Destroy()
2606       
2607    def OnExportPeakList(self,event):
2608        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2609            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2610        try:
2611            if dlg.ShowModal() == wx.ID_OK:
2612                self.peaklistfile = dlg.GetPath()
2613                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2614                file = open(self.peaklistfile,'w')               
2615                item, cookie = self.PatternTree.GetFirstChild(self.root)
2616                while item:
2617                    name = self.PatternTree.GetItemText(item)
2618                    if 'PWDR' in name:
2619                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2620                        while item2:
2621                            name2 = self.PatternTree.GetItemText(item2)
2622                            if name2 == 'Peak List':
2623                                peaks = self.PatternTree.GetItemPyData(item2)
2624                                file.write("%s \n" % (name+' Peak List'))               
2625                                for peak in peaks:
2626                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2627                                        (peak[0],peak[2],peak[4],peak[6]))
2628                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2629                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2630                file.close()
2631        finally:
2632            dlg.Destroy()
2633       
2634    def OnExportHKL(self,event):
2635        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2636            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2637        try:
2638            if dlg.ShowModal() == wx.ID_OK:
2639                self.peaklistfile = dlg.GetPath()
2640                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2641                file = open(self.peaklistfile,'w')               
2642                item, cookie = self.PatternTree.GetFirstChild(self.root)
2643                while item:
2644                    name = self.PatternTree.GetItemText(item)
2645                    if 'PWDR' in name:
2646                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2647                        while item2:
2648                            name2 = self.PatternTree.GetItemText(item2)
2649                            if name2 == 'Reflection Lists':
2650                                data = self.PatternTree.GetItemPyData(item2)
2651                                phases = data.keys()
2652                                for phase in phases:
2653                                    peaks = data[phase]
2654                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2655                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2656                                    for peak in peaks:
2657                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2658                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2659                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2660                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2661                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2662                file.close()
2663        finally:
2664            dlg.Destroy()
2665       
2666    def OnExportPDF(self,event):
2667        #need S(Q) and G(R) to be saved here - probably best from selection?
2668        names = ['All']
2669        exports = []
2670        item, cookie = self.PatternTree.GetFirstChild(self.root)
2671        while item:
2672            name = self.PatternTree.GetItemText(item)
2673            if 'PDF' in name:
2674                names.append(name)
2675            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2676        if names:
2677            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2678            if dlg.ShowModal() == wx.ID_OK:
2679                sel = dlg.GetSelections()
2680                if sel[0] == 0:
2681                    exports = names[1:]
2682                else:
2683                    for x in sel:
2684                        exports.append(names[x])
2685            dlg.Destroy()
2686        if exports:
2687            G2IO.PDFSave(self,exports)
2688       
2689    def OnMakePDFs(self,event):
2690        '''Calculates PDFs
2691        '''
2692        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2693        TextList = ['All PWDR']
2694        PDFlist = []
2695        Names = []
2696        if self.PatternTree.GetCount():
2697            id, cookie = self.PatternTree.GetFirstChild(self.root)
2698            while id:
2699                name = self.PatternTree.GetItemText(id)
2700                Names.append(name)
2701                if 'PWDR' in name:
2702                    TextList.append(name)
2703                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2704            if len(TextList) == 1:
2705                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2706                return
2707            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2708            try:
2709                if dlg.ShowModal() == wx.ID_OK:
2710                    result = dlg.GetSelections()
2711                    for i in result: PDFlist.append(TextList[i])
2712                    if 0 in result:
2713                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
2714                    for item in PDFlist:
2715                        PWDRname = item[4:]
2716                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
2717                        Data = {
2718                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
2719                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
2720                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
2721                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
2722                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
2723                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
2724                            'Lorch':True,}
2725                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
2726                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
2727                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
2728                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
2729                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
2730                for item in self.ExportPDF: item.Enable(True)
2731            finally:
2732                dlg.Destroy()
2733               
2734    def GetPWDRdatafromTree(self,PWDRname):
2735        ''' Returns powder data from GSASII tree
2736
2737        :param str PWDRname: a powder histogram name as obtained from
2738          :meth:`GSASIIstruct.GetHistogramNames`
2739
2740        :returns: PWDRdata = powder data dictionary with
2741          Powder data arrays, Limits, Instrument Parameters,
2742          Sample Parameters           
2743        '''
2744        PWDRdata = {}
2745        try:
2746            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
2747        except ValueError:
2748            PWDRdata['wtFactor'] = 1.0
2749        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
2750        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
2751        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
2752        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
2753        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
2754        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
2755        if 'ranId' not in PWDRdata:  # patch, add a random Id
2756            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
2757        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
2758            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
2759        return PWDRdata
2760
2761    def GetHKLFdatafromTree(self,HKLFname):
2762        ''' Returns single crystal data from GSASII tree
2763
2764        :param str HKLFname: a single crystal histogram name as obtained
2765          from
2766          :meth:`GSASIIstruct.GetHistogramNames`
2767
2768        :returns: HKLFdata = single crystal data list of reflections
2769
2770        '''
2771        HKLFdata = {}
2772        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2773#        try:
2774#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
2775#        except ValueError:
2776#            HKLFdata['wtFactor'] = 1.0
2777        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
2778        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
2779        return HKLFdata
2780       
2781    def GetPhaseData(self):
2782        '''Returns a dict with defined phases.
2783        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
2784        get same info from GPX file.
2785        '''
2786        phaseData = {}
2787        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2788            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2789        else:
2790            print 'no phases found in GetPhaseData'
2791            sub = None
2792        if sub:
2793            item, cookie = self.PatternTree.GetFirstChild(sub)
2794            while item:
2795                phaseName = self.PatternTree.GetItemText(item)
2796                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
2797                if 'ranId' not in phaseData[phaseName]:
2798                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
2799                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2800        return phaseData
2801
2802    def GetPhaseInfofromTree(self):
2803        '''Get the phase names and their rId values,
2804        also the histograms used in each phase.
2805
2806        :returns: (phaseRIdList, usedHistograms) where
2807
2808          *
2809          ,phaseRIdList,usedHistograms
2810        '''
2811        phaseRIdList = []
2812        usedHistograms = {}
2813        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2814        if sub:
2815            item, cookie = self.PatternTree.GetFirstChild(sub)
2816            while item:
2817                phaseName = self.PatternTree.GetItemText(item)
2818                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2819                if ranId: phaseRIdList.append(ranId)
2820                data = self.PatternTree.GetItemPyData(item)
2821                UseList = data['Histograms']
2822                usedHistograms[phaseName] = UseList.keys()
2823                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2824        return phaseRIdList,usedHistograms
2825
2826    def GetPhaseNames(self):
2827        '''Returns a list of defined phases.
2828        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
2829        get same info from GPX file.
2830        '''
2831        phaseNames = []
2832        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2833            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2834        else:
2835            print 'no phases found in GetPhaseNames'
2836            sub = None
2837        if sub:
2838            item, cookie = self.PatternTree.GetFirstChild(sub)
2839            while item:
2840                phase = self.PatternTree.GetItemText(item)
2841                phaseNames.append(phase)
2842                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2843        return phaseNames
2844   
2845    def GetHistogramNames(self,hType):
2846        """ Returns a list of histogram names found in the GSASII data tree
2847        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
2848        get same info from GPX file.
2849       
2850        :param str hType: list of histogram types
2851        :return: list of histogram names
2852       
2853        """
2854        HistogramNames = []
2855        if self.PatternTree.GetCount():
2856            item, cookie = self.PatternTree.GetFirstChild(self.root)
2857            while item:
2858                name = self.PatternTree.GetItemText(item)
2859                if name[:4] in hType:
2860                    HistogramNames.append(name)       
2861                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
2862
2863        return HistogramNames
2864
2865                   
2866    def GetUsedHistogramsAndPhasesfromTree(self):
2867        ''' Returns all histograms that are found in any phase
2868        and any phase that uses a histogram.
2869        This also assigns numbers to used phases and histograms by the
2870        order they appear in the file.
2871        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
2872        get same info from GPX file.
2873
2874        :returns: (Histograms,Phases)
2875
2876            * Histograms = dictionary of histograms as {name:data,...}
2877            * Phases = dictionary of phases that use histograms
2878        '''
2879        Histograms = {}
2880        Phases = {}
2881        phaseNames = self.GetPhaseNames()
2882        phaseData = self.GetPhaseData()
2883        histoList = self.GetHistogramNames(['PWDR','HKLF'])
2884
2885        for phase in phaseData:
2886            Phase = phaseData[phase]
2887            pId = phaseNames.index(phase)
2888            Phase['pId'] = pId
2889            if Phase['Histograms']:
2890                if phase not in Phases:
2891                    Phases[phase] = Phase
2892                for hist in Phase['Histograms']:
2893                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
2894                        Phase['Histograms'][hist]['Use'] = True         
2895                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
2896                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
2897                        if item:
2898                            if 'PWDR' in hist[:4]: 
2899                                Histograms[hist] = self.GetPWDRdatafromTree(item)
2900                            elif 'HKLF' in hist[:4]:
2901                                Histograms[hist] = self.GetHKLFdatafromTree(item)
2902                            hId = histoList.index(hist)
2903                            Histograms[hist]['hId'] = hId
2904                        else: # would happen if a referenced histogram were renamed or deleted
2905                            print('For phase "'+str(phase)+
2906                                  '" unresolved reference to histogram "'+str(hist)+'"')
2907        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
2908        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
2909        return Histograms,Phases
2910       
2911    def MakeLSParmDict(self):
2912        '''Load all parameters used for computation from the tree into a
2913        dict of paired values [value, refine flag]. Note that this is
2914        different than the parmDict used in the refinement, which only has
2915        values.
2916
2917        :returns: (parmDict,varyList) where:
2918
2919         * parmDict is a dict with values and refinement flags
2920           for each parameter and
2921         * varyList is a list of variables (refined parameters).
2922        '''
2923        parmDict = {}
2924        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
2925        for phase in Phases:
2926            if 'pId' not in Phases[phase]:
2927                self.ErrorDialog('View parameter error','You must run least squares at least once')
2928                raise Exception,'No pId for phase '+str(phase)
2929        rigidbodyDict = self.PatternTree.GetItemPyData(   
2930            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
2931        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2932        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2933        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
2934        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
2935        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
2936        varyList = rbVary+phaseVary+hapVary+histVary
2937        parmDict.update(rbDict)
2938        parmDict.update(phaseDict)
2939        parmDict.update(hapDict)
2940        parmDict.update(histDict)
2941        for parm in parmDict:
2942            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
2943                'Omega','Chi','Phi','nDebye','nPeaks']:
2944                parmDict[parm] = [parmDict[parm],'-']
2945            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
2946                parmDict[parm] = [parmDict[parm],'-']
2947            elif parm in varyList:
2948                parmDict[parm] = [parmDict[parm],'T']
2949            else:
2950                parmDict[parm] = [parmDict[parm],'F']
2951        return parmDict,varyList
2952
2953    def ShowLSParms(self,event):
2954        '''Displays a window showing all parameters in the refinement.
2955        Called from the Calculate/View LS Parms menu.
2956        '''
2957        parmDict,varyList = self.MakeLSParmDict()
2958        parmValDict = {}
2959        for i in parmDict:
2960            parmValDict[i] = parmDict[i][0]
2961           
2962        reqVaryList = tuple(varyList) # save requested variables
2963        try:
2964            # process constraints
2965            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
2966            Constraints = self.PatternTree.GetItemPyData(sub)
2967            constList = []
2968            for item in Constraints:
2969                if item.startswith('_'): continue
2970                constList += Constraints[item]
2971            G2mv.InitVars()
2972            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2973            groups,parmlist = G2mv.GroupConstraints(constrDict)
2974            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList)
2975            G2mv.Map2Dict(parmValDict,varyList)
2976        except:
2977            pass
2978        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
2979        dlg.ShowModal()
2980        dlg.Destroy()
2981       
2982    def OnRefine(self,event):
2983        '''Perform a refinement.
2984        Called from the Calculate/Refine menu.
2985        '''       
2986        self.OnFileSave(event)
2987        # check that constraints are OK here
2988        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
2989        if errmsg:
2990            print('Error in constraints:\n'+errmsg+
2991                  '\nRefinement not possible')
2992            self.ErrorDialog('Constraint Error',
2993                             'Error in constraints:\n'+errmsg+
2994                             '\nRefinement not possible')
2995            return
2996        if warnmsg:
2997            print('Conflict between refinment flag settings and constraints:\n'+
2998                  warnmsg+'\nRefinement not possible')
2999            self.ErrorDialog('Refinement Flag Error',
3000                             'Conflict between refinment flag settings and constraints:\n'+
3001                             warnmsg+
3002                             '\nRefinement not possible')
3003            return
3004        #works - but it'd be better if it could restore plots
3005        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3006            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3007        screenSize = wx.ClientDisplayRect()
3008        Size = dlg.GetSize()
3009        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3010        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3011        dlg.SetSize(Size)
3012        Rw = 100.00
3013        try:
3014            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3015        finally:
3016            dlg.Destroy()
3017        oldId =  self.PatternTree.GetSelection()
3018        oldName = self.PatternTree.GetItemText(oldId)
3019        parentId = self.PatternTree.GetItemParent(oldId)
3020        parentName = ''
3021        if parentId:
3022            parentName = self.PatternTree.GetItemText(parentId)
3023        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3024        try:
3025            if dlg.ShowModal() == wx.ID_OK:
3026                Id = 0
3027                self.PatternTree.DeleteChildren(self.root)
3028                if self.HKL: self.HKL = []
3029                if self.G2plotNB.plotList:
3030                    self.G2plotNB.clear()
3031                G2IO.ProjFileOpen(self)
3032                item, cookie = self.PatternTree.GetFirstChild(self.root)
3033                while item and not Id:
3034                    name = self.PatternTree.GetItemText(item)
3035                    if name[:4] in ['PWDR','HKLF']:
3036                        Id = item
3037                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3038                if parentName:
3039                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3040                    if parentId:
3041                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3042                    else:
3043                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3044                    self.PatternTree.SelectItem(itemId)
3045                elif Id:
3046                    self.PatternTree.SelectItem(Id)
3047        finally:
3048            dlg.Destroy()
3049
3050    def OnSeqRefine(self,event):
3051        '''Perform a sequential refinement.
3052        Called from the Calculate/Sequential refine menu.
3053        '''       
3054        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3055        if not Id:
3056            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3057            self.PatternTree.SetItemPyData(Id,{})           
3058        self.OnFileSave(event)
3059        # check that constraints are OK here
3060        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3061        if errmsg:
3062            print('Error in constraints:\n'+errmsg+
3063                  '\nRefinement not possible')
3064            self.ErrorDialog('Constraint Error',
3065                             'Error in constraints:\n'+errmsg+
3066                             '\nRefinement not possible')
3067            return
3068        if warnmsg:
3069            print('Conflict between refinment flag settings and constraints:\n'+
3070                  warnmsg+'\nRefinement not possible')
3071            self.ErrorDialog('Refinement Flag Error',
3072                             'Conflict between refinment flag settings and constraints:\n'+
3073                             warnmsg+'\nRefinement not possible')
3074            return
3075        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3076            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3077        screenSize = wx.ClientDisplayRect()
3078        Size = dlg.GetSize()
3079        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3080        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3081        dlg.SetSize(Size)
3082        try:
3083            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3084        finally:
3085            dlg.Destroy()       
3086        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3087        try:
3088            if dlg.ShowModal() == wx.ID_OK:
3089                Id = 0
3090                self.PatternTree.DeleteChildren(self.root)
3091                if self.HKL: self.HKL = []
3092                if self.G2plotNB.plotList:
3093                    self.G2plotNB.clear()
3094                G2IO.ProjFileOpen(self)
3095                item, cookie = self.PatternTree.GetFirstChild(self.root)
3096                while item and not Id:
3097                    name = self.PatternTree.GetItemText(item)
3098                    if name[:4] in ['PWDR','HKLF']:
3099                        Id = item
3100                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3101                if Id:
3102                    self.PatternTree.SelectItem(Id)
3103        finally:
3104            dlg.Destroy()
3105       
3106    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3107        'Display an error message'
3108        result = None
3109        if parent is None:
3110            dlg = wx.MessageDialog(self, message, title,  wtype)
3111        else:
3112            dlg = wx.MessageDialog(parent, message, title,  wtype)
3113            dlg.CenterOnParent() # not working on Mac
3114        try:
3115            result = dlg.ShowModal()
3116        finally:
3117            dlg.Destroy()
3118        return result
3119
3120class GSASIImain(wx.App):
3121    '''Defines a wxApp for GSAS-II
3122
3123    Creates a wx frame (self.main) which contains the display of the
3124    data tree.
3125    '''
3126    def OnInit(self):
3127        '''Called automatically when the app is created.'''
3128        self.main = GSASII(None)
3129        self.main.Show()
3130        self.SetTopWindow(self.main)
3131        return True
3132    def MacOpenFile(self, filename):
3133        '''Called on Mac every time a file is dropped on the app when it is running,
3134        treat this like a File/Open project menu action.
3135        Should be ignored on other platforms
3136        '''
3137        self.main.OnFileOpen(None,filename)
3138
3139def main():
3140    '''Start up the GSAS-II application'''
3141    #application = GSASIImain() # don't redirect output, someday we
3142    # may want to do this if we can
3143    application = GSASIImain(0)
3144    if wxInspector:
3145        import wx.lib.inspection as wxeye
3146        wxeye.InspectionTool().Show()
3147
3148    #application.main.OnRefine(None)
3149    application.MainLoop()
3150   
3151if __name__ == '__main__':
3152    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.