source: trunk/GSASII.py @ 2151

Last change on this file since 2151 was 2151, checked in by vondreele, 6 years ago

add Generic TOF to defaultIparms.py; forces user to set flight path & 2-theta
modify instparm reading to sort out multibank issues
add MultiFloatDialog? to solicit multi values from user

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 183.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2016-02-16 22:19:48 +0000 (Tue, 16 Feb 2016) $
6# $Author: vondreele $
7# $Revision: 2151 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2151 2016-02-16 22:19:48Z vondreele $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. Restarting GSAS-II')
59    print('*******************************************************')         
60    loc = os.path.dirname(__file__)
61    import subprocess
62    subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
63    sys.exit()
64   
65# load the GSAS routines
66import GSASIIpath
67GSASIIpath.SetVersionNumber("$Revision: 2151 $")
68import GSASIIIO as G2IO
69import GSASIIgrid as G2gd
70import GSASIIctrls as G2G
71import GSASIIplot as G2plt
72import GSASIIpwd as G2pwd
73import GSASIIpwdGUI as G2pdG
74import GSASIIphsGUI as G2phsG
75import GSASIIimgGUI as G2imG
76import GSASIIspc as G2spc
77import GSASIIstrMain as G2stMn
78import GSASIIstrIO as G2stIO
79import GSASIImath as G2mth
80import GSASIImapvars as G2mv
81import GSASIIobj as G2obj
82import GSASIIlattice as G2lat
83import GSASIIlog as log
84
85__version__ = '0.2.0'
86
87# PATCH: for Mavericks (OS X 10.9.x), wx produces an annoying warning about LucidaGrandeUI.
88# In case stderr has been suppressed there, redirect python error output to stdout. Nobody
89# else should care much about this.
90sys.stderr = sys.stdout
91
92def create(parent):
93    return GSASII(parent)
94
95def SetDefaultDData(dType,histoName,NShkl=0,NDij=0):
96    if dType in ['SXC','SNC']:
97        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
98            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
99            'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
100            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}],
101            'Flack':[0.0,False]}
102    elif dType == 'SNT':
103        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
104            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
105            'Extinction':['Lorentzian','None', {
106            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}]}
107    elif 'P' in dType:
108        return {'Histogram':histoName,'Show':False,'Scale':[1.0,False],
109            'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{},[],0.1],
110            'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
111                [1.,1.,1.,0.,0.,0.],6*[False,]],
112            'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
113                NShkl*[0.01,],NShkl*[False,]],
114            'HStrain':[NDij*[0.0,],NDij*[False,]],                         
115            'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
116
117class GSASII(wx.Frame):
118    '''Define the main GSAS-II frame and its associated menu items
119    '''
120    def MenuBinding(self,event):
121        '''Called when a menu is clicked upon; looks up the binding in table
122        '''
123        log.InvokeMenuCommand(event.GetId(),self,event)
124           
125    def Bind(self,eventtype,handler,*args,**kwargs):
126        '''Override the Bind function so that we can wrap calls that will be logged.
127       
128        N.B. This is a bit kludgy. Menu bindings with an id are wrapped and
129        menu bindings with an object and no id are not.
130        '''
131        if eventtype == wx.EVT_MENU and 'id' in kwargs:
132            menulabels = log.SaveMenuCommand(kwargs['id'],self,handler)
133            if menulabels:
134                wx.Frame.Bind(self,eventtype,self.MenuBinding,*args,**kwargs)
135                return
136        wx.Frame.Bind(self,eventtype,handler,*args,**kwargs)     
137   
138    def _Add_FileMenuItems(self, parent):
139        item = parent.Append(
140            help='Open a GSAS-II project file (*.gpx)', id=wx.ID_ANY,
141            kind=wx.ITEM_NORMAL,text='&Open project...')
142        self.Bind(wx.EVT_MENU, self.OnFileOpen, id=item.GetId())
143        item = parent.Append(
144            help='Save project under current name', id=wx.ID_ANY,
145            kind=wx.ITEM_NORMAL,text='&Save project')
146        self.Bind(wx.EVT_MENU, self.OnFileSave, id=item.GetId())
147        item = parent.Append(
148            help='Save current project to new file', id=wx.ID_ANY,
149            kind=wx.ITEM_NORMAL,text='Save project as...')
150        self.Bind(wx.EVT_MENU, self.OnFileSaveas, id=item.GetId())
151        item = parent.Append(
152            help='Create empty new project, saving current is optional', id=wx.ID_ANY,
153            kind=wx.ITEM_NORMAL,text='&New project')
154        self.Bind(wx.EVT_MENU, self.OnFileClose, id=item.GetId())
155        item = parent.Append(wx.ID_PREFERENCES, text = "&Preferences")
156        self.Bind(wx.EVT_MENU, self.OnPreferences, item)
157        if GSASIIpath.GetConfigValue('debug'):
158            def OnIPython(event):
159                GSASIIpath.IPyBreak()
160            item = parent.Append(wx.ID_ANY, text = "IPython Console")
161            self.Bind(wx.EVT_MENU, OnIPython, item)
162        item = parent.Append(
163            help='Exit from GSAS-II', id=wx.ID_ANY,
164            kind=wx.ITEM_NORMAL,text='&Exit')
165        self.Bind(wx.EVT_MENU, self.OnFileExit, id=item.GetId())
166       
167    def _Add_DataMenuItems(self,parent):
168        item = parent.Append(
169            help='',id=wx.ID_ANY,
170            kind=wx.ITEM_NORMAL,
171            text='Read image data...')
172        self.Bind(wx.EVT_MENU, self.OnImageRead, id=item.GetId())
173        item = parent.Append(
174            help='',id=wx.ID_ANY,
175            kind=wx.ITEM_NORMAL,
176            text='Read Powder Pattern Peaks...')
177        self.Bind(wx.EVT_MENU, self.OnReadPowderPeaks, id=item.GetId())
178        item = parent.Append(
179            help='',id=wx.ID_ANY,
180            kind=wx.ITEM_NORMAL,
181            text='Sum powder data')
182        self.Bind(wx.EVT_MENU, self.OnPwdrSum, id=item.GetId())
183        item = parent.Append(
184            help='',id=wx.ID_ANY,
185            kind=wx.ITEM_NORMAL,
186            text='Sum image data')
187        self.Bind(wx.EVT_MENU, self.OnImageSum, id=item.GetId())
188        item = parent.Append(
189            help='',id=wx.ID_ANY,
190            kind=wx.ITEM_NORMAL,
191            text='Add new phase')
192        self.Bind(wx.EVT_MENU, self.OnAddPhase, id=item.GetId())
193        item = parent.Append(
194            help='',id=wx.ID_ANY,
195            kind=wx.ITEM_NORMAL,
196            text='Delete phase')
197        self.Bind(wx.EVT_MENU, self.OnDeletePhase, id=item.GetId())
198        item = parent.Append(
199            help='',id=wx.ID_ANY,
200            kind=wx.ITEM_NORMAL,
201            text='Rename data') 
202        self.Bind(wx.EVT_MENU, self.OnRenameData, id=item.GetId())
203        item = parent.Append(
204            help='',id=wx.ID_ANY,
205            kind=wx.ITEM_NORMAL,
206            text='Delete data')
207        self.Bind(wx.EVT_MENU, self.OnDataDelete, id=item.GetId())
208               
209    def _Add_CalculateMenuItems(self,parent):
210        item = parent.Append(help='Make new PDFs from selected powder patterns', 
211            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='Make new PDFs')
212        self.MakePDF.append(item)
213#        item.Enable(False)
214        self.Bind(wx.EVT_MENU, self.OnMakePDFs, id=item.GetId())
215       
216        item = parent.Append(help='View least squares parameters', 
217            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='&View LS parms')
218        self.Bind(wx.EVT_MENU, self.ShowLSParms, id=item.GetId())
219       
220        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
221            text='&Refine')
222        if len(self.Refine): # extend state for new menus to match main (on mac)
223            state = self.Refine[0].IsEnabled()
224        else:
225            state = False
226        item.Enable(state)
227        self.Refine.append(item)
228        self.Bind(wx.EVT_MENU, self.OnRefine, id=item.GetId())
229       
230        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
231            text='Sequential refine')
232        if len(self.SeqRefine): # extend state for new menus to match main (on mac)
233            state = self.SeqRefine[0].IsEnabled()
234        else:
235            state = False
236        item.Enable(state)
237        self.SeqRefine.append(item) # save menu obj for use in self.EnableSeqRefineMenu
238        self.Bind(wx.EVT_MENU, self.OnSeqRefine, id=item.GetId())
239       
240    def _init_Imports(self):
241        '''import all the G2phase*.py & G2sfact*.py & G2pwd*.py files that
242        are found in the path
243        '''
244
245        self.ImportPhaseReaderlist = []
246        self._init_Import_routines('phase',self.ImportPhaseReaderlist,'Phase')
247        self.ImportSfactReaderlist = []
248        self._init_Import_routines('sfact',self.ImportSfactReaderlist,'Struct_Factor')
249        self.ImportPowderReaderlist = []
250        self._init_Import_routines('pwd',self.ImportPowderReaderlist,'Powder_Data')
251        self.ImportSmallAngleReaderlist = []
252        self._init_Import_routines('sad',self.ImportSmallAngleReaderlist,'SmallAngle_Data')
253        self.ImportImageReaderlist = []
254        self._init_Import_routines('img',self.ImportImageReaderlist,'Images')
255        self.ImportMenuId = {}
256
257    def _init_Import_routines(self,prefix,readerlist,errprefix):
258        '''import all the import readers matching the prefix
259        '''
260        #path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file
261        #pathlist = sys.path[:]
262        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
263        #path2GSAS2 = os.path.join(
264        #    os.path.dirname(os.path.realpath(__file__)), # location of this file
265        #    'imports')
266        pathlist = sys.path[:]
267        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
268        if '.' not in pathlist: pathlist.append('.') # insert the directory where G2 is started
269
270        filelist = []
271        for path in pathlist:
272            for filename in glob.iglob(os.path.join(
273                path,
274                "G2"+prefix+"*.py")):
275                filelist.append(filename)   
276                #print 'debug: found',filename
277        filelist = sorted(list(set(filelist))) # remove duplicates
278        for filename in filelist:
279            path,rootname = os.path.split(filename)
280            pkg = os.path.splitext(rootname)[0]
281            try:
282                fp = None
283                fp, fppath,desc = imp.find_module(pkg,[path,])
284                pkg = imp.load_module(pkg,fp,fppath,desc)
285                for clss in inspect.getmembers(pkg): # find classes defined in package
286                    if clss[0].startswith('_'): continue
287                    if inspect.isclass(clss[1]):
288                        # check if we have the required methods
289                        for m in 'Reader','ExtensionValidator','ContentsValidator':
290                            if not hasattr(clss[1],m): break
291                            if not callable(getattr(clss[1],m)): break
292                        else:
293                            reader = clss[1]() # create an import instance
294                            if reader.UseReader:
295                                readerlist.append(reader)
296            except AttributeError:
297                print 'Import_'+errprefix+': Attribute Error '+str(filename)
298            #except ImportError:
299            #    print 'Import_'+errprefix+': Error importing file '+str(filename)
300            except Exception,errmsg:
301                print('\nImport_'+errprefix+': Error importing file '+str(filename))
302                print('Error message: '+str(errmsg)+'\n')
303            if fp: fp.close()
304
305    def EnableSeqRefineMenu(self):
306        '''Enable or disable the sequential refinement menu items based on the
307        contents of the Controls 'Seq Data' item (if present)
308        '''
309        controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
310        if controls.get('Seq Data'):
311            for i in self.SeqRefine: i.Enable(True)
312        else:
313            for i in self.SeqRefine: i.Enable(False)
314
315    def PreviewFile(self,filename,fp):
316        'confirm we have the right file'
317        rdmsg = 'File '+str(filename)+' begins:\n\n'
318        for i in range(3):
319            rdmsg += fp.readline()
320        rdmsg += '\n\nDo you want to read this file?'
321        if not all([ord(c) < 128 and ord(c) != 0 for c in rdmsg]): # show only if ASCII
322            rdmsg = 'File '+str(
323                filename)+' is a binary file. Do you want to read this file?'
324        # it would be better to use something that
325        # would resize better, but this will do for now
326        dlg = wx.MessageDialog(
327            self, rdmsg,
328            'Is this the file you want?', 
329            wx.YES_NO | wx.ICON_QUESTION,
330            )
331        dlg.SetSize((700,300)) # does not resize on Mac
332        result = wx.ID_NO
333        try:
334            result = dlg.ShowModal()
335        finally:
336            dlg.Destroy()
337        if result == wx.ID_NO: return True
338        return False
339   
340    def OnImportGeneric(self,reader,readerlist,label,multiple=False,
341                        usedRanIdList=[],Preview=True):
342        '''Used for all imports, including Phases, datasets, images...
343
344        Called from :meth:`GSASII.OnImportPhase`, :meth:`GSASII.OnImportImage`,
345        :meth:`GSASII.OnImportSfact`, :meth:`GSASII.OnImportPowder` and
346        :meth:`GSASII.OnImportSmallAngle`
347
348        Uses reader_objects subclassed from :class:`GSASIIIO.ImportPhase`,
349        :class:`GSASIIIO.ImportStructFactor`,
350        :class:`GSASIIIO.ImportPowderData`,
351        :class:`GSASIIIO.ImportSmallAngleData` or
352        :class:`GSASIIIO.ImportImage`.
353        If a specific reader is specified, only that method will be called,
354        but if no reader is specified, every one that is potentially
355        compatible (by file extension) will be tried on the file(s)
356        selected in the Open File dialog.
357
358        :param reader_object reader: This will be a reference to
359          a particular object to be used to read a file or None,
360          if every appropriate reader should be used.
361
362        :param list readerlist: a list of reader objects appropriate for
363          the current read attempt. At present, this will be either
364          self.ImportPhaseReaderlist, self.ImportSfactReaderlist
365          self.ImportPowderReaderlist or self.ImportImageReaderlist
366          (defined in _init_Imports from the files found in the path),
367          but in theory this list could be tailored.
368          Used only when reader is None.
369
370        :param str label: string to place on the open file dialog:
371          Open `label` input file
372
373        :param bool multiple: True if multiple files can be selected
374          in the file dialog. False is default. At present True is used
375          only for reading of powder data.
376
377        :param list usedRanIdList: an optional list of random Ids that
378          have been used and should not be reused
379
380        :param bool Preview: indicates if a preview of the file should
381          be shown. Default is True, but set to False for image files
382          which are all binary.
383
384        :returns: a list of reader objects (rd_list) that were able
385          to read the specified file(s). This list may be empty.
386        '''
387        self.lastimport = ''
388        self.zipfile = None
389        singlereader = True
390        if reader is None:
391            singlereader = False
392            multiple = False
393            #print "use all formats"
394            choices = "any file (*.*)|*.*"
395            choices += "|zip archive (.zip)|*.zip"
396            extdict = {}
397            # compile a list of allowed extensions
398            for rd in readerlist:
399                fmt = rd.formatName
400                for extn in rd.extensionlist:
401                    if not extdict.get(extn): extdict[extn] = []
402                    extdict[extn] += [fmt,]
403            for extn in sorted(extdict.keys(),cmp=lambda x,y: cmp(x.lower(), y.lower())):
404                fmt = ''
405                for f in extdict[extn]:
406                    if fmt != "": fmt += ', '
407                    fmt += f
408                choices += "|" + fmt + " file (*" + extn + ")|*" + extn
409        else:
410            readerlist = [reader,]
411            # compile a list of allowed extensions
412            choices = reader.formatName + " file ("
413            w = ""
414            for extn in reader.extensionlist:
415                if w != "": w += ";"
416                w += "*" + extn
417            choices += w + ")|" + w
418            choices += "|zip archive (.zip)|*.zip"
419            if not reader.strictExtension:
420                choices += "|any file (*.*)|*.*"
421        # get the file(s)
422        if multiple:
423            mode = style=wx.OPEN | wx.MULTIPLE
424        else:
425            mode = style=wx.OPEN
426        filelist = G2G.GetImportFile(self,message="Choose "+label+" input file",
427                    defaultFile="",wildcard=choices, style=mode)
428        rd_list = []
429        filelist1 = []
430        for filename in filelist:
431            # is this a zip file?
432            if os.path.splitext(filename)[1].lower() == '.zip':
433                extractedfiles = G2IO.ExtractFileFromZip(
434                    filename,parent=self,
435                    multipleselect=True)
436                if extractedfiles is None: continue # error or Cancel
437                if extractedfiles != filename:
438                    self.zipfile = filename # save zip name
439                    filelist1 += extractedfiles
440                    continue
441            filelist1.append(filename)
442        filelist = filelist1
443        for filename in filelist:
444            # is this a zip file?
445            if os.path.splitext(filename)[1].lower() == '.zip':
446                extractedfile = G2IO.ExtractFileFromZip(filename,parent=self)
447                if extractedfile is None: continue # error or Cancel
448                if extractedfile != filename:
449                    filename,self.zipfile = extractedfile,filename # now use the file that was created
450            # determine which formats are compatible with this file
451            primaryReaders = []
452            secondaryReaders = []
453            for rd in readerlist:
454                flag = rd.ExtensionValidator(filename)
455                if flag is None: 
456                    secondaryReaders.append(rd)
457                elif flag:
458                    primaryReaders.append(rd)
459            if len(secondaryReaders) + len(primaryReaders) == 0 and reader:
460                self.ErrorDialog('Not supported','The selected reader cannot read file '+filename)
461                return []
462            elif len(secondaryReaders) + len(primaryReaders) == 0:
463                self.ErrorDialog('No Format','No matching format for file '+filename)
464                return []
465
466            fp = None
467            msg = ''
468            fp = open(filename,'Ur')
469            if len(filelist) == 1 and Preview:
470                if self.PreviewFile(filename,fp): return []
471            self.lastimport = filename # this is probably not what I want to do -- it saves only the
472            # last name in a series. See rd.readfilename for a better name.
473
474            # try the file first with Readers that specify the
475            # file's extension and later with ones that merely allow it
476            errorReport = ''
477            for rd in primaryReaders+secondaryReaders:
478                rd.ReInitialize() # purge anything from a previous read
479                fp.seek(0)  # rewind
480                rd.errors = "" # clear out any old errors
481                if not rd.ContentsValidator(fp): # rejected on cursory check
482                    errorReport += "\n  "+rd.formatName + ' validator error'
483                    if rd.errors: 
484                        errorReport += ': '+rd.errors
485                    continue 
486                repeat = True
487                rdbuffer = {} # create temporary storage for file reader
488                block = 0
489                while repeat: # loop if the reader asks for another pass on the file
490                    block += 1
491                    repeat = False
492                    fp.seek(0)  # rewind
493                    rd.objname = os.path.basename(filename)
494                    flag = False
495                    if GSASIIpath.GetConfigValue('debug'):
496                        flag = rd.Reader(filename,fp,self,
497                                         buffer=rdbuffer,
498                                         blocknum=block,
499                                         usedRanIdList=usedRanIdList,
500                                         )
501                    else:
502                        try:
503                            flag = rd.Reader(filename,fp,self,
504                                             buffer=rdbuffer,
505                                             blocknum=block,
506                                             usedRanIdList=usedRanIdList,
507                                             )
508                        except rd.ImportException as detail:
509                            rd.errors += "\n  Read exception: "+str(detail)
510                        except Exception as detail:
511                            import traceback
512                            rd.errors += "\n  Unhandled read exception: "+str(detail)
513                            rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
514                    if flag: # this read succeeded
515                        rd.readfilename = filename
516                        rd_list.append(copy.deepcopy(rd)) # save the result before it is written over
517                        if rd.repeat:
518                            repeat = True
519                        continue
520                    errorReport += '\n'+rd.formatName + ' read error'
521                    if rd.errors:
522                        errorReport += ': '+rd.errors
523                if rd_list: # read succeeded, was there a warning or any errors?
524                    if rd.warnings:
525                        self.ErrorDialog('Read Warning','The '+ rd.formatName+
526                                         ' reader reported a warning message:\n\n'+
527                                         rd.warnings)
528                    break # success in reading, try no further
529            else:
530                if singlereader:
531                    self.ErrorDialog('Read Error','The '+ rd.formatName+
532                                     ' reader was not able to read file '+filename+msg+
533                                     '\n\nError message(s):\n'+errorReport
534                                     )
535                else:
536                    self.ErrorDialog('Read Error','No reader was able to read file '+filename+msg+
537                                     '\n\nError messages:\n'+errorReport
538                                     )
539            if fp: fp.close()
540        return rd_list
541
542    def _Add_ImportMenu_Phase(self,parent):
543        '''configure the Import Phase menus accord to the readers found in _init_Imports
544        '''
545        submenu = wx.Menu()
546        item = parent.AppendMenu(wx.ID_ANY, 'Phase',
547            submenu, help='Import phase data')
548        for reader in self.ImportPhaseReaderlist:
549            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
550                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
551            self.ImportMenuId[item.GetId()] = reader
552            self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
553        item = submenu.Append(wx.ID_ANY,
554                              help='Import phase data, use file to try to determine format',
555                              kind=wx.ITEM_NORMAL,
556                              text='guess format from file')
557        self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
558       
559    def OnImportPhase(self,event):
560        '''Called in response to an Import/Phase/... menu item
561        to read phase information.
562        dict self.ImportMenuId is used to look up the specific
563        reader item associated with the menu item, which will be
564        None for the last menu item, which is the "guess" option
565        where all appropriate formats will be tried.
566        '''
567        # look up which format was requested
568        reqrdr = self.ImportMenuId.get(event.GetId())
569       
570        # make a list of phase names, ranId's and the histograms used in those phases
571        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
572        phaseNameList = usedHistograms.keys() # phase names in use
573        usedHKLFhists = [] # used single-crystal histograms
574        for p in usedHistograms:
575            for h in usedHistograms[p]:
576                if h.startswith('HKLF ') and h not in usedHKLFhists:
577                    usedHKLFhists.append(h)
578        rdlist = self.OnImportGeneric(reqrdr,
579                                  self.ImportPhaseReaderlist,
580                                  'phase',usedRanIdList=phaseRIdList)
581        if len(rdlist) == 0: return
582        # for now rdlist is only expected to have one element
583        # but below will allow multiple phases to be imported
584        # if ever the import routines ever implement multiple phase reads.
585        self.CheckNotebook()
586        newPhaseList = []
587        for rd in rdlist:
588            PhaseName = ''
589            dlg = wx.TextEntryDialog( # allow editing of phase name
590                                    self, 'Enter the name for the new phase',
591                                    'Edit phase name', rd.Phase['General']['Name'],
592                                    style=wx.OK)
593            while PhaseName == '':
594                dlg.CenterOnParent()
595                if dlg.ShowModal() == wx.ID_OK:
596                    PhaseName = dlg.GetValue().strip()
597                else:
598                    dlg.Destroy()
599                    return
600            dlg.Destroy()
601            # make new phase names unique
602            rd.Phase['General']['Name'] = G2obj.MakeUniqueLabel(PhaseName,phaseNameList)
603            PhaseName = rd.Phase['General']['Name'][:]
604            newPhaseList.append(PhaseName)
605            print 'Read phase '+str(PhaseName)+' from file '+str(self.lastimport)
606            if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
607                sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
608            else:
609                sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
610            psub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
611            self.PatternTree.SetItemPyData(psub,rd.Phase)
612            self.PatternTree.Expand(self.root) # make sure phases are seen
613            self.PatternTree.Expand(sub) 
614            self.PatternTree.Expand(psub)
615            self.PatternTree.SelectItem(psub) # show the page to complete the initialization (yuk!)
616            wx.Yield() # make sure call of GSASII.OnPatternTreeSelChanged happens before we go on
617
618            if rd.Constraints:
619                sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') # was created in CheckNotebook if needed
620                Constraints = self.PatternTree.GetItemPyData(sub)               
621                # TODO: make sure that NEWVAR names are unique here?
622                for i in rd.Constraints:
623                    if type(i) is dict:
624                        #for j in i: print j,' --> ',i[j]
625                        if '_Explain' not in Constraints: Constraints['_Explain'] = {}
626                        Constraints['_Explain'].update(i)
627                        continue
628                    Constraints['Phase'].append(i)
629        if not newPhaseList: return # somehow, no new phases
630        # get a list of existing histograms
631        PWDRlist = []
632        HKLFlist = []
633        if self.PatternTree.GetCount():
634            item, cookie = self.PatternTree.GetFirstChild(self.root)
635            while item:
636                name = self.PatternTree.GetItemText(item)
637                if name.startswith('PWDR ') and name not in PWDRlist:
638                    PWDRlist.append(name)
639                if name.startswith('HKLF ') and name not in HKLFlist:
640                    HKLFlist.append(name)
641                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
642        TextList = PWDRlist + HKLFlist
643        if not len(TextList): return # no data loaded yet
644        header = 'Select histogram(s) to add to new phase(s):'
645        for phaseName in newPhaseList:
646            header += '\n  '+str(phaseName)
647
648        notOK = True
649        while notOK:
650            result = G2G.ItemSelector(TextList,self,header,header='Add histogram(s)',multiple=True)
651            if not result: return
652            # check that selected single crystal histograms are not already in use!
653            used = [TextList[i] for i in result if TextList[i] in usedHKLFhists]
654            #for i in result:
655            #    if TextList[i] in usedHKLFhists: used.append(TextList[i])
656            if used:
657                msg = 'The following single crystal histogram(s) are already in use'
658                for i in used:
659                    msg += '\n  '+str(i)
660                msg += '\nAre you sure you want to add them to this phase? '
661                msg += 'Associating a single crystal dataset to >1 histogram is usually an error, '
662                msg += 'so No is suggested here.'
663                if self.ErrorDialog('Likely error',msg,self,wtype=wx.YES_NO) == wx.ID_YES: notOK = False
664            else:
665                notOK = False
666        # connect new phases to histograms
667        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
668        if not sub:
669            raise Exception('ERROR -- why are there no phases here?')
670        wx.BeginBusyCursor()
671        item, cookie = self.PatternTree.GetFirstChild(sub)
672        while item: # loop over (new) phases
673            phaseName = self.PatternTree.GetItemText(item)
674            data = self.PatternTree.GetItemPyData(item)
675            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
676            if phaseName not in newPhaseList: continue
677            generalData = data['General']
678            SGData = generalData['SGData']
679            Super = generalData.get('Super',0)
680            SuperVec = []
681            if Super:
682                SuperVec = np.array(generalData['SuperVec'][0])
683            UseList = data['Histograms']
684            NShkl = len(G2spc.MustrainNames(SGData))
685            NDij = len(G2spc.HStrainNames(SGData))
686            for i in result:
687                histoName = TextList[i]
688                if histoName in HKLFlist:
689                    #redo UpdateHKLFdata(histoName) here:
690                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
691                    refDict,reflData = self.PatternTree.GetItemPyData(Id)
692                    G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
693                    Super = reflData.get('Super',0)
694                    for iref,ref in enumerate(reflData['RefList']):
695                        hkl = ref[:3]
696                        if Super:
697                            H = list(hkl+SuperVec*ref[3])
698                        else:
699                            H = hkl
700                        ref[4+Super] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
701                        iabsnt = G2spc.GenHKLf(H,SGData)[0]
702                        if iabsnt:  #flag space gp. absences
703                            if Super: 
704                                if not ref[2+Super]:
705                                    ref[3+Super] = 0
706                                else:
707                                    ref[3+Super] = 1    #twin id
708                            else:
709                                ref[3] = 0
710                    UseList[histoName] = SetDefaultDData(reflData['Type'],histoName)
711                elif histoName in PWDRlist:
712                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
713                    refList = self.PatternTree.GetItemPyData(
714                        G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
715                    refList[generalData['Name']] = {}
716                    UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
717                else:
718                    raise Exception('Unexpected histogram '+str(histoName))
719        wx.EndBusyCursor()
720        return # success
721       
722    def _Add_ImportMenu_Image(self,parent):
723        '''configure the Import Image menus accord to the readers found in _init_Imports
724        '''
725        submenu = wx.Menu()
726        item = parent.AppendMenu(wx.ID_ANY, 'Image',
727            submenu, help='Import image file')
728        for reader in self.ImportImageReaderlist:
729            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
730                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
731            self.ImportMenuId[item.GetId()] = reader
732            self.Bind(wx.EVT_MENU, self.OnImportImage, id=item.GetId())
733        item = submenu.Append(wx.ID_ANY,
734                              help='Import image data, use file to try to determine format',
735                              kind=wx.ITEM_NORMAL,
736                              text='guess format from file')
737        self.Bind(wx.EVT_MENU, self.OnImportImage, id=item.GetId())
738       
739    def OnImportImage(self,event):
740        '''Called in response to an Import/Image/... menu item
741        to read an image from a file. Like all the other imports,
742        dict self.ImportMenuId is used to look up the specific
743        reader item associated with the menu item, which will be
744        None for the last menu item, which is the "guess" option
745        where all appropriate formats will be tried.
746
747        A reader object is filled each time an image is read.
748        '''
749        # look up which format was requested
750        reqrdr = self.ImportMenuId.get(event.GetId())
751        rdlist = self.OnImportGeneric(reqrdr,
752                                  self.ImportImageReaderlist,
753                                  'image',multiple=True,Preview=False)
754        if not rdlist: return
755        first = True
756        for rd in rdlist:
757            if first:
758                first = False
759                self.CheckNotebook()
760            if rd.repeatcount == 1 and not rd.repeat: # skip image number if only one in set
761                rd.Data['ImageTag'] = None
762            else:
763                rd.Data['ImageTag'] = rd.repeatcount
764            G2IO.LoadImage2Tree(rd.readfilename,self,rd.Comments,rd.Data,rd.Npix,rd.Image)
765        self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,self.Image,'Image Controls'))             #show last image to have beeen read
766                   
767    def _Add_ImportMenu_Sfact(self,parent):
768        '''configure the Import Structure Factor menus accord to the readers found in _init_Imports
769        '''
770        submenu = wx.Menu()
771        item = parent.AppendMenu(wx.ID_ANY, 'Structure Factor',
772            submenu, help='Import Structure Factor data')
773        for reader in self.ImportSfactReaderlist:
774            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,               
775                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
776            self.ImportMenuId[item.GetId()] = reader
777            self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
778        item = submenu.Append(wx.ID_ANY,
779            help='Import Structure Factor, use file to try to determine format',
780            kind=wx.ITEM_NORMAL,
781            text='guess format from file')
782        self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
783
784    def OnImportSfact(self,event):
785        '''Called in response to an Import/Structure Factor/... menu item
786        to read single crystal datasets.
787        dict self.ImportMenuId is used to look up the specific
788        reader item associated with the menu item, which will be
789        None for the last menu item, which is the "guess" option
790        where all appropriate formats will be tried.
791        '''
792        # get a list of existing histograms
793        HKLFlist = []
794        if self.PatternTree.GetCount():
795            item, cookie = self.PatternTree.GetFirstChild(self.root)
796            while item:
797                name = self.PatternTree.GetItemText(item)
798                if name.startswith('HKLF ') and name not in HKLFlist:
799                    HKLFlist.append(name)
800                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
801        # look up which format was requested
802        reqrdr = self.ImportMenuId.get(event.GetId())
803        rdlist = self.OnImportGeneric(reqrdr,self.ImportSfactReaderlist,
804            'Structure Factor',multiple=True)
805        if len(rdlist) == 0: return
806        self.CheckNotebook()
807        newHistList = []
808        for rd in rdlist:
809            HistName = rd.objname
810            if len(rdlist) <= 2: 
811                dlg = wx.TextEntryDialog( # allow editing of Structure Factor name
812                    self, 'Enter the name for the new Structure Factor',
813                    'Edit Structure Factor name', HistName,
814                    style=wx.OK)
815                dlg.CenterOnParent()
816                if dlg.ShowModal() == wx.ID_OK:
817                    HistName = dlg.GetValue()
818                dlg.Destroy()
819            HistName = 'HKLF '+HistName
820            # make new histogram names unique
821            if len(rd.Banks):
822                for Bank in rd.Banks:
823                    valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
824                    HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
825                    print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
826                    Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
827                    if not Bank['RefDict'].get('FF'):
828                        Bank['RefDict']['FF'] = {}
829                    self.PatternTree.SetItemPyData(Id,[valuesdict,Bank['RefDict']])
830                    Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
831                    self.PatternTree.SetItemPyData(Sub,copy.copy(rd.Parameters))
832                    self.PatternTree.SetItemPyData(
833                        self.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
834                    newHistList.append(HistName)
835            else:
836                valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
837                HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
838                print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
839                if not rd.RefDict.get('FF'):
840                    rd.RefDict['FF'] = {}
841                Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
842                self.PatternTree.SetItemPyData(Id,[valuesdict,rd.RefDict])
843                Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
844                self.PatternTree.SetItemPyData(Sub,rd.Parameters)
845                self.PatternTree.SetItemPyData(
846                    self.PatternTree.AppendItem(Id,text='Reflection List'),{})  #dummy entry for GUI use
847                newHistList.append(HistName)
848               
849            self.PatternTree.SelectItem(Id)
850            self.PatternTree.Expand(Id)
851            self.Sngl = True
852
853        if not newHistList: return # somehow, no new histograms
854        # make a list of phase names
855        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
856        phaseNameList = usedHistograms.keys() # phase names in use
857        if not phaseNameList: return # no phases yet, nothing to do
858        header = 'Select phase(s) to add the new\nsingle crystal dataset(s) to:'
859        for Name in newHistList:
860            header += '\n  '+str(Name)
861        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
862        if not result: return
863        # connect new phases to histograms
864        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
865        if not sub:
866            raise Exception('ERROR -- why are there no phases here?')
867        wx.BeginBusyCursor()
868        item, cookie = self.PatternTree.GetFirstChild(sub)
869        iph = -1
870        while item: # loop over (new) phases
871            iph += 1
872            phaseName = self.PatternTree.GetItemText(item)
873            data = self.PatternTree.GetItemPyData(item)
874            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
875            if iph not in result: continue
876            generalData = data['General']
877            SGData = generalData['SGData']
878            Super = generalData.get('Super',0)
879            SuperVec = []
880            if Super:
881                SuperVec = np.array(generalData['SuperVec'][0])
882            UseList = data['Histograms']
883            for histoName in newHistList:
884                #redo UpdateHKLFdata(histoName) here:
885                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
886                refDict,reflData = self.PatternTree.GetItemPyData(Id)
887                UseList[histoName] = SetDefaultDData(reflData['Type'],histoName)
888                G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
889                if 'TwMax' in reflData:     #nonmerohedral twins present
890                    UseList[histoName]['Twins'] = []
891                    for iT in range(reflData['TwMax'][0]+1):
892                        if iT in reflData['TwMax'][1]:
893                            UseList[histoName]['Twins'].append([False,0.0])
894                        else:
895                            UseList[histoName]['Twins'].append([np.array([[1,0,0],[0,1,0],[0,0,1]]),[1.0,False,reflData['TwMax'][0]]])
896                else:   #no nonmerohedral twins
897                    UseList[histoName]['Twins'] = [[np.array([[1,0,0],[0,1,0],[0,0,1]]),[1.0,False,0]],]
898                for iref,ref in enumerate(reflData['RefList']):
899                    hkl = ref[:3]
900                    if Super:
901                        H = list(hkl+SuperVec*ref[3])
902                    else:
903                        H = hkl
904                    ref[4+Super] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
905                    iabsnt,mul,Uniq,phi = G2spc.GenHKLf(H,SGData)
906                    if iabsnt:  #flag space gp. absences
907                        if Super: 
908                            if not ref[2+Super]:
909                                ref[3+Super] = 0
910                            else:
911                                ref[3+Super] = 1    #twin id?
912                        else:
913                            ref[3] = 0
914        wx.EndBusyCursor()
915       
916        return # success
917
918    def _Add_ImportMenu_powder(self,parent):
919        '''configure the Powder Data menus accord to the readers found in _init_Imports
920        '''
921        submenu = wx.Menu()
922        item = parent.AppendMenu(wx.ID_ANY, 'Powder Data',
923            submenu, help='Import Powder data')
924        for reader in self.ImportPowderReaderlist:
925            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
926                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
927            self.ImportMenuId[item.GetId()] = reader
928            self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
929        item = submenu.Append(wx.ID_ANY,
930            help='Import powder data, use file to try to determine format',
931            kind=wx.ITEM_NORMAL,text='guess format from file')
932        self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
933        submenu.AppendSeparator()
934        item = submenu.Append(wx.ID_ANY,
935            help='Create a powder data set entry that will be simulated',
936            kind=wx.ITEM_NORMAL,text='Simulate a dataset')
937        self.Bind(wx.EVT_MENU, self.OnDummyPowder, id=item.GetId())
938       
939    def OpenPowderInstprm(self,instfile):
940        '''Read a GSAS-II (new) instrument parameter file
941
942        :param str instfile: name of instrument parameter file
943
944        '''
945        File = open(instfile,'r')
946        lines = File.readlines()
947        File.close()
948        return lines       
949           
950    def ReadPowderInstprm(self,instLines,bank):
951        '''Read lines from a GSAS-II (new) instrument parameter file
952        similar to G2pwdGUI.OnLoad
953        If instprm file has multiple banks each with header #Bank n: ..., this
954        finds matching bank no. to load - problem with nonmatches?
955
956        :param list instLines: strings from GSAS-II parameter file; can be concatenated with ';'
957        :param int  bank: bank number to check when instprm file has '#BANK n:...' strings
958            when bank = n then use parameters; otherwise skip that set. Ignored if BANK n:
959            not present. NB: this kind of instprm file made by a Save all profile command in Instrument Parameters
960        :return dict: Inst  instrument parameter dict if OK, or
961                str: Error message if failed   
962        '''
963        if 'GSAS-II' not in instLines[0]: # not a valid file
964            return 'Not a valid GSAS-II instprm file'
965        newItems = []
966        newVals = []
967        Found = False
968        il = 0
969        while il < len(instLines):
970            S = instLines[il]
971            if S[0] == '#':
972                if Found:
973                    break
974                if 'Bank' in S:
975                    if bank == int(S.split(':')[0].split()[1]):
976                        il += 1
977                        S = instLines[il]
978                    else:
979                        il += 1
980                        S = instLines[il]
981                        while il < len(instLines) and '#Bank' not in S:
982                            il += 1
983                            if il == len(instLines):
984                                return 'Bank %d not found in instprm file'%(bank)
985                            S = instLines[il]
986                        continue
987                else:   #a non #Bank file
988                    il += 1
989                    S = instLines[il]
990            Found = True
991            S = S.replace(' ','')
992            SS = S[:-1].split(';')
993            for s in SS:
994                [item,val] = s.split(':')
995                newItems.append(item)
996                try:
997                    newVals.append(float(val))
998                except ValueError:
999                    newVals.append(val)
1000            il += 1                       
1001        return [G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}]
1002       
1003    def ReadPowderIparm(self,instfile,bank,databanks,rd):
1004        '''Read a GSAS (old) instrument parameter file
1005
1006        :param str instfile: name of instrument parameter file
1007        :param int bank: the bank number read in the raw data file
1008        :param int databanks: the number of banks in the raw data file.
1009          If the number of banks in the data and instrument parameter files
1010          agree, then the sets of banks are assumed to match up and bank
1011          is used to select the instrument parameter file. If not, the user
1012          is asked to make a selection.
1013        :param obj rd: the raw data (histogram) data object. This
1014          sets rd.instbank.
1015
1016        '''
1017        if not os.path.exists(instfile): # no such file
1018            return {}
1019        fp = 0
1020        try:
1021            fp = open(instfile,'Ur')
1022            Iparm = {}
1023            for S in fp:
1024                if '#' in S[0]:
1025                    continue
1026                Iparm[S[:12]] = S[12:-1]
1027        except IOError:
1028            print('Error reading file:'+str(instfile))
1029        if fp:       
1030            fp.close()
1031
1032        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
1033        hType = Iparm['INS   HTYPE '].strip()
1034        if ibanks == 1: # there is only one bank here, return it
1035            rd.instbank = 1
1036            return Iparm
1037        if 'PNT' in hType:
1038            rd.instbank = bank
1039        elif ibanks != databanks:
1040            # number of banks in data and prm file not not agree, need a
1041            # choice from a human here
1042            choices = []
1043            for i in range(1,1+ibanks):
1044                choices.append('Bank '+str(i))
1045            bank = rd.BlockSelector(
1046                choices, self,
1047                title='Select an instrument parameter bank for '+
1048                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
1049                '\nOr use Cancel to select from the default parameter sets',
1050                header='Block Selector')
1051        if bank is None: return {}
1052        # pull out requested bank # bank from the data, and change the bank to 1
1053        IparmS = {}
1054        for key in Iparm:
1055            if 'INS' in key[:3]:    #skip around rubbish lines in some old iparm files
1056                if key[4:6] == "  ":
1057                    IparmS[key] = Iparm[key]
1058                elif int(key[4:6].strip()) == bank:
1059                    IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
1060        rd.instbank = bank
1061        return IparmS
1062                       
1063    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
1064        '''Open and read an instrument parameter file for a data file
1065        Returns the list of parameters used in the data tree
1066
1067        :param obj rd: the raw data (histogram) data object.
1068
1069        :param str prevIparm: not used
1070
1071        :param str lastIparmfile: Name of last instrument parameter
1072          file that was read, or a empty string.
1073
1074        :param str lastdatafile: Name of last data file that was read.
1075
1076        :returns: a list of two dicts, the first containing instrument parameters
1077          and the second used for TOf lookup tables for profile coeff.
1078
1079        '''
1080        def SetPowderInstParms(Iparm, rd):
1081            '''extracts values from instrument parameters in rd.instdict
1082            or in array Iparm.
1083            Create and return the contents of the instrument parameter tree entry.
1084            '''
1085            Irads = {0:' ',1:'CrKa',2:'FeKa',3:'CuKa',4:'MoKa',5:'AgKa',6:'TiKa',7:'CoKa'}
1086            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
1087            # override inst values with values read from data file
1088            Bank = rd.powderentry[2]    #should be used in multibank iparm files
1089            if rd.instdict.get('type'):
1090                DataType = rd.instdict.get('type')
1091            data = [DataType,]
1092            instname = Iparm.get('INS  1INAME ')
1093            irad = int(Iparm.get('INS  1 IRAD ','0'))
1094            if instname:
1095                rd.Sample['InstrName'] = instname.strip()
1096            if 'C' in DataType:
1097                wave1 = None
1098                wave2 = 0.0
1099                if rd.instdict.get('wave'):
1100                    wl = rd.instdict.get('wave')
1101                    wave1 = wl[0]
1102                    if len(wl) > 1: wave2 = wl[1]
1103                s = Iparm['INS  1 ICONS']
1104                if not wave1:
1105                    wave1 = G2IO.sfloat(s[:10])
1106                    wave2 = G2IO.sfloat(s[10:20])
1107                v = (wave1,wave2,
1108                     G2IO.sfloat(s[20:30]),G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
1109                if not v[1]:
1110                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
1111                    v = (v[0],v[2],v[4])
1112                    codes = [0,0,0,0]
1113                else:
1114                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
1115                    codes = [0,0,0,0,0,0]
1116                data.extend(v)
1117                if 'INS  1PRCF  ' in Iparm:
1118                    v1 = Iparm['INS  1PRCF  '].split()                                                 
1119                    v = Iparm['INS  1PRCF 1'].split()
1120                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1121                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1122                    v = Iparm['INS  1PRCF 2'].split()
1123                    if v1[0] == 3:
1124                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1125                    else:
1126                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file                   
1127                else:
1128                    v1 = Iparm['INS  1PRCF1 '].split()                                                 
1129                    v = Iparm['INS  1PRCF11'].split()
1130                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1131                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1132                    v = Iparm['INS  1PRCF12'].split()
1133                    if v1[0] == 3:
1134                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1135                    else:
1136                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
1137                codes.extend([0,0,0,0,0,0,0])
1138                Iparm1 = G2IO.makeInstDict(names,data,codes)
1139                Iparm1['Source'] = [Irads[irad],Irads[irad]]
1140                Iparm1['Bank'] = [Bank,Bank,0]
1141                return [Iparm1,{}]
1142            elif 'T' in DataType:
1143                names = ['Type','fltPath','2-theta','difC','difA', 'difB','Zero','alpha','beta-0','beta-1',
1144                    'beta-q','sig-0','sig-1','sig-2','sig-q', 'X','Y','Azimuth',]
1145                codes = [0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,]
1146                azm = 0.
1147                if 'INS  1DETAZM' in Iparm:
1148                    azm = float(Iparm['INS  1DETAZM'])
1149                s = Iparm['INS   FPATH1'].split()
1150                fltPath0 = G2IO.sfloat(s[0])
1151                s = Iparm['INS  1BNKPAR'].split()
1152                fltPath1 = G2IO.sfloat(s[0])
1153                data.extend([fltPath0+fltPath1,])               #Flight path source-sample-detector
1154                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
1155                s = Iparm['INS  1 ICONS'].split()
1156                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,G2IO.sfloat(s[2])])    #difC,difA,difB,Zero
1157                if 'INS  1PRCF  ' in Iparm:
1158                    s = Iparm['INS  1PRCF  '].split()
1159                    pfType = int(s[0])
1160                    s = Iparm['INS  1PRCF 1'].split()
1161                    if abs(pfType) == 1:
1162                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1163                        s = Iparm['INS  1PRCF 2'].split()
1164                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1165                    elif abs(pfType) in [3,4,5]:
1166                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1167                        if abs(pfType) == 4:
1168                            data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1169                        else:
1170                            s = Iparm['INS  1PRCF 2'].split()
1171                            data.extend([0.0,0.0,G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                       
1172                    elif abs(pfType) == 2:
1173                        data.extend([G2IO.sfloat(s[1]),0.0,1./G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1174                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                           
1175                else:
1176                    s = Iparm['INS  1PRCF1 '].split()
1177                    pfType = int(s[0])
1178                    s = Iparm['INS  1PRCF11'].split()
1179                    if abs(pfType) == 1:
1180                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1181                        s = Iparm['INS  1PRCF12'].split()
1182                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1183                    elif abs(pfType) in [3,4,5]:
1184                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1185                        if abs(pfType) == 4:
1186                            data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y
1187                        else:
1188                            s = Iparm['INS  1PRCF12'].split()
1189                            data.extend([0.0,0.0,G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])    #beta-q, sig-0, sig-1, sig-2, sig-q, X, Y                       
1190                Inst1 = G2IO.makeInstDict(names,data,codes)
1191                Inst1['Bank'] = [Bank,Bank,0]
1192                Inst2 = {}
1193                if pfType < 0:
1194                    Ipab = 'INS  1PAB'+str(-pfType)
1195                    Npab = int(Iparm[Ipab+'  '].strip())
1196                    Inst2['Pdabc'] = []
1197                    for i in range(Npab):
1198                        k = Ipab+str(i+1).rjust(2)
1199                        s = Iparm[k].split()
1200                        Inst2['Pdabc'].append([float(t) for t in s])
1201                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
1202                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
1203                if 'INS  1I ITYP' in Iparm:
1204                    s = Iparm['INS  1I ITYP'].split()
1205                    Ityp = int(s[0])
1206                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
1207                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
1208                    if Ityp in [1,2,4]:
1209                        Inst2['Itype'] = Itypes[Ityp-1]
1210                        Inst2['Tminmax'] = Tminmax
1211                        Icoeff = []
1212                        Iesd = []
1213                        Icovar = []                   
1214                        for i in range(3):
1215                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
1216                            Icoeff += [float(S) for S in s]
1217                            s = Iparm['INS  1IECOF'+str(i+1)].split()
1218                            Iesd += [float(S) for S in s]
1219                        NT = 10
1220                        for i in range(8):
1221                            s = Iparm['INS  1IECOR'+str(i+1)]
1222                            if i == 7:
1223                                NT = 8
1224                            Icovar += [float(s[6*j:6*j+6]) for j in range(NT)]
1225                        Inst2['Icoeff'] = Icoeff
1226                        Inst2['Iesd'] = Iesd
1227                        Inst2['Icovar'] = Icovar
1228                return [Inst1,Inst2]
1229               
1230        def GetDefaultParms(self,rd):
1231            '''Solicits from user a default set of parameters & returns Inst parm dict
1232            param: self: refers to the GSASII main class
1233            param: rd: importer data structure
1234            returns: dict: Instrument parameter dictionary
1235            '''       
1236            sind = lambda x: math.sin(x*math.pi/180.)
1237            tand = lambda x: math.tan(x*math.pi/180.)
1238            import defaultIparms as dI
1239            while True: # loop until we get a choice
1240                choices = []
1241                head = 'Select from default instrument parameters for '+rd.idstring
1242   
1243                for l in dI.defaultIparm_lbl:
1244                    choices.append('Defaults for '+l)
1245                res = rd.BlockSelector(choices,ParentFrame=self,title=head,
1246                    header='Select default inst parms',useCancel=False)
1247                if res is None: continue
1248                rd.instfile = ''
1249                if 'Generic' in choices[res]:
1250                    dlg = G2G.MultiFloatDialog(self,title='Generic TOF detector bank',
1251                        prompts=['Total FP','2-theta',],values=[25.0,150.,],
1252                            limits=[[6.,200.],[5.,175.],],formats=['%6.2f','%6.1f',])
1253                    if dlg.ShowModal() == wx.ID_OK:
1254                        FP,tth = dlg.GetValues()
1255                        difC = 252.816*2.*FP*sind(tth/2.)
1256                        sig1 = 50.+2.5e-6*(difC/tand(tth/2.))**2
1257                        rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1258                        Inst = self.ReadPowderInstprm(dI.defaultIparms[res],bank)
1259                        Inst[0]['difC'] = [difC,difC,0]
1260                        Inst[0]['sig-1'] = [sig1,sig1,0]
1261                        return Inst    #this is [Inst1,Inst2] a pair of dicts
1262                    dlg.Destroy()
1263                else:
1264                    rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1265                    return self.ReadPowderInstprm(dI.defaultIparms[res],bank)    #this is [Inst1,Inst2] a pair of dicts
1266
1267        # stuff we might need from the reader
1268        filename = rd.powderentry[0]
1269        bank = rd.powderentry[2]
1270        numbanks = rd.numbanks
1271        #1st priority: is there an instrument parameter file matching the current file
1272        # with extension .instprm, .prm, .inst, or .ins? If so read it
1273        basename = os.path.splitext(filename)[0]
1274        for ext in '.instprm','.prm','.inst','.ins':
1275            if self.zipfile:
1276                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1277                    selection=os.path.split(basename + ext)[1],parent=self)
1278            else:
1279                instfile = basename + ext
1280            if not os.path.exists(instfile):
1281                continue
1282            if 'instprm' in instfile:
1283                Lines = self.OpenPowderInstprm(instfile)
1284                instParmList = self.ReadPowderInstprm(Lines,bank)    #this is [Inst1,Inst2] a pair of dicts
1285                if 'list' in str(type(instParmList)):
1286                    rd.instfile = instfile
1287                    rd.instmsg = 'GSAS-II file '+instfile
1288                    return instParmList
1289                else:
1290                    #print 'debug: open/read failed',instfile
1291                    pass # fail silently
1292            else:
1293                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1294                if Iparm:
1295                    #print 'debug: success'
1296                    rd.instfile = instfile
1297                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1298                    return SetPowderInstParms(Iparm,rd)
1299                else:
1300                    #print 'debug: open/read failed',instfile
1301                    pass # fail silently
1302
1303        #2nd priority: is there an instrument parameter file defined for the current data set?
1304        # or if this is a read on a set of set of files, use the last one again
1305        #if rd.instparm or (lastdatafile == filename and lastIparmfile):
1306        if rd.instparm or lastIparmfile:
1307            if rd.instparm:
1308                instfile = os.path.join(os.path.split(filename)[0],rd.instparm)
1309            else:
1310                # for multiple reads of one data file, reuse the inst parm file
1311                instfile = lastIparmfile
1312            if self.zipfile:
1313                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1314                    selection=os.path.split(instfile)[1],parent=self)
1315            if os.path.exists(instfile):
1316                #print 'debug: try read',instfile
1317                if 'instprm' in instfile:   #GSAS-II file must have .instprm as extension
1318                    Lines = self.OpenPowderInstprm(instfile)
1319                    if Lines is not None:
1320                        instParmList = self.ReadPowderInstprm(Lines,bank)   #this is [Inst1,Inst2] a pair of dicts
1321                else:   #old GSAS style iparm file - could be named anything!
1322                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1323                    if Iparm:
1324                        #print 'debug: success'
1325                        rd.instfile = instfile
1326                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1327                        instParmList = SetPowderInstParms(Iparm,rd)     #this is [Inst1,Inst2] a pair of dicts
1328                if 'list' in str(type(instParmList)):   #record stuff & return stuff
1329                    rd.instfile = instfile
1330                    rd.instmsg = 'GSAS-II file '+instfile
1331                    return instParmList
1332                else:   #bad iparms - try default
1333                    rd.instmsg = instParmList   #an error message
1334                    return GetDefaultParms(self,rd)
1335            else:
1336                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1337                    +str(instfile)+' requested by file '+ filename)
1338#        # did we read the data file from a zip? If so, look there for a
1339#        # instrument parameter file
1340#        if self.zipfile:
1341#            for ext in '.instprm','.prm','.inst','.ins':
1342#                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1343#                    selection=os.path.split(basename + ext)[1],parent=self)
1344#                if instfile is not None and instfile != self.zipfile:
1345#                    print 'debug:',instfile,'created from ',self.zipfile
1346#                    Lines = self.OpenPowderInstprm(instfile)
1347#                    instParmList = None
1348#                    if Lines is not None:
1349#                        instParmList = self.ReadPowderInstprm(Lines,bank)    #this is [Inst1,Inst2] a pair of dicts
1350#                    if 'dict' in str(type(instParmList)):
1351#                        rd.instfile = instfile
1352#                        rd.instmsg = 'GSAS-II file '+instfile
1353#                        return instParmList
1354#                    else:
1355#                        rd.instmsg = instParmList   #an error message
1356#                        print 'three',instParmList
1357#                        return GetDefaultParms(self,rd)
1358#                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1359#                    if Iparm:
1360#                        rd.instfile = instfile
1361#                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1362#                        return SetPowderInstParms(Iparm,rd)
1363#                    else:
1364#                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
1365#                        pass # fail silently
1366#
1367        #Finally - ask user for Instrument parametrs file - seems it can't be in a zip file
1368        while True: # loop until we get a file that works or we get a cancel
1369            instfile = ''
1370            pth = G2G.GetImportPath(self)
1371            if not pth: pth = '.'
1372            dlg = wx.FileDialog(self,
1373                'Choose inst. param file for "'+rd.idstring+'" (or Cancel for default)',
1374                pth, '',
1375                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1376                'GSAS-II iparm file (*.instprm)|*.instprm|'
1377                'All files (*.*)|*.*', wx.OPEN)
1378            if os.path.exists(lastIparmfile):
1379                dlg.SetFilename(lastIparmfile)
1380            if dlg.ShowModal() == wx.ID_OK:
1381                instfile = dlg.GetPath()
1382            dlg.Destroy()
1383            if not instfile: 
1384                return GetDefaultParms(self,rd) #on Cancel/break
1385            if 'instprm' in instfile:
1386                Lines = self.OpenPowderInstprm(instfile)
1387                if Lines is not None:
1388                    instParmList = self.ReadPowderInstprm(Lines,bank)    #this is [Inst1,Inst2] a pair of dicts
1389                if 'list' in str(type(instParmList)):
1390                    rd.instfile = instfile
1391                    rd.instmsg = 'GSAS-II file '+instfile
1392                    return instParmList
1393                else:
1394                    rd.instmsg = instParmList   #an error message
1395                    return GetDefaultParms(self,rd)
1396            else:
1397                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1398                if Iparm:
1399                    #print 'debug: success with',instfile
1400                    rd.instfile = instfile
1401                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1402                    return SetPowderInstParms(Iparm,rd)
1403                else:
1404                    self.ErrorDialog('Read Error',
1405                                     'Error opening/reading file '+str(instfile))
1406       
1407    def OnImportPowder(self,event):
1408        '''Called in response to an Import/Powder Data/... menu item
1409        to read a powder diffraction data set.
1410        dict self.ImportMenuId is used to look up the specific
1411        reader item associated with the menu item, which will be
1412        None for the last menu item, which is the "guess" option
1413        where all appropriate formats will be tried.
1414
1415        Also reads an instrument parameter file for each dataset.
1416        '''
1417        # get a list of existing histograms
1418        PWDRlist = []
1419        if self.PatternTree.GetCount():
1420            item, cookie = self.PatternTree.GetFirstChild(self.root)
1421            while item:
1422                name = self.PatternTree.GetItemText(item)
1423                if name.startswith('PWDR ') and name not in PWDRlist:
1424                    PWDRlist.append(name)
1425                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1426        # look up which format was requested
1427        reqrdr = self.ImportMenuId.get(event.GetId()) 
1428        rdlist = self.OnImportGeneric(
1429            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1430        if len(rdlist) == 0: return
1431        self.CheckNotebook()
1432        Iparm = None
1433        lastIparmfile = ''
1434        lastdatafile = ''
1435        newHistList = []
1436        self.EnablePlot = False
1437        for rd in rdlist:
1438            if 'Instrument Parameters' not in rd.pwdparms:
1439                # get instrument parameters for each dataset, unless already set
1440                Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1441                if rd.repeat_instparm: 
1442                    lastIparmfile = rd.instfile
1443                # override any keys in read instrument parameters with ones set in import
1444                for key in Iparm1: 
1445                    if key in rd.instdict:
1446                        Iparm1[key] = rd.instdict[key]
1447            else:
1448                Iparm1,Iparm2 = rd.pwdparms['Instrument Parameters']
1449            lastdatafile = rd.powderentry[0]
1450            HistName = rd.idstring
1451            HistName = 'PWDR '+HistName
1452            # make new histogram names unique
1453            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1454            print 'Read powder data '+str(HistName)+ \
1455                ' from file '+str(rd.readfilename) + \
1456                ' with parameters from '+str(rd.instmsg)
1457            # data are read, now store them in the tree
1458            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1459            if 'T' in Iparm1['Type'][0]:
1460                if not rd.clockWd and rd.GSAS:
1461                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1462                cw = np.diff(rd.powderdata[0])
1463                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1464                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
1465                    npts = min(len(rd.powderdata[0]),len(rd.powderdata[1]),len(cw))
1466                    rd.powderdata[1] = rd.powderdata[1][:npts]/cw[:npts]
1467                    rd.powderdata[2] = rd.powderdata[2][:npts]*cw[:npts]**2  #1/var=w at this point
1468                else:       #NB: from topas/fullprof type files
1469                    rd.powderdata[1] = rd.powderdata[1][:-1]
1470                    rd.powderdata[2] = rd.powderdata[2][:-1]
1471                if 'Itype' in Iparm2:
1472                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1473                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1474                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1475                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1476                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1477                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1478                    var += WYI*rd.powderdata[1]**2
1479                    var /= YI**2
1480                    rd.powderdata[2] = 1./var
1481                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1482                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1483                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1484            valuesdict = {
1485                'wtFactor':1.0,
1486                'Dummy':False,
1487                'ranId':ran.randint(0,sys.maxint),
1488                'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1489                'qPlot':False,'dPlot':False,'sqrtPlot':False
1490                }
1491            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1492            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1493            self.PatternTree.SetItemPyData(
1494                self.PatternTree.AppendItem(Id,text='Comments'),
1495                rd.comments)
1496            Tmin = min(rd.powderdata[0])
1497            Tmax = max(rd.powderdata[0])
1498            self.PatternTree.SetItemPyData(
1499                self.PatternTree.AppendItem(Id,text='Limits'),
1500                rd.pwdparms.get('Limits',[(Tmin,Tmax),[Tmin,Tmax]])
1501                )
1502            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1503            self.PatternTree.SetItemPyData(
1504                self.PatternTree.AppendItem(Id,text='Background'),
1505                rd.pwdparms.get('Background',
1506                    [['chebyschev',True,3,1.0,0.0,0.0],{'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1507                    )
1508            self.PatternTree.SetItemPyData(
1509                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1510                [Iparm1,Iparm2])
1511            self.PatternTree.SetItemPyData(
1512                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1513                rd.Sample)
1514            self.PatternTree.SetItemPyData(
1515                self.PatternTree.AppendItem(Id,text='Peak List')
1516                ,{'peaks':[],'sigDict':{}})
1517            self.PatternTree.SetItemPyData(
1518                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1519                [[],[]])
1520            self.PatternTree.SetItemPyData(
1521                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1522                [])
1523            self.PatternTree.SetItemPyData(
1524                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1525                {})
1526            # if any Control values have been set, move them into tree
1527            Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
1528            Controls.update(rd.Controls)
1529            newHistList.append(HistName)
1530        else:
1531            self.EnablePlot = True
1532            self.PatternTree.Expand(Id)
1533            self.PatternTree.SelectItem(Id)
1534           
1535        if not newHistList: return # somehow, no new histograms
1536        # make a list of phase names
1537        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1538        phaseNameList = usedHistograms.keys() # phase names in use
1539        if not phaseNameList: return # no phases yet, nothing to do
1540        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1541        for Name in newHistList:
1542            header += '\n  '+str(Name)
1543
1544        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1545        if not result: return
1546        # connect new phases to histograms
1547        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1548        if not sub:
1549            raise Exception('ERROR -- why are there no phases here?')
1550        item, cookie = self.PatternTree.GetFirstChild(sub)
1551        iph = -1
1552        while item: # loop over (new) phases
1553            iph += 1
1554            phaseName = self.PatternTree.GetItemText(item)
1555            data = self.PatternTree.GetItemPyData(item)
1556            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1557            if iph not in result: continue
1558            generalData = data['General']
1559            SGData = generalData['SGData']
1560            UseList = data['Histograms']
1561            NShkl = len(G2spc.MustrainNames(SGData))
1562            NDij = len(G2spc.HStrainNames(SGData))
1563            for histoName in newHistList:
1564                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1565                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1566                refList = self.PatternTree.GetItemPyData(
1567                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1568                refList[generalData['Name']] = []
1569        return # success
1570
1571    def OnDummyPowder(self,event):
1572        '''Called in response to Import/Powder Data/Simulate menu item
1573        to create a Dummy powder diffraction data set.
1574
1575        Reads an instrument parameter file and then gets input from the user
1576        '''
1577        # get a list of existing histograms
1578        PWDRlist = []
1579        if self.PatternTree.GetCount():
1580            item, cookie = self.PatternTree.GetFirstChild(self.root)
1581            while item:
1582                name = self.PatternTree.GetItemText(item)
1583                if name.startswith('PWDR ') and name not in PWDRlist:
1584                    PWDRlist.append(name)
1585                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1586        # Initialize a base class reader
1587        rd = G2IO.ImportPowderData(
1588            extensionlist=tuple(),
1589            strictExtension=False,
1590            formatName = 'Simulate dataset',
1591            longFormatName = 'Compute a simulated pattern')
1592        rd.powderentry[0] = '' # no filename
1593        # #self.powderentry[1] = pos # bank offset (N/A here)
1594        rd.powderentry[2] = 1 # only one bank
1595        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1596        self.CheckNotebook()
1597        Iparm = None
1598        lastIparmfile = ''
1599        lastdatafile = ''
1600        self.zipfile = None
1601        # get instrument parameters for it
1602        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1603        if 'T' in Iparm1['Type'][0]:
1604            print('TOF simulation not supported yet')
1605            return False
1606        else:
1607            # need to get name, 2theta start, end, step
1608            rd.idstring = ' CW'
1609            if 'X' in Iparm1['Type'][0]:
1610                rd.idstring = 'CW x-ray simulation'
1611            else:
1612                rd.idstring = 'CW neutron simulation'
1613            # base initial range on wavelength
1614            wave = Iparm1.get('Lam')
1615            if wave:
1616                wave = wave[0]
1617            else:
1618                wave = Iparm1.get('Lam1')
1619                if wave:
1620                    wave = wave[0]
1621        N = 0
1622        while (N < 3): # insist on a dataset with a few points
1623            names = ('dataset name', 'start angle', 'end angle', 'step size')
1624            if not wave or wave < 1.0:
1625                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1626            else:
1627                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1628            dlg = G2G.ScrolledMultiEditor(
1629                self,[inp] * len(inp),range(len(inp)),names,
1630                header='Enter simulation name and range',
1631                minvals=(None,0.001,0.001,0.0001),
1632                maxvals=(None,180.,180.,.1),
1633                sizevals=((225,-1),)
1634                )
1635            dlg.CenterOnParent()
1636            if dlg.ShowModal() == wx.ID_OK:
1637                if inp[1] > inp[2]:
1638                    end,start,step = inp[1:]
1639                else:               
1640                    start,end,step = inp[1:]
1641                step = abs(step)
1642            else:
1643                return False
1644            N = int((end-start)/step)+1
1645            x = np.linspace(start,end,N,True)
1646            N = len(x)
1647        rd.powderdata = [
1648            np.array(x), # x-axis values
1649            np.zeros_like(x), # powder pattern intensities
1650            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1651            np.zeros_like(x), # calc. intensities (zero)
1652            np.zeros_like(x), # calc. background (zero)
1653            np.zeros_like(x), # obs-calc profiles
1654            ]
1655        Tmin = rd.powderdata[0][0]
1656        Tmax = rd.powderdata[0][-1]
1657        # data are read, now store them in the tree
1658        HistName = inp[0]
1659        HistName = 'PWDR '+HistName
1660        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1661        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1662        valuesdict = {
1663            'wtFactor':1.0,
1664            'Dummy':True,
1665            'ranId':ran.randint(0,sys.maxint),
1666            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1667            'qPlot':False,'dPlot':False,'sqrtPlot':False
1668            }
1669        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1670        self.PatternTree.SetItemPyData(
1671            self.PatternTree.AppendItem(Id,text='Comments'),
1672            rd.comments)
1673        self.PatternTree.SetItemPyData(
1674            self.PatternTree.AppendItem(Id,text='Limits'),
1675            [(Tmin,Tmax),[Tmin,Tmax]])
1676        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1677        self.PatternTree.SetItemPyData(
1678            self.PatternTree.AppendItem(Id,text='Background'),
1679            [['chebyschev',True,3,1.0,0.0,0.0],
1680             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1681        self.PatternTree.SetItemPyData(
1682            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1683            [Iparm1,Iparm2])
1684        self.PatternTree.SetItemPyData(
1685            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1686            rd.Sample)
1687        self.PatternTree.SetItemPyData(
1688            self.PatternTree.AppendItem(Id,text='Peak List')
1689            ,{'peaks':[],'sigDict':{}})
1690        self.PatternTree.SetItemPyData(
1691            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1692            [[],[]])
1693        self.PatternTree.SetItemPyData(
1694            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1695            [])
1696        self.PatternTree.SetItemPyData(
1697            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1698            {})
1699        self.PatternTree.Expand(Id)
1700        self.PatternTree.SelectItem(Id)
1701        print('Added simulation powder data '+str(HistName)+ 
1702              ' with parameters from '+str(rd.instmsg))
1703
1704        # make a list of phase names
1705        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1706        phaseNameList = usedHistograms.keys() # phase names in use
1707        if not phaseNameList: return # no phases yet, nothing to do
1708        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1709        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1710        if not result: return
1711        # connect new phases to histograms
1712        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1713        if not sub:
1714            raise Exception('ERROR -- why are there no phases here?')
1715        item, cookie = self.PatternTree.GetFirstChild(sub)
1716        iph = -1
1717        while item: # loop over (new) phases
1718            iph += 1
1719            phaseName = self.PatternTree.GetItemText(item)
1720            data = self.PatternTree.GetItemPyData(item)
1721            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1722            if iph not in result: continue
1723            generalData = data['General']
1724            SGData = generalData['SGData']
1725            UseList = data['Histograms']
1726            NShkl = len(G2spc.MustrainNames(SGData))
1727            NDij = len(G2spc.HStrainNames(SGData))
1728            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
1729            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1730            refList = self.PatternTree.GetItemPyData(
1731                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1732            refList[generalData['Name']] = []
1733        return # success
1734       
1735    def OnPreferences(self,event):
1736        'Edit the GSAS-II configuration variables'
1737        dlg = G2G.SelectConfigSetting(self)
1738        dlg.ShowModal() == wx.ID_OK
1739        dlg.Destroy()
1740
1741    def _Add_ImportMenu_smallangle(self,parent):
1742        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1743        '''
1744        submenu = wx.Menu()
1745        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1746            submenu, help='Import small angle data')
1747        for reader in self.ImportSmallAngleReaderlist:
1748            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1749                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1750            self.ImportMenuId[item.GetId()] = reader
1751            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1752        # item = submenu.Append(wx.ID_ANY,
1753        #     help='Import small angle data, use file to try to determine format',
1754        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1755        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1756
1757    def OnImportSmallAngle(self,event):
1758        '''Called in response to an Import/Small Angle Data/... menu item
1759        to read a small angle diffraction data set.
1760        dict self.ImportMenuId is used to look up the specific
1761        reader item associated with the menu item, which will be
1762        None for the last menu item, which is the "guess" option
1763        where all appropriate formats will be tried.
1764
1765        '''
1766       
1767        def GetSASDIparm(reader):
1768            parm = reader.instdict
1769            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1770                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1771            return Iparm,{}
1772           
1773        # get a list of existing histograms
1774        SASDlist = []
1775        if self.PatternTree.GetCount():
1776            item, cookie = self.PatternTree.GetFirstChild(self.root)
1777            while item:
1778                name = self.PatternTree.GetItemText(item)
1779                if name.startswith('SASD ') and name not in SASDlist:
1780                    SASDlist.append(name)
1781                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1782        # look up which format was requested
1783        reqrdr = self.ImportMenuId.get(event.GetId()) 
1784        rdlist = self.OnImportGeneric(
1785            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1786        if len(rdlist) == 0: return
1787        self.CheckNotebook()
1788        Iparm = None
1789        lastdatafile = ''
1790        newHistList = []
1791        self.EnablePlot = False
1792        for rd in rdlist:
1793            lastdatafile = rd.smallangleentry[0]
1794            HistName = rd.idstring
1795            HistName = 'SASD '+HistName
1796            # make new histogram names unique
1797            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1798            print 'Read small angle data '+str(HistName)+ \
1799                ' from file '+str(self.lastimport)
1800            # data are read, now store them in the tree
1801            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1802            Iparm1,Iparm2 = GetSASDIparm(rd)
1803#            if 'T' in Iparm1['Type'][0]:
1804#                if not rd.clockWd and rd.GSAS:
1805#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1806#                cw = np.diff(rd.powderdata[0])
1807#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1808#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1809#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1810#                if 'Itype' in Iparm2:
1811#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1812#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1813#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1814#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1815#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1816#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1817#                    var += WYI*rd.powderdata[1]**2
1818#                    var /= YI**2
1819#                    rd.powderdata[2] = 1./var
1820#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1821#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1822#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1823            Tmin = min(rd.smallangledata[0])
1824            Tmax = max(rd.smallangledata[0])
1825            valuesdict = {
1826                'wtFactor':1.0,
1827                'Dummy':False,
1828                'ranId':ran.randint(0,sys.maxint),
1829                }
1830            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1831            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1832            self.PatternTree.SetItemPyData(
1833                self.PatternTree.AppendItem(Id,text='Comments'),
1834                rd.comments)
1835            self.PatternTree.SetItemPyData(
1836                self.PatternTree.AppendItem(Id,text='Limits'),
1837                [(Tmin,Tmax),[Tmin,Tmax]])
1838            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1839            self.PatternTree.SetItemPyData(
1840                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1841                [Iparm1,Iparm2])
1842            self.PatternTree.SetItemPyData(
1843                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1844            self.PatternTree.SetItemPyData(
1845                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1846                rd.Sample)
1847            self.PatternTree.SetItemPyData(
1848                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1849            newHistList.append(HistName)
1850        else:
1851            self.EnablePlot = True
1852            self.PatternTree.Expand(Id)
1853            self.PatternTree.SelectItem(Id)
1854           
1855        if not newHistList: return # somehow, no new histograms
1856        return # success
1857
1858    def OnMacroRecordStatus(self,event,setvalue=None):
1859        '''Called when the record macro menu item is used which toggles the
1860        value. Alternately a value to be set can be provided. Note that this
1861        routine is made more complex because on the Mac there are lots of menu
1862        items (listed in self.MacroStatusList) and this loops over all of them.
1863        '''
1864        nextvalue = log.ShowLogStatus() != True
1865        if setvalue is not None:
1866            nextvalue = setvalue
1867        if nextvalue:
1868            log.LogOn()
1869            set2 = True
1870        else:
1871            log.LogOff()
1872            set2 = False
1873        for menuitem in self.MacroStatusList:
1874            menuitem.Check(set2)
1875
1876    def _init_Macro(self):
1877        '''Define the items in the macro menu.
1878        '''
1879        menu = self.MacroMenu
1880        item = menu.Append(
1881                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
1882                kind=wx.ITEM_CHECK,text='Record actions')
1883        self.MacroStatusList.append(item)
1884        item.Check(log.ShowLogStatus())
1885        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
1886
1887        # this may only be of value for development work
1888        item = menu.Append(
1889            help='Show logged commands', id=wx.ID_ANY,
1890            kind=wx.ITEM_NORMAL,text='Show log')
1891        def OnShowLog(event):
1892            print 70*'='
1893            print 'List of logged actions'
1894            for i,line in enumerate(log.G2logList):
1895                if line: print i,line
1896            print 70*'='
1897        self.Bind(wx.EVT_MENU, OnShowLog, item)
1898
1899        item = menu.Append(
1900            help='Clear logged commands', id=wx.ID_ANY,
1901            kind=wx.ITEM_NORMAL,text='Clear log')
1902        def OnClearLog(event): log.G2logList=[None]
1903        self.Bind(wx.EVT_MENU, OnClearLog, item)
1904       
1905        item = menu.Append(
1906            help='Save logged commands to file', id=wx.ID_ANY,
1907            kind=wx.ITEM_NORMAL,text='Save log')
1908        def OnSaveLog(event):
1909            import cPickle
1910            defnam = os.path.splitext(
1911                os.path.split(self.GSASprojectfile)[1]
1912                )[0]+'.gcmd'
1913            dlg = wx.FileDialog(self,
1914                'Choose an file to save past actions', '.', defnam, 
1915                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1916                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1917            dlg.CenterOnParent()
1918            try:
1919                if dlg.ShowModal() == wx.ID_OK:
1920                    filename = dlg.GetPath()
1921                    # make sure extension is correct
1922                    filename = os.path.splitext(filename)[0]+'.gcmd'
1923                else:
1924                    filename = None
1925            finally:
1926                dlg.Destroy()
1927            if filename:
1928                fp = open(filename,'wb')
1929                fp.write(str(len(log.G2logList)-1)+'\n')
1930                for item in log.G2logList:
1931                    if item: cPickle.dump(item,fp)
1932                fp.close()
1933        self.Bind(wx.EVT_MENU, OnSaveLog, item)
1934
1935        item = menu.Append(
1936            help='Load logged commands from file', id=wx.ID_ANY,
1937            kind=wx.ITEM_NORMAL,text='Load log')
1938        def OnLoadLog(event):
1939            # this appends. Perhaps we should ask to clear?
1940            import cPickle
1941            defnam = os.path.splitext(
1942                os.path.split(self.GSASprojectfile)[1]
1943                )[0]+'.gcmd'
1944            dlg = wx.FileDialog(self,
1945                'Choose an file to read saved actions', '.', defnam, 
1946                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1947                wx.OPEN)
1948            dlg.CenterOnParent()
1949            try:
1950                if dlg.ShowModal() == wx.ID_OK:
1951                    filename = dlg.GetPath()
1952                    # make sure extension is correct
1953                    filename = os.path.splitext(filename)[0]+'.gcmd'
1954                else:
1955                    filename = None
1956            finally:
1957                dlg.Destroy()
1958            if filename and os.path.exists(filename):
1959                fp = open(filename,'rb')
1960                lines = fp.readline()
1961                for i in range(int(lines)):
1962                    log.G2logList.append(cPickle.load(fp))
1963                fp.close()
1964        self.Bind(wx.EVT_MENU, OnLoadLog, item)
1965
1966        item = menu.Append(
1967            help='Replay saved commands', id=wx.ID_ANY,
1968            kind=wx.ITEM_NORMAL,text='Replay log')
1969        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
1970
1971    def _init_Exports(self,menu):
1972        '''Find exporter routines and add them into menus
1973        '''
1974        # set up the top-level menus
1975        projectmenu = wx.Menu()
1976        item = menu.AppendMenu(
1977            wx.ID_ANY, 'Entire project as',
1978            projectmenu, help='Export entire project')
1979
1980        phasemenu = wx.Menu()
1981        item = menu.AppendMenu(
1982            wx.ID_ANY, 'Phase as',
1983            phasemenu, help='Export phase or sometimes phases')
1984
1985        powdermenu = wx.Menu()
1986        item = menu.AppendMenu(
1987            wx.ID_ANY, 'Powder data as',
1988            powdermenu, help='Export powder diffraction histogram(s)')
1989
1990        singlemenu = wx.Menu()
1991        item = menu.AppendMenu(
1992            wx.ID_ANY, 'Single crystal data as',
1993            singlemenu, help='Export single crystal histogram(s)')
1994
1995        imagemenu = wx.Menu()
1996        item = menu.AppendMenu(
1997            wx.ID_ANY, 'Image data as',
1998            imagemenu, help='Export powder image(s) data')
1999
2000        mapmenu = wx.Menu()
2001        item = menu.AppendMenu(
2002            wx.ID_ANY, 'Maps as',
2003            mapmenu, help='Export density map(s)')
2004
2005        # pdfmenu = wx.Menu()
2006        # item = menu.AppendMenu(
2007        #     wx.ID_ANY, 'PDFs as',
2008        #     pdfmenu, help='Export pair distribution function(s)')
2009
2010        # find all the exporter files
2011        pathlist = sys.path
2012        filelist = []
2013        for path in pathlist:
2014            for filename in glob.iglob(os.path.join(path,"G2export*.py")):
2015                filelist.append(filename)   
2016        filelist = sorted(list(set(filelist))) # remove duplicates
2017        self.exporterlist = []
2018        # go through the routines and import them, saving objects that
2019        # have export routines (method Exporter)
2020        for filename in filelist:
2021            path,rootname = os.path.split(filename)
2022            pkg = os.path.splitext(rootname)[0]
2023            try:
2024                fp = None
2025                fp, fppath,desc = imp.find_module(pkg,[path,])
2026                pkg = imp.load_module(pkg,fp,fppath,desc)
2027                for clss in inspect.getmembers(pkg): # find classes defined in package
2028                    if clss[0].startswith('_'): continue
2029                    if inspect.isclass(clss[1]):
2030                        # check if we have the required methods
2031                        for m in 'Exporter','loadParmDict':
2032                            if not hasattr(clss[1],m): break
2033                            if not callable(getattr(clss[1],m)): break
2034                        else:
2035                            exporter = clss[1](self) # create an export instance
2036                            self.exporterlist.append(exporter)
2037            except AttributeError:
2038                print 'Import_'+errprefix+': Attribute Error'+str(filename)
2039                pass
2040            except ImportError:
2041                print 'Import_'+errprefix+': Error importing file'+str(filename)
2042                pass
2043            if fp: fp.close()
2044        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
2045        for obj in self.exporterlist:
2046            #print 'exporter',obj
2047            for typ in obj.exporttype:
2048                if typ == "project":
2049                    submenu = projectmenu
2050                elif typ == "phase":
2051                    submenu = phasemenu
2052                elif typ == "powder":
2053                    submenu = powdermenu
2054                elif typ == "single":
2055                    submenu = singlemenu
2056                elif typ == "image":
2057                    submenu = imagemenu
2058                elif typ == "map":
2059                    submenu = mapmenu
2060                # elif typ == "pdf":
2061                #     submenu = pdfmenu
2062                else:
2063                    print("Error, unknown type in "+str(obj))
2064                    break
2065                item = submenu.Append(
2066                    wx.ID_ANY,
2067                    help=obj.longFormatName,
2068                    kind=wx.ITEM_NORMAL,
2069                    text=obj.formatName)
2070                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
2071                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
2072        item = imagemenu.Append(wx.ID_ANY,
2073                        help='Export image controls and masks for multiple images',
2074                        kind=wx.ITEM_NORMAL,
2075                        text='Multiple image controls and masks')
2076        self.Bind(wx.EVT_MENU, self.OnSaveMultipleImg, id=item.GetId())
2077        #code to debug an Exporter. hard-code the routine below, to allow a reload before use
2078        # def DebugExport(event):
2079        #      print 'start reload'
2080        #      reload(G2IO)
2081        #      import G2export_pwdr as dev
2082        #      reload(dev)
2083        #      dev.ExportPowderFXYE(self).Exporter(event)
2084        # item = menu.Append(
2085        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2086        #     help="debug exporter",text="test Export FXYE")
2087        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
2088        # # #self.ExportLookup[item.GetId()] = 'image'
2089        # self.ExportLookup[item.GetId()] = 'powder'
2090
2091    def _Add_ExportMenuItems(self,parent):
2092        # item = parent.Append(
2093        #     help='Select PWDR item to enable',id=wx.ID_ANY,
2094        #     kind=wx.ITEM_NORMAL,
2095        #     text='Export Powder Patterns...')
2096        # self.ExportPattern.append(item)
2097        # item.Enable(False)
2098        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
2099
2100        item = parent.Append(
2101            help='',id=wx.ID_ANY,
2102            kind=wx.ITEM_NORMAL,
2103            text='Export All Peak Lists...')
2104        self.ExportPeakList.append(item)
2105        item.Enable(True)
2106        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
2107
2108        item = parent.Append(
2109            help='',id=wx.ID_ANY,
2110            kind=wx.ITEM_NORMAL,
2111            text='Export HKLs...')
2112        self.ExportHKL.append(item)
2113        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
2114
2115        item = parent.Append(
2116            help='Select PDF item to enable',
2117            id=wx.ID_ANY,
2118            kind=wx.ITEM_NORMAL,
2119            text='Export PDF...')
2120        self.ExportPDF.append(item)
2121        item.Enable(False)
2122        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
2123
2124    def FillMainMenu(self,menubar):
2125        '''Define contents of the main GSAS-II menu for the (main) data tree window.
2126        For the mac, this is also called for the data item windows as well so that
2127        the main menu items are data menu as well.
2128        '''
2129        File = wx.Menu(title='')
2130        menubar.Append(menu=File, title='&File')
2131        self._Add_FileMenuItems(File)
2132        Data = wx.Menu(title='')
2133        menubar.Append(menu=Data, title='Data')
2134        self._Add_DataMenuItems(Data)
2135        Calculate = wx.Menu(title='')       
2136        menubar.Append(menu=Calculate, title='&Calculate')
2137        self._Add_CalculateMenuItems(Calculate)
2138        Import = wx.Menu(title='')       
2139        menubar.Append(menu=Import, title='Import')
2140        self._Add_ImportMenu_Image(Import)
2141        self._Add_ImportMenu_Phase(Import)
2142        self._Add_ImportMenu_powder(Import)
2143        self._Add_ImportMenu_Sfact(Import)
2144        self._Add_ImportMenu_smallangle(Import)
2145
2146        #======================================================================
2147        # Code to help develop/debug an importer, much is hard-coded below
2148        # but module is reloaded before each use, allowing faster testing
2149        # def DebugImport(event):
2150        #     print 'start reload'
2151        #     import G2phase_ISO as dev
2152        #     reload(dev)
2153        #     rd = dev.ISODISTORTPhaseReader()
2154        #     self.ImportMenuId[event.GetId()] = rd
2155        #     self.OnImportPhase(event)
2156            # or ----------------------------------------------------------------------
2157            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
2158            # special debug code
2159            # or ----------------------------------------------------------------------
2160            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
2161            # fp = open(filename,'Ur')
2162            # if not rd.ContentsValidator(fp):
2163            #     print 'not validated'
2164            #     # make a list of used phase ranId's
2165            # phaseRIdList = []
2166            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2167            # if sub:
2168            #     item, cookie = self.PatternTree.GetFirstChild(sub)
2169            #     while item:
2170            #         phaseName = self.PatternTree.GetItemText(item)
2171            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2172            #         if ranId: phaseRIdList.append(ranId)
2173            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2174            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2175            #     print 'read OK'
2176        # item = Import.Append(
2177        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2178        #     help="debug importer",text="test importer")
2179        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2180        #======================================================================
2181        self.ExportMenu = wx.Menu(title='')
2182        menubar.Append(menu=self.ExportMenu, title='Export')
2183        self._init_Exports(self.ExportMenu)
2184        self._Add_ExportMenuItems(self.ExportMenu)
2185        if GSASIIpath.GetConfigValue('Enable_logging'):
2186            self.MacroMenu = wx.Menu(title='')
2187            menubar.Append(menu=self.MacroMenu, title='Macro')
2188            self._init_Macro()
2189        HelpMenu=G2G.MyHelp(self,helpType='Data tree',
2190            morehelpitems=[
2191                           ('&Tutorials','Tutorials'), 
2192                           ])
2193        menubar.Append(menu=HelpMenu,title='&Help')
2194           
2195    def _init_ctrls(self, parent):
2196        wx.Frame.__init__(self, name='GSASII', parent=parent,
2197            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2198        clientSize = wx.ClientDisplayRect()
2199        Size = self.GetSize()
2200        xPos = clientSize[2]-Size[0]
2201        self.SetPosition(wx.Point(xPos,clientSize[1]))
2202        self._init_Imports()
2203        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2204        self.MakePDF = []
2205        self.Refine = []
2206        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2207        #self.ExportPattern = []
2208        self.ExportPeakList = []
2209        self.ExportHKL = []
2210        self.ExportPDF = []
2211        self.ExportPhase = []
2212        self.ExportCIF = []
2213        #
2214        self.GSASIIMenu = wx.MenuBar()
2215        # create a list of all dataframe menus (appended in PrefillDataMenu)
2216        self.dataMenuBars = [self.GSASIIMenu]
2217        self.MacroStatusList = []
2218        self.FillMainMenu(self.GSASIIMenu)
2219        self.SetMenuBar(self.GSASIIMenu)
2220        self.Bind(wx.EVT_SIZE, self.OnSize)
2221        self.Status = self.CreateStatusBar()
2222        self.mainPanel = wx.Panel(self,-1)
2223       
2224        wxID_PATTERNTREE = wx.NewId()
2225        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2226        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2227            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2228        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnPatternTreeSelChanged)
2229        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2230            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2231        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2232            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2233        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2234            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2235        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2236            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2237        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2238            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2239        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2240            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2241        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2242        self.root = self.PatternTree.root
2243        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2244            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2245        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
2246        plotFrame.Show()
2247       
2248        self.dataDisplay = None
2249       
2250    def __init__(self, parent):
2251        self.ExportLookup = {}
2252        self._init_ctrls(parent)
2253        self.Image = wx.Image(
2254            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2255            wx.BITMAP_TYPE_ICO)
2256        if "wxMSW" in wx.PlatformInfo:
2257            img = self.Image.Scale(16, 16).ConvertToBitmap()
2258        elif "wxGTK" in wx.PlatformInfo:
2259            img = self.Image.Scale(22, 22).ConvertToBitmap()
2260        else:
2261            img = self.Image.ConvertToBitmap()
2262        self.SetIcon(wx.IconFromBitmap(img))
2263        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2264        # various defaults
2265        self.oldFocus = None
2266        self.GSASprojectfile = ''
2267        self.undofile = ''
2268        self.TreeItemDelete = False
2269        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2270        self.Weight = False
2271        self.IfPlot = False
2272        self.DDShowAll = False
2273        self.atmSel = ''
2274        self.PatternId = 0
2275        self.PickId = 0
2276        self.PickIdText = None
2277        self.PeakTable = []
2278        self.LimitsTable = []
2279        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2280        self.HKL = []
2281        self.Lines = []
2282        self.itemPicked = None
2283        self.dataFrame = None
2284        self.Interpolate = 'nearest'
2285        self.ContourColor = 'Paired'
2286        self.VcovColor = 'RdYlGn'
2287        self.RamaColor = 'Blues'
2288        self.Projection = 'equal area'
2289        self.logPlot = False
2290        self.plusPlot = True
2291        self.sqPlot = False
2292        self.ErrorBars = False
2293        self.Contour = False
2294        self.Legend = False
2295        self.SinglePlot = True
2296        self.SubBack = False
2297        self.seqReverse = False
2298        self.seqLines = True #draw lines between points
2299        self.plotView = 0
2300        self.Image = 0
2301        self.oldImagefile = '' # the name of the last image file read
2302        self.oldImageTag = None # the name of the tag for multi-image files
2303        self.ImageZ = []  # this contains the image plotted and used for integration
2304        # self.ImageZ and self.oldImagefile are set in GSASIIplot.PlotImage
2305        # and GSASIIIO.GetImageData
2306        # any changes to self.ImageZ should initialize self.oldImagefile to force a reread
2307        self.Integrate = 0
2308        self.imageDefault = {}
2309        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2310        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2311        self.autoIntFrame = None
2312        self.IntegratedList = [] # list of already integrated IMG tree items
2313        self.Sngl = False
2314        self.ifGetRing = False
2315        self.MaskKey = ''           #trigger for making image masks
2316        self.StrainKey = ''         #ditto for new strain d-zeros
2317        self.EnablePlot = True
2318        self.hist = ''              # selected histogram in Phase/Data tab
2319        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
2320        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2321        self.LastImportDir = None # last-used directory where an import was done
2322        self.LastGPXdir = None    # directory where a GPX file was last read
2323        self.LastExportDir = None  # the last directory used for exports, if any.
2324       
2325        arg = sys.argv
2326        if len(arg) > 1 and arg[1]:
2327            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2328            self.dirname = os.path.dirname(arg[1])
2329            if self.dirname: os.chdir(self.dirname)
2330            try:
2331                self.StartProject()         #open the file if possible
2332                return
2333            except Exception:
2334                print 'Error opening or reading file',arg[1]
2335                import traceback
2336                print traceback.format_exc()
2337               
2338        if GSASIIpath.GetConfigValue('Starting_directory'):
2339            try:
2340                pth = GSASIIpath.GetConfigValue('Starting_directory')
2341                pth = os.path.expanduser(pth) 
2342                os.chdir(pth)
2343                self.LastGPXdir = pth
2344            except:
2345                print('Ignoring Config Starting_directory value: '+
2346                      GSASIIpath.GetConfigValue('Starting_directory'))
2347
2348    def GetTreeItemsList(self,item):
2349        return self.PatternTree._getTreeItemsList(item)
2350
2351    def OnSize(self,event):
2352        'Called when the main window is resized. Not sure why'
2353        w,h = self.GetClientSizeTuple()
2354        self.mainPanel.SetSize(wx.Size(w,h))
2355        self.PatternTree.SetSize(wx.Size(w,h))
2356                       
2357    def OnPatternTreeSelChanged(self, event):
2358        '''Called when a data tree item is selected'''
2359        if self.TreeItemDelete:
2360            self.TreeItemDelete = False
2361        else:
2362            pltNum = self.G2plotNB.nb.GetSelection()
2363            if pltNum >= 0:                         #to avoid the startup with no plot!
2364                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2365                pltPlot = pltPage.figure
2366            item = event.GetItem()
2367            G2gd.MovePatternTreeToGrid(self,item)
2368            if self.oldFocus:
2369                self.oldFocus.SetFocus()
2370       
2371    def OnPatternTreeItemCollapsed(self, event):
2372        'Called when a tree item is collapsed - all children will be collapsed'
2373        self.PatternTree.CollapseAllChildren(event.GetItem())
2374
2375    def OnPatternTreeItemExpanded(self, event):
2376        'Called when a tree item is expanded'
2377        self.OnPatternTreeSelChanged(event)
2378        event.Skip()
2379       
2380    def OnPatternTreeItemDelete(self, event):
2381        'Called when a tree item is deleted -- not sure what this does'
2382        self.TreeItemDelete = True
2383
2384    def OnPatternTreeItemActivated(self, event):
2385        'Called when a tree item is activated'
2386        event.Skip()
2387       
2388    def OnPatternTreeBeginRDrag(self,event):
2389        event.Allow()
2390        self.BeginDragId = event.GetItem()
2391        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2392        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2393        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2394        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2395        while item:     #G2 data tree has no sub children under a child of a tree item
2396            name = self.PatternTree.GetItemText(item)
2397            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2398            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2399       
2400    def OnPatternTreeEndDrag(self,event):
2401        event.Allow()
2402        self.EndDragId = event.GetItem()
2403        try:
2404            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2405        except:
2406            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2407            NewParent = self.root
2408        if self.ParentId != NewParent:
2409            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2410        else:
2411            Name,Item = self.DragData[0]
2412            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2413            self.PatternTree.SetItemPyData(NewId,Item)
2414            for name,item in self.DragData[1:]:     #loop over children
2415                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2416                self.PatternTree.SetItemPyData(Id,item)
2417            self.PatternTree.Delete(self.BeginDragId)
2418            G2gd.MovePatternTreeToGrid(self,NewId)
2419       
2420    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2421        'Allows stepping through the tree with the up/down arrow keys'
2422        self.oldFocus = wx.Window.FindFocus()
2423        keyevt = event.GetKeyEvent()
2424        key = event.GetKeyCode()
2425        item = self.PatternTree.GetSelection()
2426        if type(item) is int: return # is this the toplevel in tree?
2427        name = self.PatternTree.GetItemText(item)
2428        parent = self.PatternTree.GetItemParent(item)
2429        if key == wx.WXK_UP:
2430            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2431                if type(parent) is int: return # is this the toplevel in tree?
2432                prev = self.PatternTree.GetPrevSibling(parent)
2433                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2434                if NewId:
2435                    self.PatternTree.Collapse(parent)
2436                    self.PatternTree.Expand(prev)
2437                    self.oldFocus = wx.Window.FindFocus()
2438                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2439                else:
2440                    wx.CallAfter(self.PatternTree.SelectItem,item)
2441            else:   
2442                self.PatternTree.GetPrevSibling(item)
2443                self.PatternTree.SelectItem(item)
2444        elif key == wx.WXK_DOWN:
2445            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2446                next = self.PatternTree.GetNextSibling(parent)
2447                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2448                if NewId:
2449                    self.PatternTree.Collapse(parent)
2450                    self.PatternTree.Expand(next)
2451                    self.oldFocus = wx.Window.FindFocus()
2452                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2453                else:
2454                    wx.CallAfter(self.PatternTree.SelectItem,item)
2455            else:   
2456                self.PatternTree.GetNextSibling(item)
2457                self.PatternTree.SelectItem(item)
2458               
2459    def OnReadPowderPeaks(self,event):
2460        'Bound to menu Data/Read Powder Peaks'
2461        Cuka = 1.54052
2462        self.CheckNotebook()
2463        pth = G2G.GetImportPath(self)
2464        if not pth: pth = '.'
2465        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2466            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2467        try:
2468            if dlg.ShowModal() == wx.ID_OK:
2469                self.HKL = []
2470                self.powderfile = dlg.GetPath()
2471                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2472                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2473                data = ['PKS',wave,0.0]
2474                names = ['Type','Lam','Zero'] 
2475                codes = [0,0,0]
2476                inst = [G2IO.makeInstDict(names,data,codes),{}]
2477                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2478                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2479                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2480                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2481                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2482                self.PatternTree.Expand(Id)
2483                self.PatternTree.SelectItem(Id)
2484                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2485        finally:
2486            dlg.Destroy()
2487                       
2488    def OnImageRead(self,event):
2489        '''Called to read in an image in any known format. *** Depreciated. ***
2490        Note: When removed, G2IO.ReadLoadImage can also be removed
2491        '''
2492        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2493        self.CheckNotebook()
2494        dlg = wx.FileDialog(
2495            self, 'Choose image files', '.', '',
2496            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png)|'
2497            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png;*.zip|'
2498            'European detector file (*.edf)|*.edf|'
2499            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2500            'MAR file (*.mar*)|*.mar*|'
2501            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2502            'ADSC Image (*.img)|*.img|'
2503            'Rigaku R-Axis IV (*.stl)|*.stl|'
2504            'GSAS-II Image (*.G2img)|*.G2img|'
2505            'Portable Network Graphics image (*.png)|*.png|'
2506            'Zip archive (*.zip)|*.zip|'
2507            'All files (*.*)|*.*',
2508            wx.OPEN | wx.MULTIPLE)
2509        try:
2510            if dlg.ShowModal() == wx.ID_OK:
2511                imagefiles = dlg.GetPaths()
2512                imagefiles.sort()
2513                for imagefile in imagefiles:
2514                    G2IO.ReadLoadImage(imagefile,self)
2515                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2516                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,self.Image,'Image Controls'))             #show last image to be read
2517        finally:
2518            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2519            os.chdir(path)
2520            dlg.Destroy()
2521
2522    def CheckNotebook(self):
2523        '''Make sure the data tree has the minimally expected controls.
2524        '''
2525        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2526            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2527            self.PatternTree.SetItemPyData(sub,[''])
2528        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2529            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2530            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2531        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2532            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2533            self.PatternTree.SetItemPyData(sub,{})
2534        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2535            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2536            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2537        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2538            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2539            self.PatternTree.SetItemPyData(sub,{})
2540        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2541            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2542            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2543                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2544               
2545    class CopyDialog(wx.Dialog):
2546        '''Creates a dialog for copying control settings between
2547        data tree items'''
2548        def __init__(self,parent,title,text,data):
2549            wx.Dialog.__init__(self,parent,-1,title, 
2550                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2551            self.data = data
2552            panel = wx.Panel(self)
2553            mainSizer = wx.BoxSizer(wx.VERTICAL)
2554            topLabl = wx.StaticText(panel,-1,text)
2555            mainSizer.Add((10,10),1)
2556            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2557            mainSizer.Add((10,10),1)
2558            ncols = len(data)/40+1
2559            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2560            for id,item in enumerate(self.data):
2561                ckbox = wx.CheckBox(panel,id,item[1])
2562                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2563                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2564            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2565            OkBtn = wx.Button(panel,-1,"Ok")
2566            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2567            cancelBtn = wx.Button(panel,-1,"Cancel")
2568            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2569            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2570            btnSizer.Add((20,20),1)
2571            btnSizer.Add(OkBtn)
2572            btnSizer.Add((20,20),1)
2573            btnSizer.Add(cancelBtn)
2574            btnSizer.Add((20,20),1)
2575           
2576            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2577            panel.SetSizer(mainSizer)
2578            panel.Fit()
2579            self.Fit()
2580       
2581        def OnCopyChange(self,event):
2582            id = event.GetId()
2583            self.data[id][0] = self.FindWindowById(id).GetValue()       
2584           
2585        def OnOk(self,event):
2586            parent = self.GetParent()
2587            parent.Raise()
2588            self.EndModal(wx.ID_OK)             
2589           
2590        def OnCancel(self,event):
2591            parent = self.GetParent()
2592            parent.Raise()
2593            self.EndModal(wx.ID_CANCEL)             
2594           
2595        def GetData(self):
2596            return self.data
2597       
2598    class SumDialog(wx.Dialog):
2599        'Allows user to supply scale factor(s) when summing data'
2600        def __init__(self,parent,title,text,dataType,data):
2601            wx.Dialog.__init__(self,parent,-1,title, 
2602                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2603            self.data = data
2604            panel = wx.Panel(self)
2605            mainSizer = wx.BoxSizer(wx.VERTICAL)
2606            topLabl = wx.StaticText(panel,-1,text)
2607            mainSizer.Add((10,10),1)
2608            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2609            mainSizer.Add((10,10),1)
2610            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2611            for id,item in enumerate(self.data[:-1]):
2612                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2613                name.SetEditable(False)
2614                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2615                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2616                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2617                dataGridSizer.Add(scale,0,wx.LEFT,10)
2618                dataGridSizer.Add(name,0,wx.RIGHT,10)
2619            if dataType:
2620                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2621                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2622                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2623                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2624                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2625                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2626            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2627            OkBtn = wx.Button(panel,-1,"Ok")
2628            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2629            cancelBtn = wx.Button(panel,-1,"Cancel")
2630            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2631            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2632            btnSizer.Add((20,20),1)
2633            btnSizer.Add(OkBtn)
2634            btnSizer.Add((20,20),1)
2635            btnSizer.Add(cancelBtn)
2636            btnSizer.Add((20,20),1)
2637           
2638            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2639            panel.SetSizer(mainSizer)
2640            panel.Fit()
2641            self.Fit()
2642
2643        def OnScaleChange(self,event):
2644            id = event.GetId()
2645            value = self.FindWindowById(id).GetValue()
2646            try:
2647                self.data[id][0] = float(value)
2648                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2649            except ValueError:
2650                if value and '-' not in value[0]:
2651                    print 'bad input - numbers only'
2652                    self.FindWindowById(id).SetValue('0.000')
2653           
2654        def OnNameChange(self,event):
2655            self.data[-1] = self.name.GetValue() 
2656           
2657        def OnOk(self,event):
2658            parent = self.GetParent()
2659            parent.Raise()
2660            self.EndModal(wx.ID_OK)             
2661           
2662        def OnCancel(self,event):
2663            parent = self.GetParent()
2664            parent.Raise()
2665            self.EndModal(wx.ID_CANCEL)             
2666           
2667        def GetData(self):
2668            return self.data
2669                       
2670    def OnPwdrSum(self,event):
2671        'Sum together powder data(?)'
2672        TextList = []
2673        DataList = []
2674        SumList = []
2675        Names = []
2676        Inst = None
2677        SumItemList = []
2678        Comments = ['Sum equals: \n']
2679        if self.PatternTree.GetCount():
2680            item, cookie = self.PatternTree.GetFirstChild(self.root)
2681            while item:
2682                name = self.PatternTree.GetItemText(item)
2683                Names.append(name)
2684                if 'PWDR' in name:
2685                    TextList.append([0.0,name])
2686                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2687                    if not Inst:
2688                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2689                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2690            if len(TextList) < 2:
2691                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2692                return
2693            TextList.append('default_sum_name')               
2694            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2695            try:
2696                if dlg.ShowModal() == wx.ID_OK:
2697                    lenX = 0
2698                    Xminmax = [0,0]
2699                    Xsum = []
2700                    Ysum = []
2701                    Vsum = []
2702                    result = dlg.GetData()
2703                    for i,item in enumerate(result[:-1]):
2704                        scale,name = item
2705                        data = DataList[i]
2706                        if scale:
2707                            Comments.append("%10.3f %s" % (scale,' * '+name))
2708                            x,y,w,yc,yb,yd = data   #numpy arrays!
2709                            v = 1./w
2710                            if lenX:
2711                                if lenX != len(x):
2712                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2713                                        '\nExpected:'+str(lenX)+ \
2714                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2715                                    return
2716                            else:
2717                                lenX = len(x)
2718                            if Xminmax[1]:
2719                                if Xminmax != [x[0],x[-1]]:
2720                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2721                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2722                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2723                                    return
2724                                else:
2725                                    for j,yi in enumerate(y):
2726                                         Ysum[j] += scale*yi
2727                                         Vsum[j] += abs(scale)*v[j]
2728                            else:
2729                                Xminmax = [x[0],x[-1]]
2730                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2731                                for j,yi in enumerate(y):
2732                                    Xsum.append(x[j])
2733                                    Ysum.append(scale*yi)
2734                                    Vsum.append(abs(scale*v[j]))
2735                    Wsum = 1./np.array(Vsum)
2736                    outname = 'PWDR '+result[-1]
2737                    Id = 0
2738                    if outname in Names:
2739                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2740                        try:
2741                            if dlg2.ShowModal() == wx.ID_OK:
2742                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2743                                self.PatternTree.Delete(Id)
2744                        finally:
2745                            dlg2.Destroy()
2746                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2747                    if Id:
2748                        Sample = G2pdG.SetDefaultSample()
2749                        valuesdict = {
2750                            'wtFactor':1.0,
2751                            'Dummy':False,
2752                            'ranId':ran.randint(0,sys.maxint),
2753                            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
2754                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2755                            }
2756                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2757                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2758                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2759                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2760                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2761                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2762                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2763                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2764                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2765                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2766                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2767                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2768                        self.PatternTree.SelectItem(Id)
2769                        self.PatternTree.Expand(Id)
2770            finally:
2771                dlg.Destroy()
2772
2773    def OnImageSum(self,event):
2774        'Sum together image data(?)'
2775        TextList = []
2776        DataList = []
2777        SumList = []
2778        Names = []
2779        Inst = []
2780        SumItemList = []
2781        Comments = ['Sum equals: \n']
2782        if self.PatternTree.GetCount():
2783            item, cookie = self.PatternTree.GetFirstChild(self.root)
2784            while item:
2785                name = self.PatternTree.GetItemText(item)
2786                Names.append(name)
2787                if 'IMG' in name:
2788                    TextList.append([0.0,name])
2789                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2790                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2791                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2792            if len(TextList) < 2:
2793                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2794                return
2795            TextList.append('default_sum_name')               
2796            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2797            try:
2798                if dlg.ShowModal() == wx.ID_OK:
2799                    imSize = 0
2800                    result = dlg.GetData()
2801                    First = True
2802                    Found = False
2803                    for i,item in enumerate(result[:-1]):
2804                        scale,name = item
2805                        if scale:
2806                            Found = True                               
2807                            Comments.append("%10.3f %s" % (scale,' * '+name))
2808                            Npix,imagefile,imagetag = DataList[i]
2809                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2810                            if First:
2811                                newImage = np.zeros_like(image)
2812                                First = False
2813                            if imSize:
2814                                if imSize != Npix:
2815                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2816                                        '\nExpected:'+str(imSize)+ \
2817                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2818                                    return
2819                                newImage = newImage+scale*image
2820                            else:
2821                                imSize = Npix
2822                                newImage = newImage+scale*image
2823                            del(image)
2824                    if not Found:
2825                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2826                        return
2827                       
2828                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2829                    outname = 'IMG '+result[-1]
2830                    Id = 0
2831                    if outname in Names:
2832                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2833                        try:
2834                            if dlg2.ShowModal() == wx.ID_OK:
2835                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2836                        finally:
2837                            dlg2.Destroy()
2838                    else:
2839                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2840                    if Id:
2841                        pth = G2G.GetExportPath(self)
2842                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth, '', 
2843                            'G2img files (*.G2img)|*.G2img', 
2844                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
2845                        if dlg.ShowModal() == wx.ID_OK:
2846                            newimagefile = dlg.GetPath()
2847                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2848                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2849                            Imax = np.amax(newImage)
2850                            Imin = np.amin(newImage)
2851                            newImage = []
2852                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2853                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2854                        del(newImage)
2855                        if self.imageDefault:
2856                            Data = copy.copy(self.imageDefault)
2857                        Data['showLines'] = True
2858                        Data['ring'] = []
2859                        Data['rings'] = []
2860                        Data['cutoff'] = 10
2861                        Data['pixLimit'] = 20
2862                        Data['ellipses'] = []
2863                        Data['calibrant'] = ''
2864                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2865                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2866                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2867                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2868                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2869                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2870                        self.PatternTree.SelectItem(Id)
2871                        self.PatternTree.Expand(Id)
2872                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2873                        self.Image = self.PickId
2874            finally:
2875                dlg.Destroy()
2876                     
2877    def OnAddPhase(self,event):
2878        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2879        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2880            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2881        else:
2882            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2883        PhaseName = ''
2884        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2885            style=wx.OK)
2886        if dlg.ShowModal() == wx.ID_OK:
2887            PhaseName = dlg.GetValue()
2888        dlg.Destroy()
2889        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2890        E,SGData = G2spc.SpcGroup('P 1')
2891        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2892       
2893    def OnDeletePhase(self,event):
2894        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2895        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2896        if self.dataFrame:
2897            self.dataFrame.Clear() 
2898        TextList = []
2899        DelList = []
2900        DelItemList = []
2901        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2902            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2903        else:
2904            return
2905        if sub:
2906            item, cookie = self.PatternTree.GetFirstChild(sub)
2907            while item:
2908                TextList.append(self.PatternTree.GetItemText(item))
2909                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2910            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2911            try:
2912                if dlg.ShowModal() == wx.ID_OK:
2913                    result = dlg.GetSelections()
2914                    for i in result: DelList.append([i,TextList[i]])
2915                    item, cookie = self.PatternTree.GetFirstChild(sub)
2916                    i = 0
2917                    while item:
2918                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2919                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2920                        i += 1
2921                    for item in DelItemList:
2922                        name = self.PatternTree.GetItemText(item)
2923                        self.PatternTree.Delete(item)
2924                        self.G2plotNB.Delete(name)
2925                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2926                    while item:
2927                        name = self.PatternTree.GetItemText(item)
2928                        if 'PWDR' in name:
2929                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2930                            refList = self.PatternTree.GetItemPyData(Id)
2931                            if len(refList):
2932                                for i,item in DelList:
2933                                    if item in refList:
2934                                        del(refList[item])
2935                            self.PatternTree.SetItemPyData(Id,refList)
2936                        elif 'HKLF' in name:
2937                            data = self.PatternTree.GetItemPyData(item)
2938                            data[0] = {}
2939                            self.PatternTree.SetItemPyData(item,data)
2940                           
2941                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2942            finally:
2943                dlg.Destroy()
2944               
2945    def OnRenameData(self,event):
2946        'Renames an existing phase. Called by Data/Rename Phase menu'
2947        name = self.PatternTree.GetItemText(self.PickId)     
2948        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2949            dataType = name[:name.index(' ')+1]                 #includes the ' '
2950            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2951                defaultValue=name[name.index(' ')+1:])
2952            try:
2953                if dlg.ShowModal() == wx.ID_OK:
2954                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2955            finally:
2956                dlg.Destroy()
2957       
2958    def GetFileList(self,fileType,skip=None):        #potentially useful?
2959        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2960        fileList = []
2961        Source = ''
2962        id, cookie = self.PatternTree.GetFirstChild(self.root)
2963        while id:
2964            name = self.PatternTree.GetItemText(id)
2965            if fileType in name:
2966                if id == skip:
2967                    Source = name
2968                else:
2969                    fileList.append([False,name,id])
2970            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2971        if skip:
2972            return fileList,Source
2973        else:
2974            return fileList
2975           
2976    def OnDataDelete(self, event):
2977        '''Delete one or more histograms from data tree. Called by the
2978        Data/DeleteData menu
2979        '''
2980        TextList = []
2981        DelList = []
2982        DelItemList = []
2983        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
2984        ifPWDR = False
2985        ifSASD = False
2986        ifIMG = False
2987        ifHKLF = False
2988        ifPDF = False
2989        if self.PatternTree.GetCount():
2990            item, cookie = self.PatternTree.GetFirstChild(self.root)
2991            while item:
2992                name = self.PatternTree.GetItemText(item)
2993                if name not in ['Notebook','Controls','Covariance','Constraints',
2994                    'Restraints','Phases','Rigid bodies','Sequential results']:
2995                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
2996                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
2997                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
2998                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
2999                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
3000                    TextList.append(name)
3001                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3002            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
3003            try:
3004                if dlg.ShowModal() == wx.ID_OK:
3005                    result = dlg.GetSelections()
3006                    for i in result: DelList.append(TextList[i])
3007                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3008                    while item:
3009                        itemName = self.PatternTree.GetItemText(item)
3010                        if itemName in DelList:
3011                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
3012                            elif 'SASD' in itemName: nItems['SASD'] -= 1
3013                            elif 'IMG' in itemName: nItems['IMG'] -= 1
3014                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
3015                            elif 'PDF' in itemName: nItems['PDF'] -= 1
3016                            DelItemList.append(item)
3017                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3018                    for item in DelItemList:
3019                        self.PatternTree.Delete(item)
3020                    self.PickId = 0
3021                    self.PickIdText = None
3022                    self.PatternId = 0
3023                    if nItems['PWDR']:
3024                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3025                    else:
3026                        self.G2plotNB.Delete('Powder Patterns')
3027                    if not nItems['IMG']:
3028                        self.G2plotNB.Delete('2D Powder Image')
3029                    if not nItems['HKLF']:
3030                        self.G2plotNB.Delete('Structure Factors')
3031                        if '3D Structure Factors' in self.G2plotNB.plotList:
3032                            self.G2plotNB.Delete('3D Structure Factors')
3033            finally:
3034                dlg.Destroy()
3035
3036    def OnFileOpen(self, event, filename=None):
3037        '''Gets a GSAS-II .gpx project file in response to the
3038        File/Open Project menu button
3039        '''
3040        result = wx.ID_OK
3041        self.EnablePlot = False
3042        if self.PatternTree.GetChildrenCount(self.root,False):
3043            if self.dataFrame:
3044                self.dataFrame.Clear() 
3045            dlg = wx.MessageDialog(
3046                self,
3047                'Do you want to overwrite the current project? '+
3048                'Any unsaved changes in current project will be lost. Press OK to continue.',
3049                'Overwrite?',  wx.OK | wx.CANCEL)
3050            try:
3051                result = dlg.ShowModal()
3052                if result == wx.ID_OK:
3053                    self.PatternTree.DeleteChildren(self.root)
3054                    self.GSASprojectfile = ''
3055                    self.HKL = []
3056                    if self.G2plotNB.plotList:
3057                        self.G2plotNB.clear()
3058            finally:
3059                dlg.Destroy()
3060        if result != wx.ID_OK: return
3061
3062        if not filename:
3063            if self.dataDisplay: self.dataDisplay.Destroy()
3064            if self.LastGPXdir:
3065                pth = self.LastGPXdir
3066            else:
3067                pth = '.'
3068            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3069                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3070            try:
3071                if dlg.ShowModal() != wx.ID_OK: return
3072                self.GSASprojectfile = dlg.GetPath()
3073                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3074                self.dirname = dlg.GetDirectory()
3075            finally:
3076                dlg.Destroy()
3077        else:
3078            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3079            self.dirname = os.path.split(filename)[0]
3080
3081        try:
3082            self.StartProject()         #open the file if possible
3083        except:
3084            print '\nError opening file ',filename
3085            import traceback
3086            print traceback.format_exc()
3087       
3088    def StartProject(self):
3089        '''Opens a GSAS-II project file & selects the 1st available data set to
3090        display (PWDR, HKLF or SASD)
3091        '''
3092       
3093        Id = 0
3094        G2IO.ProjFileOpen(self)
3095        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3096        self.PatternTree.Expand(self.root)
3097        self.HKL = []
3098        item, cookie = self.PatternTree.GetFirstChild(self.root)
3099        while item and not Id:
3100            name = self.PatternTree.GetItemText(item)
3101            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3102                Id = item
3103            elif name == 'Controls':
3104                data = self.PatternTree.GetItemPyData(item)
3105                if data:
3106                    for item in self.Refine: item.Enable(True)
3107                    self.EnableSeqRefineMenu()
3108            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3109        if Id:
3110            self.EnablePlot = True
3111            self.PatternTree.SelectItem(Id)
3112        self.CheckNotebook()
3113        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3114        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3115        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3116        self.LastGPXdir = pth
3117
3118    def OnFileClose(self, event):
3119        '''Clears the data tree in response to the
3120        File/New Project menu button. User is given option to save
3121        the project.
3122        '''
3123        if self.dataFrame:
3124            self.dataFrame.Clear()
3125            self.dataFrame.SetLabel('GSAS-II data display') 
3126        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3127        try:
3128            result = dlg.ShowModal()
3129            if result == wx.ID_OK:
3130                self.OnFileSaveMenu(event)
3131            if result != wx.ID_CANCEL:
3132                self.GSASprojectfile = ''
3133                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3134                self.PatternTree.DeleteChildren(self.root)
3135                if self.HKL: self.HKL = []
3136                if self.G2plotNB.plotList:
3137                    self.G2plotNB.clear()
3138        finally:
3139            dlg.Destroy()
3140
3141    def OnFileSave(self, event):
3142        '''Save the current project in response to the
3143        File/Save Project menu button
3144        '''
3145       
3146        if self.GSASprojectfile: 
3147            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3148            self.CheckNotebook()
3149            G2IO.ProjFileSave(self)
3150        else:
3151            self.OnFileSaveas(event)
3152
3153    def OnFileSaveas(self, event):
3154        '''Save the current project in response to the
3155        File/Save as menu button
3156        '''
3157        if GSASIIpath.GetConfigValue('Starting_directory'):
3158            pth = GSASIIpath.GetConfigValue('Starting_directory')
3159            pth = os.path.expanduser(pth) 
3160        elif self.LastGPXdir:
3161            pth = self.LastGPXdir
3162        else:
3163            pth = '.'
3164        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3165            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3166        try:
3167            if dlg.ShowModal() == wx.ID_OK:
3168                self.GSASprojectfile = dlg.GetPath()
3169                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3170                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3171                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3172                self.CheckNotebook()
3173                G2IO.ProjFileSave(self)
3174                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3175        finally:
3176            dlg.Destroy()
3177
3178    def ExitMain(self, event):
3179        '''Called if the main window is closed'''
3180        if self.G2plotNB:
3181            self.G2plotNB.Destroy()
3182        if self.dataFrame:
3183            self.dataFrame.Clear() 
3184            self.dataFrame.Destroy()
3185        if self.undofile:
3186            os.remove(self.undofile)
3187        sys.exit()
3188       
3189    def OnFileExit(self, event):
3190        '''Called in response to the File/Quit menu button'''
3191        if self.G2plotNB:
3192            self.G2plotNB.Destroy()
3193        if self.dataFrame:
3194            self.dataFrame.Clear() 
3195            self.dataFrame.Destroy()
3196        self.Close()
3197       
3198    def OnExportPeakList(self,event):
3199        nptand = lambda x: np.tan(x*math.pi/180.)
3200        pth = G2G.GetExportPath(self)
3201        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3202            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3203        try:
3204            if dlg.ShowModal() == wx.ID_OK:
3205                self.peaklistfile = dlg.GetPath()
3206                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3207                file = open(self.peaklistfile,'w')               
3208                item, cookie = self.PatternTree.GetFirstChild(self.root)
3209                while item:
3210                    name = self.PatternTree.GetItemText(item)
3211                    if 'PWDR' in name:
3212                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3213                        wave = 0.0
3214                        while item2:
3215                            name2 = self.PatternTree.GetItemText(item2)
3216                            if name2 == 'Instrument Parameters':
3217                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3218                                Type = Inst['Type'][0]
3219                                if 'T' not in Type:
3220                                    wave = G2mth.getWave(Inst)
3221                            elif name2 == 'Peak List':
3222                                pkdata = self.PatternTree.GetItemPyData(item2)
3223                                peaks = pkdata['peaks']
3224                                sigDict = pkdata['sigDict']
3225                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3226                        file.write("#%s \n" % (name+' Peak List'))
3227                        if wave:
3228                            file.write('#wavelength = %10.6f\n'%(wave))
3229                        if 'T' in Type:
3230                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3231                        else:
3232                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3233                        for ip,peak in enumerate(peaks):
3234                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3235                            if 'T' in Type:  #TOF - more cols
3236                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3237                                for name in esds.keys():
3238                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3239                                sig = np.sqrt(peak[8])
3240                                gam = peak[10]
3241                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3242                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3243                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3244                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3245                            else:               #CW
3246                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3247                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3248                                for name in esds.keys():
3249                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3250                                sig = np.sqrt(peak[4]) #var -> sig
3251                                gam = peak[6]
3252                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3253                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3254                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3255                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3256                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3257                file.close()
3258        finally:
3259            dlg.Destroy()
3260       
3261    def OnExportHKL(self,event):
3262        pth = G2G.GetExportPath(self)
3263        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3264            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3265        try:
3266            if dlg.ShowModal() == wx.ID_OK:
3267                self.peaklistfile = dlg.GetPath()
3268                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3269                file = open(self.peaklistfile,'w')               
3270                item, cookie = self.PatternTree.GetFirstChild(self.root)
3271                while item:
3272                    name = self.PatternTree.GetItemText(item)
3273                    if 'PWDR' in name:
3274                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3275                        while item2:
3276                            name2 = self.PatternTree.GetItemText(item2)
3277                            if name2 == 'Reflection Lists':
3278                                data = self.PatternTree.GetItemPyData(item2)
3279                                phases = data.keys()
3280                                for phase in phases:
3281                                    peaks = data[phase]
3282                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3283                                    if 'T' in peaks.get('Type','PXC'):
3284                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3285                                    else:               
3286                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3287                                    for peak in peaks['RefList']:
3288                                        if 'T' in peaks.get('Type','PXC'):
3289                                            sig = np.sqrt(peak[6])
3290                                            gam = peak[7]
3291                                            FWHM = G2pwd.getgamFW(gam,sig)
3292                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3293                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3294                                        else:
3295                                            sig = np.sqrt(peak[6])
3296                                            gam = peak[7]
3297                                            FWHM = G2pwd.getgamFW(gam,sig)
3298                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3299                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3300                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3301                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3302                file.close()
3303        finally:
3304            dlg.Destroy()
3305       
3306    def OnExportPDF(self,event):
3307        #need S(Q) and G(R) to be saved here - probably best from selection?
3308        names = ['All']
3309        exports = []
3310        item, cookie = self.PatternTree.GetFirstChild(self.root)
3311        while item:
3312            name = self.PatternTree.GetItemText(item)
3313            if 'PDF' in name:
3314                names.append(name)
3315            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3316        if names:
3317            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3318            if dlg.ShowModal() == wx.ID_OK:
3319                sel = dlg.GetSelections()
3320                if sel[0] == 0:
3321                    exports = names[1:]
3322                else:
3323                    for x in sel:
3324                        exports.append(names[x])
3325            dlg.Destroy()
3326        if exports:
3327            G2IO.PDFSave(self,exports)
3328       
3329    def OnMakePDFs(self,event):
3330        '''Calculates PDFs
3331        '''
3332        sind = lambda x: math.sin(x*math.pi/180.)
3333        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3334        TextList = ['All PWDR']
3335        PDFlist = []
3336        Names = []
3337        if self.PatternTree.GetCount():
3338            id, cookie = self.PatternTree.GetFirstChild(self.root)
3339            while id:
3340                name = self.PatternTree.GetItemText(id)
3341                Names.append(name)
3342                if 'PWDR' in name:
3343                    TextList.append(name)
3344                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3345            if len(TextList) == 1:
3346                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3347                return
3348            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3349            try:
3350                if dlg.ShowModal() == wx.ID_OK:
3351                    result = dlg.GetSelections()
3352                    for i in result: PDFlist.append(TextList[i])
3353                    if 0 in result:
3354                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3355                    for item in PDFlist:
3356                        PWDRname = item[4:]
3357                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3358                        Data = {
3359                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3360                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3361                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3362                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3363                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3364                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3365                            'Lorch':True,}
3366                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3367                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3368                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3369                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3370                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3371                for item in self.ExportPDF: item.Enable(True)
3372            finally:
3373                dlg.Destroy()
3374               
3375    def GetPWDRdatafromTree(self,PWDRname):
3376        ''' Returns powder data from GSASII tree
3377
3378        :param str PWDRname: a powder histogram name as obtained from
3379          :meth:`GSASIIstruct.GetHistogramNames`
3380
3381        :returns: PWDRdata = powder data dictionary with
3382          Powder data arrays, Limits, Instrument Parameters,
3383          Sample Parameters           
3384        '''
3385        PWDRdata = {}
3386        try:
3387            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3388        except ValueError:
3389            PWDRdata['wtFactor'] = 1.0
3390        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3391        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3392        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3393        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3394        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3395        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3396        if 'ranId' not in PWDRdata:  # patch, add a random Id
3397            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3398        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3399            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3400        return PWDRdata
3401
3402    def GetHKLFdatafromTree(self,HKLFname):
3403        ''' Returns single crystal data from GSASII tree
3404
3405        :param str HKLFname: a single crystal histogram name as obtained
3406          from
3407          :meth:`GSASIIstruct.GetHistogramNames`
3408
3409        :returns: HKLFdata = single crystal data list of reflections
3410
3411        '''
3412        HKLFdata = {}
3413        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3414#        try:
3415#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3416#        except ValueError:
3417#            HKLFdata['wtFactor'] = 1.0
3418        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3419        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3420        return HKLFdata
3421       
3422    def GetPhaseData(self):
3423        '''Returns a dict with defined phases.
3424        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3425        get same info from GPX file.
3426        '''
3427        phaseData = {}
3428        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3429            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3430        else:
3431            print 'no phases found in GetPhaseData'
3432            sub = None
3433        if sub:
3434            item, cookie = self.PatternTree.GetFirstChild(sub)
3435            while item:
3436                phaseName = self.PatternTree.GetItemText(item)
3437                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3438                if 'ranId' not in phaseData[phaseName]:
3439                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3440                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3441        return phaseData
3442
3443    def GetPhaseInfofromTree(self):
3444        '''Get the phase names and their rId values,
3445        also the histograms used in each phase.
3446
3447        :returns: (phaseRIdList, usedHistograms) where
3448
3449          * phaseRIdList is a list of random Id values for each phase
3450          * usedHistograms is a dict where the keys are the phase names
3451            and the values for each key are a list of the histogram names
3452            used in each phase.
3453        '''
3454        phaseRIdList = []
3455        usedHistograms = {}
3456        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3457        if sub:
3458            item, cookie = self.PatternTree.GetFirstChild(sub)
3459            while item:
3460                phaseName = self.PatternTree.GetItemText(item)
3461                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3462                if ranId: phaseRIdList.append(ranId)
3463                data = self.PatternTree.GetItemPyData(item)
3464                UseList = data['Histograms']
3465                usedHistograms[phaseName] = UseList.keys()
3466                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3467        return phaseRIdList,usedHistograms
3468
3469    def GetPhaseNames(self):
3470        '''Returns a list of defined phases.
3471        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3472        get same info from GPX file.
3473        '''
3474        phaseNames = []
3475        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3476            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3477        else:
3478            print 'no phases found in GetPhaseNames'
3479            sub = None
3480        if sub:
3481            item, cookie = self.PatternTree.GetFirstChild(sub)
3482            while item:
3483                phase = self.PatternTree.GetItemText(item)
3484                phaseNames.append(phase)
3485                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3486        return phaseNames
3487   
3488    def GetHistogramNames(self,hType):
3489        """ Returns a list of histogram names found in the GSASII data tree
3490        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3491        get same info from GPX file.
3492       
3493        :param str hType: list of histogram types
3494        :return: list of histogram names
3495       
3496        """
3497        HistogramNames = []
3498        if self.PatternTree.GetCount():
3499            item, cookie = self.PatternTree.GetFirstChild(self.root)
3500            while item:
3501                name = self.PatternTree.GetItemText(item)
3502                if name[:4] in hType:
3503                    HistogramNames.append(name)       
3504                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3505
3506        return HistogramNames
3507                   
3508    def GetUsedHistogramsAndPhasesfromTree(self):
3509        ''' Returns all histograms that are found in any phase
3510        and any phase that uses a histogram.
3511        This also assigns numbers to used phases and histograms by the
3512        order they appear in the file.
3513        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3514        get same info from GPX file.
3515
3516        :returns: (Histograms,Phases)
3517
3518            * Histograms = dictionary of histograms as {name:data,...}
3519            * Phases = dictionary of phases that use histograms
3520        '''
3521        Histograms = {}
3522        Phases = {}
3523        phaseNames = self.GetPhaseNames()
3524        phaseData = self.GetPhaseData()
3525        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3526
3527        for phase in phaseData:
3528            Phase = phaseData[phase]
3529            pId = phaseNames.index(phase)
3530            Phase['pId'] = pId
3531            if Phase['Histograms']:
3532                if phase not in Phases:
3533                    Phases[phase] = Phase
3534                for hist in Phase['Histograms']:
3535                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3536                        Phase['Histograms'][hist]['Use'] = True         
3537                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3538                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3539                        if item:
3540                            if 'PWDR' in hist[:4]: 
3541                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3542                            elif 'HKLF' in hist[:4]:
3543                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3544                            hId = histoList.index(hist)
3545                            Histograms[hist]['hId'] = hId
3546                        else: # would happen if a referenced histogram were renamed or deleted
3547                            print('For phase "'+str(phase)+
3548                                  '" unresolved reference to histogram "'+str(hist)+'"')
3549        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3550        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3551        return Histograms,Phases
3552       
3553    def MakeLSParmDict(self):
3554        '''Load all parameters used for computation from the tree into a
3555        dict of paired values [value, refine flag]. Note that this is
3556        different than the parmDict used in the refinement, which only has
3557        values.
3558
3559        Note that similar things are done in
3560        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3561        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3562        a GPX file).
3563
3564        :returns: (parmDict,varyList) where:
3565
3566         * parmDict is a dict with values and refinement flags
3567           for each parameter and
3568         * varyList is a list of variables (refined parameters).
3569        '''
3570        parmDict = {}
3571        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3572        for phase in Phases:
3573            if 'pId' not in Phases[phase]:
3574                self.ErrorDialog('View parameter error','You must run least squares at least once')
3575                raise Exception,'No pId for phase '+str(phase)
3576        rigidbodyDict = self.PatternTree.GetItemPyData(   
3577            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3578        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3579        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3580        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3581        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3582        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3583        varyList = rbVary+phaseVary+hapVary+histVary
3584        parmDict.update(rbDict)
3585        parmDict.update(phaseDict)
3586        parmDict.update(hapDict)
3587        parmDict.update(histDict)
3588        for parm in parmDict:
3589            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3590                'Omega','Chi','Phi','nDebye','nPeaks']:
3591                parmDict[parm] = [parmDict[parm],'-']
3592            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3593                parmDict[parm] = [parmDict[parm],'-']
3594            elif parm in varyList:
3595                parmDict[parm] = [parmDict[parm],'T']
3596            else:
3597                parmDict[parm] = [parmDict[parm],'F']
3598        # for i in parmDict: print i,'\t',parmDict[i]
3599        # fl = open('parmDict.dat','wb')
3600        # import cPickle
3601        # cPickle.dump(parmDict,fl,1)
3602        # fl.close()
3603        return parmDict,varyList
3604
3605    def ShowLSParms(self,event):
3606        '''Displays a window showing all parameters in the refinement.
3607        Called from the Calculate/View LS Parms menu.
3608        '''
3609        parmDict,varyList = self.MakeLSParmDict()
3610        parmValDict = {}
3611        for i in parmDict:
3612            parmValDict[i] = parmDict[i][0]
3613           
3614        reqVaryList = tuple(varyList) # save requested variables
3615        try:
3616            # process constraints
3617            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3618            Constraints = self.PatternTree.GetItemPyData(sub)
3619            constList = []
3620            for item in Constraints:
3621                if item.startswith('_'): continue
3622                constList += Constraints[item]
3623            G2mv.InitVars()
3624            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3625            groups,parmlist = G2mv.GroupConstraints(constrDict)
3626            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3627            G2mv.Map2Dict(parmValDict,varyList)
3628        except:
3629            pass
3630        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3631        dlg.ShowModal()
3632        dlg.Destroy()
3633       
3634    def OnRefine(self,event):
3635        '''Perform a refinement.
3636        Called from the Calculate/Refine menu.
3637        '''       
3638        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3639        if Id:
3640            dlg = wx.MessageDialog(
3641                self,
3642                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3643                'Remove sequential results?',wx.OK|wx.CANCEL)
3644            if dlg.ShowModal() == wx.ID_OK:
3645                self.PatternTree.Delete(Id)
3646                dlg.Destroy()
3647            else:
3648                dlg.Destroy()
3649                return
3650        self.OnFileSave(event)
3651        # check that constraints are OK here
3652        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3653        if errmsg:
3654            self.ErrorDialog('Refinement error',errmsg)
3655            return
3656        if warnmsg:
3657            print('Conflict between refinment flag settings and constraints:\n'+
3658                warnmsg+'\nRefinement not possible')
3659            self.ErrorDialog('Refinement Flag Error',
3660                'Conflict between refinement flag settings and constraints:\n'+
3661                warnmsg+'\nRefinement not possible')
3662            return
3663        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3664            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3665            parent=self)
3666        Size = dlg.GetSize()
3667        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3668            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3669        dlg.CenterOnParent()
3670        Rw = 100.00
3671        oldId =  self.PatternTree.GetSelection()        #retain current selection
3672        oldPath = self.GetTreeItemsList(oldId)
3673        parentName = ''
3674        oldName = self.PatternTree.GetItemText(oldId)
3675        parentId = self.PatternTree.GetItemParent(oldId)
3676        if parentId:
3677            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3678            if 'Phases' in parentName:
3679                tabId = self.dataDisplay.GetSelection()
3680        try:
3681            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)
3682        finally:
3683            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3684            dlg.Destroy()
3685            wx.Yield()
3686        if OK:
3687            Rw = Msg
3688            dlg2 = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3689            try:
3690                if dlg2.ShowModal() == wx.ID_OK:
3691                    Id = 0
3692                    self.PatternTree.DeleteChildren(self.root)
3693                    self.HKL = []
3694                    G2IO.ProjFileOpen(self)
3695                    Id =  self.root
3696                    txt = None
3697                    for txt in oldPath:
3698                        Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3699                    self.PickIdText = None  #force reload of page
3700                    self.PickId = Id
3701                    self.PatternTree.SelectItem(Id)
3702                    G2gd.MovePatternTreeToGrid(self,Id)
3703            finally:
3704                dlg2.Destroy()
3705        else:
3706            self.ErrorDialog('Refinement error',Msg)
3707
3708    def OnSeqRefine(self,event):
3709        '''Perform a sequential refinement.
3710        Called from the Calculate/Sequential refine menu.
3711        '''       
3712        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3713        if not Id:
3714            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3715            self.PatternTree.SetItemPyData(Id,{})           
3716        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3717        Controls['ShowCell'] = True
3718        self.OnFileSave(event)
3719        # check that constraints are OK here
3720        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3721        if errmsg:
3722            self.ErrorDialog('Refinement error',errmsg)
3723            return
3724        if warnmsg:
3725            print('Conflict between refinment flag settings and constraints:\n'+
3726                  warnmsg+'\nRefinement not possible')
3727            self.ErrorDialog('Refinement Flag Error',
3728                             'Conflict between refinment flag settings and constraints:\n'+
3729                             warnmsg+'\nRefinement not possible')
3730            return
3731        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3732            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3733            parent=self)           
3734        Size = dlg.GetSize()
3735        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3736            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3737        dlg.CenterOnParent()
3738        try:
3739            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3740        finally:
3741            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3742            dlg.Destroy()
3743            wx.Yield()
3744        if OK:
3745            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3746            try:
3747                if dlg.ShowModal() == wx.ID_OK:
3748                    Id = 0
3749                    self.PickIdText = None  #force reload of PickId contents
3750                    self.PatternTree.DeleteChildren(self.root)
3751                    if len(self.HKL): self.HKL = []
3752                    if self.G2plotNB.plotList:
3753                        self.G2plotNB.clear()
3754                    G2IO.ProjFileOpen(self)
3755                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3756                    self.PatternTree.SelectItem(Id)
3757   
3758            finally:
3759                dlg.Destroy()
3760        else:
3761            self.ErrorDialog('Sequential refinement error',Msg)
3762       
3763    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3764        'Display an error message'
3765        result = None
3766        if parent is None:
3767            dlg = wx.MessageDialog(self, message, title,  wtype)
3768        else:
3769            dlg = wx.MessageDialog(parent, message, title,  wtype)
3770            dlg.CenterOnParent() # not working on Mac
3771        try:
3772            result = dlg.ShowModal()
3773        finally:
3774            dlg.Destroy()
3775        return result
3776   
3777    def OnSaveMultipleImg(self,event):
3778        '''Select and save multiple image parameter and mask files
3779        '''
3780        G2IO.SaveMultipleImg(self)
3781       
3782class GSASIImain(wx.App):
3783    '''Defines a wxApp for GSAS-II
3784
3785    Creates a wx frame (self.main) which contains the display of the
3786    data tree.
3787    '''
3788    def OnInit(self):
3789        '''Called automatically when the app is created.'''
3790        import platform
3791        if '2.7' not in sys.version[:5]:
3792            dlg = wx.MessageDialog(None, 
3793                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
3794                'Python version error',  wx.OK)
3795            try:
3796                result = dlg.ShowModal()
3797            finally:
3798                dlg.Destroy()
3799            sys.exit()
3800        self.main = GSASII(None)
3801        self.main.Show()
3802        self.SetTopWindow(self.main)
3803        # save the current package versions
3804        self.main.PackageVersions = []
3805        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
3806        for p in (wx,mpl,np,sp,ogl):
3807            self.main.PackageVersions.append([p.__name__,p.__version__])
3808        try:
3809            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
3810        except:
3811            try:
3812                from PIL import PILLOW_VERSION
3813                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
3814            except:
3815                pass
3816        self.main.PackageVersions.append([' Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
3817       
3818        return True
3819    # def MacOpenFile(self, filename):
3820    #     '''Called on Mac every time a file is dropped on the app when it is running,
3821    #     treat this like a File/Open project menu action.
3822    #     Should be ignored on other platforms
3823    #     '''
3824    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3825    #     print 'MacOpen',filename
3826    #     if os.path.splitext(filename)[1] == '.py': return
3827    #     # end PATCH
3828    #     self.main.OnFileOpen(None,filename)
3829    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3830       
3831def main():
3832    '''Start up the GSAS-II application'''
3833    #application = GSASIImain() # don't redirect output, someday we
3834    # may want to do this if we can
3835    application = GSASIImain(0)
3836    if GSASIIpath.GetConfigValue('wxInspector'):
3837        import wx.lib.inspection as wxeye
3838        wxeye.InspectionTool().Show()
3839
3840    #application.main.OnRefine(None)
3841    application.MainLoop()
3842   
3843if __name__ == '__main__':
3844    # print versions
3845    print "Python module versions loaded:"
3846    print "python:     ",sys.version.split()[0]
3847    print "wxpython:   ",wx.__version__
3848    print "matplotlib: ",mpl.__version__
3849    print "numpy:      ",np.__version__
3850    print "scipy:      ",sp.__version__
3851    print "OpenGL:     ",ogl.__version__
3852    try:
3853        from PIL import Image
3854        try:
3855            from PIL import PILLOW_VERSION
3856            version = PILLOW_VERSION
3857        except:
3858            version = Image.VERSION
3859        print "pillow:     ",version
3860    except ImportError:
3861        try:
3862            import Image
3863            print "Image (PIL):",Image.VERSION
3864        except ImportError:
3865            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3866    try:
3867        import mkl
3868        print "Max threads ",mkl.get_max_threads()
3869    except:
3870        pass
3871    import platform
3872    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3873    #print "wxPython description",wx.PlatformInfo
3874    print "This is GSAS-II version:     ",__version__,' revision #'+str(GSASIIpath.GetVersionNumber())
3875    GSASIIpath.InvokeDebugOpts()
3876    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.