source: trunk/GSASII.py @ 1160

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

finish ISODISPLACE fixes; improve show var window; improve help window; add refine checkbox for newvars in constraints display

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