source: trunk/GSASII.py @ 1173

Last change on this file since 1173 was 1173, checked in by toby, 10 years ago

ask to link phases to histograms after dummy hist

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