source: trunk/GSASII.py @ 1172

Last change on this file since 1172 was 1172, checked in by toby, 9 years ago

ask to link phases to histograms after an import of either; rethink import initialization

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