source: trunk/GSASII.py @ 2148

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

ReadPowderInstprm? modified to handle multibank instprm files. Fails if bank missing.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 180.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2016-02-13 19:56:05 +0000 (Sat, 13 Feb 2016) $
6# $Author: vondreele $
7# $Revision: 2148 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2148 2016-02-13 19:56:05Z 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: 2148 $")
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        if os.path.splitext(instfile)[1].lower() != '.instprm': # invalid file
946            return None           
947        if not os.path.exists(instfile): # no such file
948            return None
949        File = open(instfile,'r')
950        lines = File.readlines()
951        File.close()
952        return lines       
953           
954    def ReadPowderInstprm(self,instLines,bank):
955        '''Read lines from a GSAS-II (new) instrument parameter file
956        similar to G2pwdGUI.OnLoad
957        If instprm file has multiple banks each with header #Bank n: ..., this
958        finds matching bank no. to load - problem with nonmatches?
959
960        :param list instLines: strings from GSAS-II parameter file; can be concatenated with ';'
961        :param int  bank: bank number to check when instprm file has '#BANK n:...' strings
962            when bank = n then use parameters; otherwise skip that set. Ignored if BANK n:
963            not present. NB: this kind of instprm file made by a Save all profile command in Instrument Parameters
964
965        '''
966        if 'GSAS-II' not in instLines[0]: # not a valid file
967            return 'Not a valid GSAS-II instprm file'
968        newItems = []
969        newVals = []
970        Found = False
971        il = 0
972        while il < len(instLines):
973            S = instLines[il]
974            if S[0] == '#':
975                if Found:
976                    break
977                if 'Bank' in S:
978                    if bank == int(S.split(':')[0].split()[1]):
979                        il += 1
980                        S = instLines[il]
981                    else:
982                        il += 1
983                        S = instLines[il]
984                        while il < len(instLines) and '#Bank' not in S:
985                            il += 1
986                            if il == len(instLines):
987                                return 'Bank %d not found in .instprm file'%(bank)
988                            S = instLines[il]
989                        continue
990                else:   #a non #Bank file
991                    il += 1
992                    S = instLines[il]
993            Found = True
994            S = S.replace(' ','')
995            SS = S[:-1].split(';')
996            for s in SS:
997                [item,val] = s.split(':')
998                newItems.append(item)
999                try:
1000                    newVals.append(float(val))
1001                except ValueError:
1002                    newVals.append(val)
1003            il += 1                       
1004        return G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}
1005       
1006    def ReadPowderIparm(self,instfile,bank,databanks,rd):
1007        '''Read a GSAS (old) instrument parameter file
1008
1009        :param str instfile: name of instrument parameter file
1010
1011        :param int bank: the bank number read in the raw data file
1012
1013        :param int databanks: the number of banks in the raw data file.
1014          If the number of banks in the data and instrument parameter files
1015          agree, then the sets of banks are assumed to match up and bank
1016          is used to select the instrument parameter file. If not, the user
1017          is asked to make a selection.
1018
1019        :param obj rd: the raw data (histogram) data object. This
1020          sets rd.instbank.
1021
1022        '''
1023        if not os.path.exists(instfile): # no such file
1024            return {}
1025        fp = 0
1026        try:
1027            fp = open(instfile,'Ur')
1028            Iparm = {}
1029            for S in fp:
1030                if '#' in S[0]:
1031                    continue
1032                Iparm[S[:12]] = S[12:-1]
1033        except IOError:
1034            print('Error reading file:'+str(instfile))
1035        if fp:       
1036            fp.close()
1037
1038        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
1039        hType = Iparm['INS   HTYPE '].strip()
1040        if ibanks == 1: # there is only one bank here, return it
1041            rd.instbank = 1
1042            return Iparm
1043        if 'PNT' in hType:
1044            rd.instbank = bank
1045        elif ibanks != databanks:
1046            # number of banks in data and prm file not not agree, need a
1047            # choice from a human here
1048            choices = []
1049            for i in range(1,1+ibanks):
1050                choices.append('Bank '+str(i))
1051            bank = rd.BlockSelector(
1052                choices, self,
1053                title='Select an instrument parameter bank for '+
1054                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
1055                '\nOr use Cancel to select from the default parameter sets',
1056                header='Block Selector')
1057        if bank is None: return {}
1058        # pull out requested bank # bank from the data, and change the bank to 1
1059        IparmS = {}
1060        for key in Iparm:
1061            if 'INS' in key[:3]:    #skip around rubbish lines in some old iparm files
1062                if key[4:6] == "  ":
1063                    IparmS[key] = Iparm[key]
1064                elif int(key[4:6].strip()) == bank:
1065                    IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
1066        rd.instbank = bank
1067        return IparmS
1068                       
1069    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
1070        '''Open and read an instrument parameter file for a data file
1071        Returns the list of parameters used in the data tree
1072
1073        :param obj rd: the raw data (histogram) data object.
1074
1075        :param str prevIparm: not used
1076
1077        :param str lastIparmfile: Name of last instrument parameter
1078          file that was read, or a empty string.
1079
1080        :param str lastdatafile: Name of last data file that was read.
1081
1082        :returns: a list of two dicts, the first containing instrument parameters
1083          and the second used for TOf lookup tables for profile coeff.
1084
1085        '''
1086        def SetPowderInstParms(Iparm, rd):
1087            '''extracts values from instrument parameters in rd.instdict
1088            or in array Iparm.
1089            Create and return the contents of the instrument parameter tree entry.
1090            '''
1091            Irads = {0:' ',1:'CrKa',2:'FeKa',3:'CuKa',4:'MoKa',5:'AgKa',6:'TiKa',7:'CoKa'}
1092            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
1093            # override inst values with values read from data file
1094            Bank = rd.powderentry[2]    #should be used in multibank iparm files
1095            if rd.instdict.get('type'):
1096                DataType = rd.instdict.get('type')
1097            data = [DataType,]
1098            instname = Iparm.get('INS  1INAME ')
1099            irad = int(Iparm.get('INS  1 IRAD ','0'))
1100            if instname:
1101                rd.Sample['InstrName'] = instname.strip()
1102            if 'C' in DataType:
1103                wave1 = None
1104                wave2 = 0.0
1105                if rd.instdict.get('wave'):
1106                    wl = rd.instdict.get('wave')
1107                    wave1 = wl[0]
1108                    if len(wl) > 1: wave2 = wl[1]
1109                s = Iparm['INS  1 ICONS']
1110                if not wave1:
1111                    wave1 = G2IO.sfloat(s[:10])
1112                    wave2 = G2IO.sfloat(s[10:20])
1113                v = (wave1,wave2,
1114                     G2IO.sfloat(s[20:30]),G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
1115                if not v[1]:
1116                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
1117                    v = (v[0],v[2],v[4])
1118                    codes = [0,0,0,0]
1119                else:
1120                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
1121                    codes = [0,0,0,0,0,0]
1122                data.extend(v)
1123                if 'INS  1PRCF  ' in Iparm:
1124                    v1 = Iparm['INS  1PRCF  '].split()                                                 
1125                    v = Iparm['INS  1PRCF 1'].split()
1126                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1127                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1128                    v = Iparm['INS  1PRCF 2'].split()
1129                    if v1[0] == 3:
1130                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1131                    else:
1132                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file                   
1133                else:
1134                    v1 = Iparm['INS  1PRCF1 '].split()                                                 
1135                    v = Iparm['INS  1PRCF11'].split()
1136                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
1137                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
1138                    v = Iparm['INS  1PRCF12'].split()
1139                    if v1[0] == 3:
1140                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
1141                    else:
1142                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
1143                codes.extend([0,0,0,0,0,0,0])
1144                Iparm1 = G2IO.makeInstDict(names,data,codes)
1145                Iparm1['Source'] = [Irads[irad],Irads[irad]]
1146                Iparm1['Bank'] = [Bank,Bank,0]
1147                return [Iparm1,{}]
1148            elif 'T' in DataType:
1149                names = ['Type','fltPath','2-theta','difC','difA', 'difB','Zero','alpha','beta-0','beta-1',
1150                    'beta-q','sig-0','sig-1','sig-2','sig-q', 'X','Y','Azimuth',]
1151                codes = [0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,]
1152                azm = 0.
1153                if 'INS  1DETAZM' in Iparm:
1154                    azm = float(Iparm['INS  1DETAZM'])
1155                s = Iparm['INS   FPATH1'].split()
1156                fltPath0 = G2IO.sfloat(s[0])
1157                s = Iparm['INS  1BNKPAR'].split()
1158                fltPath1 = G2IO.sfloat(s[0])
1159                data.extend([fltPath0+fltPath1,])               #Flight path source-sample-detector
1160                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
1161                s = Iparm['INS  1 ICONS'].split()
1162                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,G2IO.sfloat(s[2])])    #difC,difA,difB,Zero
1163                if 'INS  1PRCF  ' in Iparm:
1164                    s = Iparm['INS  1PRCF  '].split()
1165                    pfType = int(s[0])
1166                    s = Iparm['INS  1PRCF 1'].split()
1167                    if abs(pfType) == 1:
1168                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1169                        s = Iparm['INS  1PRCF 2'].split()
1170                        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
1171                    elif abs(pfType) in [3,4,5]:
1172                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1173                        if abs(pfType) == 4:
1174                            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
1175                        else:
1176                            s = Iparm['INS  1PRCF 2'].split()
1177                            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                       
1178                    elif abs(pfType) == 2:
1179                        data.extend([G2IO.sfloat(s[1]),0.0,1./G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1180                        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                           
1181                else:
1182                    s = Iparm['INS  1PRCF1 '].split()
1183                    pfType = int(s[0])
1184                    s = Iparm['INS  1PRCF11'].split()
1185                    if abs(pfType) == 1:
1186                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])]) #alpha, beta-0, beta-1
1187                        s = Iparm['INS  1PRCF12'].split()
1188                        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
1189                    elif abs(pfType) in [3,4,5]:
1190                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])]) #alpha, beta-0, beta-1
1191                        if abs(pfType) == 4:
1192                            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
1193                        else:
1194                            s = Iparm['INS  1PRCF12'].split()
1195                            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                       
1196                Inst1 = G2IO.makeInstDict(names,data,codes)
1197                Inst1['Bank'] = [Bank,Bank,0]
1198                Inst2 = {}
1199                if pfType < 0:
1200                    Ipab = 'INS  1PAB'+str(-pfType)
1201                    Npab = int(Iparm[Ipab+'  '].strip())
1202                    Inst2['Pdabc'] = []
1203                    for i in range(Npab):
1204                        k = Ipab+str(i+1).rjust(2)
1205                        s = Iparm[k].split()
1206                        Inst2['Pdabc'].append([float(t) for t in s])
1207                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
1208                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
1209                if 'INS  1I ITYP' in Iparm:
1210                    s = Iparm['INS  1I ITYP'].split()
1211                    Ityp = int(s[0])
1212                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
1213                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
1214                    if Ityp in [1,2,4]:
1215                        Inst2['Itype'] = Itypes[Ityp-1]
1216                        Inst2['Tminmax'] = Tminmax
1217                        Icoeff = []
1218                        Iesd = []
1219                        Icovar = []                   
1220                        for i in range(3):
1221                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
1222                            Icoeff += [float(S) for S in s]
1223                            s = Iparm['INS  1IECOF'+str(i+1)].split()
1224                            Iesd += [float(S) for S in s]
1225                        NT = 10
1226                        for i in range(8):
1227                            s = Iparm['INS  1IECOR'+str(i+1)]
1228                            if i == 7:
1229                                NT = 8
1230                            Icovar += [float(s[6*j:6*j+6]) for j in range(NT)]
1231                        Inst2['Icoeff'] = Icoeff
1232                        Inst2['Iesd'] = Iesd
1233                        Inst2['Icovar'] = Icovar
1234                return [Inst1,Inst2]
1235
1236        # stuff we might need from the reader
1237        filename = rd.powderentry[0]
1238        bank = rd.powderentry[2]
1239        numbanks = rd.numbanks
1240        # is there an instrument parameter file defined for the current data set?
1241        # or if this is a read on a set of set of files, use the last one again
1242        #if rd.instparm or (lastdatafile == filename and lastIparmfile):
1243        if rd.instparm or lastIparmfile:
1244            if rd.instparm:
1245                instfile = os.path.join(os.path.split(filename)[0],
1246                                    rd.instparm)
1247            else:
1248                # for multiple reads of one data file, reuse the inst parm file
1249                instfile = lastIparmfile
1250            if os.path.exists(instfile):
1251                #print 'debug: try read',instfile
1252                Lines = self.OpenPowderInstprm(instfile)
1253                instParmList = None
1254                if Lines is not None:
1255                    instParmList = self.ReadPowderInstprm(Lines,bank)    #know Bank - see above
1256                if 'list' in str(type(instParmList)):
1257                    rd.instfile = instfile
1258                    rd.instmsg = 'GSAS-II file '+instfile
1259                    return instParmList
1260                else:
1261                    rd.instmsg = instParmList   #an error message
1262                    return None
1263                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1264                if Iparm:
1265                    #print 'debug: success'
1266                    rd.instfile = instfile
1267                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1268                    return SetPowderInstParms(Iparm,rd)
1269            else:
1270                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1271                    +str(instfile)+' requested by file '+ filename)
1272        # is there an instrument parameter file matching the current file
1273        # with extension .inst or .prm? If so read it
1274        basename = os.path.splitext(filename)[0]
1275        for ext in '.instprm','.prm','.inst','.ins':
1276            instfile = basename + ext
1277            Lines = self.OpenPowderInstprm(instfile)
1278            instParmList = None
1279            if Lines is not None:
1280                instParmList = self.ReadPowderInstprm(Lines,bank)    #know Bank - see above
1281            if instParmList is not None:
1282                rd.instfile = instfile
1283                rd.instmsg = 'GSAS-II file '+instfile
1284                return instParmList
1285            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1286            if Iparm:
1287                #print 'debug: success'
1288                rd.instfile = instfile
1289                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1290                return SetPowderInstParms(Iparm,rd)
1291            else:
1292                #print 'debug: open/read failed',instfile
1293                pass # fail silently
1294
1295        # did we read the data file from a zip? If so, look there for a
1296        # instrument parameter file
1297        if self.zipfile:
1298            for ext in '.instprm','.prm','.inst','.ins':
1299                instfile = G2IO.ExtractFileFromZip(
1300                    self.zipfile,
1301                    selection=os.path.split(basename + ext)[1],
1302                    parent=self)
1303                if instfile is not None and instfile != self.zipfile:
1304                    print 'debug:',instfile,'created from ',self.zipfile
1305                    Lines = self.OpenPowderInstprm(instfile)
1306                    instParmList = None
1307                    if Lines is not None:
1308                        instParmList = self.ReadPowderInstprm(Lines,bank)    #know Bank - see above
1309                    if instParmList is not None:
1310                        rd.instfile = instfile
1311                        rd.instmsg = 'GSAS-II file '+instfile
1312                        return instParmList
1313                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1314                    if Iparm:
1315                        rd.instfile = instfile
1316                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1317                        return SetPowderInstParms(Iparm,rd)
1318                    else:
1319                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
1320                        pass # fail silently
1321
1322        while True: # loop until we get a file that works or we get a cancel
1323            instfile = ''
1324            pth = G2G.GetImportPath(self)
1325            if not pth: pth = '.'
1326            dlg = wx.FileDialog(
1327                self,
1328                'Choose inst. param file for "'+rd.idstring+'" (or Cancel for default)',
1329                pth, '',
1330                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1331                'GSAS-II iparm file (*.instprm)|*.instprm|'
1332                'All files (*.*)|*.*', wx.OPEN)
1333            if os.path.exists(lastIparmfile):
1334                dlg.SetFilename(lastIparmfile)
1335            if dlg.ShowModal() == wx.ID_OK:
1336                instfile = dlg.GetPath()
1337            dlg.Destroy()
1338            if not instfile: break
1339            Lines = self.OpenPowderInstprm(instfile)
1340            instParmList = None
1341            if Lines is not None:
1342                instParmList = self.ReadPowderInstprm(Lines,bank)    #know Bank - see above
1343            if instParmList is not None:
1344                rd.instfile = instfile
1345                rd.instmsg = 'GSAS-II file '+instfile
1346                return instParmList
1347            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1348            if Iparm:
1349                #print 'debug: success with',instfile
1350                rd.instfile = instfile
1351                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1352                return SetPowderInstParms(Iparm,rd)
1353            else:
1354                self.ErrorDialog('Read Error',
1355                                 'Error opening/reading file '+str(instfile))
1356       
1357        # still no success: offer user choice of defaults
1358        import defaultIparms as dI
1359        while True: # loop until we get a choice
1360            choices = []
1361            head = 'Select from default instrument parameters for '+rd.idstring
1362
1363            for l in dI.defaultIparm_lbl:
1364                choices.append('Defaults for '+l)
1365            res = rd.BlockSelector(
1366                choices,
1367                ParentFrame=self,
1368                title=head,
1369                header='Select default inst parms',
1370                useCancel=False)
1371            if res is None: continue
1372            rd.instfile = ''
1373            rd.instmsg = 'default: '+dI.defaultIparm_lbl[res]
1374            return self.ReadPowderInstprm(dI.defaultIparms[res],bank)    #know Bank - see above
1375
1376    def OnImportPowder(self,event):
1377        '''Called in response to an Import/Powder Data/... menu item
1378        to read a powder diffraction data set.
1379        dict self.ImportMenuId is used to look up the specific
1380        reader item associated with the menu item, which will be
1381        None for the last menu item, which is the "guess" option
1382        where all appropriate formats will be tried.
1383
1384        Also reads an instrument parameter file for each dataset.
1385        '''
1386        # get a list of existing histograms
1387        PWDRlist = []
1388        if self.PatternTree.GetCount():
1389            item, cookie = self.PatternTree.GetFirstChild(self.root)
1390            while item:
1391                name = self.PatternTree.GetItemText(item)
1392                if name.startswith('PWDR ') and name not in PWDRlist:
1393                    PWDRlist.append(name)
1394                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1395        # look up which format was requested
1396        reqrdr = self.ImportMenuId.get(event.GetId()) 
1397        rdlist = self.OnImportGeneric(
1398            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1399        if len(rdlist) == 0: return
1400        self.CheckNotebook()
1401        Iparm = None
1402        lastIparmfile = ''
1403        lastdatafile = ''
1404        newHistList = []
1405        self.EnablePlot = False
1406        for rd in rdlist:
1407            if 'Instrument Parameters' not in rd.pwdparms:
1408                # get instrument parameters for each dataset, unless already set
1409                Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1410                if rd.repeat_instparm: 
1411                    lastIparmfile = rd.instfile
1412                # override any keys in read instrument parameters with ones set in import
1413                for key in Iparm1: 
1414                    if key in rd.instdict:
1415                        Iparm1[key] = rd.instdict[key]
1416            else:
1417                Iparm1,Iparm2 = rd.pwdparms['Instrument Parameters']
1418            lastdatafile = rd.powderentry[0]
1419            HistName = rd.idstring
1420            HistName = 'PWDR '+HistName
1421            # make new histogram names unique
1422            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1423            print 'Read powder data '+str(HistName)+ \
1424                ' from file '+str(rd.readfilename) + \
1425                ' with parameters from '+str(rd.instmsg)
1426            # data are read, now store them in the tree
1427            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1428            if 'T' in Iparm1['Type'][0]:
1429                if not rd.clockWd and rd.GSAS:
1430                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1431                cw = np.diff(rd.powderdata[0])
1432                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1433                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
1434                    npts = min(len(rd.powderdata[0]),len(rd.powderdata[1]),len(cw))
1435                    rd.powderdata[1] = rd.powderdata[1][:npts]/cw[:npts]
1436                    rd.powderdata[2] = rd.powderdata[2][:npts]*cw[:npts]**2  #1/var=w at this point
1437                else:       #NB: from topas/fullprof type files
1438                    rd.powderdata[1] = rd.powderdata[1][:-1]
1439                    rd.powderdata[2] = rd.powderdata[2][:-1]
1440                if 'Itype' in Iparm2:
1441                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1442                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1443                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1444                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1445                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1446                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1447                    var += WYI*rd.powderdata[1]**2
1448                    var /= YI**2
1449                    rd.powderdata[2] = 1./var
1450                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1451                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1452                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1453            valuesdict = {
1454                'wtFactor':1.0,
1455                'Dummy':False,
1456                'ranId':ran.randint(0,sys.maxint),
1457                'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1458                'qPlot':False,'dPlot':False,'sqrtPlot':False
1459                }
1460            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1461            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1462            self.PatternTree.SetItemPyData(
1463                self.PatternTree.AppendItem(Id,text='Comments'),
1464                rd.comments)
1465            Tmin = min(rd.powderdata[0])
1466            Tmax = max(rd.powderdata[0])
1467            self.PatternTree.SetItemPyData(
1468                self.PatternTree.AppendItem(Id,text='Limits'),
1469                rd.pwdparms.get('Limits',[(Tmin,Tmax),[Tmin,Tmax]])
1470                )
1471            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1472            self.PatternTree.SetItemPyData(
1473                self.PatternTree.AppendItem(Id,text='Background'),
1474                rd.pwdparms.get('Background',
1475                    [['chebyschev',True,3,1.0,0.0,0.0],{'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1476                    )
1477            self.PatternTree.SetItemPyData(
1478                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1479                [Iparm1,Iparm2])
1480            self.PatternTree.SetItemPyData(
1481                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1482                rd.Sample)
1483            self.PatternTree.SetItemPyData(
1484                self.PatternTree.AppendItem(Id,text='Peak List')
1485                ,{'peaks':[],'sigDict':{}})
1486            self.PatternTree.SetItemPyData(
1487                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1488                [[],[]])
1489            self.PatternTree.SetItemPyData(
1490                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1491                [])
1492            self.PatternTree.SetItemPyData(
1493                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1494                {})
1495            # if any Control values have been set, move them into tree
1496            Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
1497            Controls.update(rd.Controls)
1498            newHistList.append(HistName)
1499        else:
1500            self.EnablePlot = True
1501            self.PatternTree.Expand(Id)
1502            self.PatternTree.SelectItem(Id)
1503           
1504        if not newHistList: return # somehow, no new histograms
1505        # make a list of phase names
1506        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1507        phaseNameList = usedHistograms.keys() # phase names in use
1508        if not phaseNameList: return # no phases yet, nothing to do
1509        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1510        for Name in newHistList:
1511            header += '\n  '+str(Name)
1512
1513        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1514        if not result: return
1515        # connect new phases to histograms
1516        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1517        if not sub:
1518            raise Exception('ERROR -- why are there no phases here?')
1519        item, cookie = self.PatternTree.GetFirstChild(sub)
1520        iph = -1
1521        while item: # loop over (new) phases
1522            iph += 1
1523            phaseName = self.PatternTree.GetItemText(item)
1524            data = self.PatternTree.GetItemPyData(item)
1525            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1526            if iph not in result: continue
1527            generalData = data['General']
1528            SGData = generalData['SGData']
1529            UseList = data['Histograms']
1530            NShkl = len(G2spc.MustrainNames(SGData))
1531            NDij = len(G2spc.HStrainNames(SGData))
1532            for histoName in newHistList:
1533                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1534                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1535                refList = self.PatternTree.GetItemPyData(
1536                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1537                refList[generalData['Name']] = []
1538        return # success
1539
1540    def OnDummyPowder(self,event):
1541        '''Called in response to Import/Powder Data/Simulate menu item
1542        to create a Dummy powder diffraction data set.
1543
1544        Reads an instrument parameter file and then gets input from the user
1545        '''
1546        # get a list of existing histograms
1547        PWDRlist = []
1548        if self.PatternTree.GetCount():
1549            item, cookie = self.PatternTree.GetFirstChild(self.root)
1550            while item:
1551                name = self.PatternTree.GetItemText(item)
1552                if name.startswith('PWDR ') and name not in PWDRlist:
1553                    PWDRlist.append(name)
1554                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1555        # Initialize a base class reader
1556        rd = G2IO.ImportPowderData(
1557            extensionlist=tuple(),
1558            strictExtension=False,
1559            formatName = 'Simulate dataset',
1560            longFormatName = 'Compute a simulated pattern')
1561        rd.powderentry[0] = '' # no filename
1562        # #self.powderentry[1] = pos # bank offset (N/A here)
1563        rd.powderentry[2] = 1 # only one bank
1564        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1565        self.CheckNotebook()
1566        Iparm = None
1567        lastIparmfile = ''
1568        lastdatafile = ''
1569        self.zipfile = None
1570        # get instrument parameters for it
1571        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1572        if 'T' in Iparm1['Type'][0]:
1573            print('TOF simulation not supported yet')
1574            return False
1575        else:
1576            # need to get name, 2theta start, end, step
1577            rd.idstring = ' CW'
1578            if 'X' in Iparm1['Type'][0]:
1579                rd.idstring = 'CW x-ray simulation'
1580            else:
1581                rd.idstring = 'CW neutron simulation'
1582            # base initial range on wavelength
1583            wave = Iparm1.get('Lam')
1584            if wave:
1585                wave = wave[0]
1586            else:
1587                wave = Iparm1.get('Lam1')
1588                if wave:
1589                    wave = wave[0]
1590        N = 0
1591        while (N < 3): # insist on a dataset with a few points
1592            names = ('dataset name', 'start angle', 'end angle', 'step size')
1593            if not wave or wave < 1.0:
1594                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1595            else:
1596                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1597            dlg = G2G.ScrolledMultiEditor(
1598                self,[inp] * len(inp),range(len(inp)),names,
1599                header='Enter simulation name and range',
1600                minvals=(None,0.001,0.001,0.0001),
1601                maxvals=(None,180.,180.,.1),
1602                sizevals=((225,-1),)
1603                )
1604            dlg.CenterOnParent()
1605            if dlg.ShowModal() == wx.ID_OK:
1606                if inp[1] > inp[2]:
1607                    end,start,step = inp[1:]
1608                else:               
1609                    start,end,step = inp[1:]
1610                step = abs(step)
1611            else:
1612                return False
1613            N = int((end-start)/step)+1
1614            x = np.linspace(start,end,N,True)
1615            N = len(x)
1616        rd.powderdata = [
1617            np.array(x), # x-axis values
1618            np.zeros_like(x), # powder pattern intensities
1619            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1620            np.zeros_like(x), # calc. intensities (zero)
1621            np.zeros_like(x), # calc. background (zero)
1622            np.zeros_like(x), # obs-calc profiles
1623            ]
1624        Tmin = rd.powderdata[0][0]
1625        Tmax = rd.powderdata[0][-1]
1626        # data are read, now store them in the tree
1627        HistName = inp[0]
1628        HistName = 'PWDR '+HistName
1629        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1630        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1631        valuesdict = {
1632            'wtFactor':1.0,
1633            'Dummy':True,
1634            'ranId':ran.randint(0,sys.maxint),
1635            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1636            'qPlot':False,'dPlot':False,'sqrtPlot':False
1637            }
1638        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1639        self.PatternTree.SetItemPyData(
1640            self.PatternTree.AppendItem(Id,text='Comments'),
1641            rd.comments)
1642        self.PatternTree.SetItemPyData(
1643            self.PatternTree.AppendItem(Id,text='Limits'),
1644            [(Tmin,Tmax),[Tmin,Tmax]])
1645        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1646        self.PatternTree.SetItemPyData(
1647            self.PatternTree.AppendItem(Id,text='Background'),
1648            [['chebyschev',True,3,1.0,0.0,0.0],
1649             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1650        self.PatternTree.SetItemPyData(
1651            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1652            [Iparm1,Iparm2])
1653        self.PatternTree.SetItemPyData(
1654            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1655            rd.Sample)
1656        self.PatternTree.SetItemPyData(
1657            self.PatternTree.AppendItem(Id,text='Peak List')
1658            ,{'peaks':[],'sigDict':{}})
1659        self.PatternTree.SetItemPyData(
1660            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1661            [[],[]])
1662        self.PatternTree.SetItemPyData(
1663            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1664            [])
1665        self.PatternTree.SetItemPyData(
1666            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1667            {})
1668        self.PatternTree.Expand(Id)
1669        self.PatternTree.SelectItem(Id)
1670        print('Added simulation powder data '+str(HistName)+ 
1671              ' with parameters from '+str(rd.instmsg))
1672
1673        # make a list of phase names
1674        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1675        phaseNameList = usedHistograms.keys() # phase names in use
1676        if not phaseNameList: return # no phases yet, nothing to do
1677        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1678        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1679        if not result: return
1680        # connect new phases to histograms
1681        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1682        if not sub:
1683            raise Exception('ERROR -- why are there no phases here?')
1684        item, cookie = self.PatternTree.GetFirstChild(sub)
1685        iph = -1
1686        while item: # loop over (new) phases
1687            iph += 1
1688            phaseName = self.PatternTree.GetItemText(item)
1689            data = self.PatternTree.GetItemPyData(item)
1690            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1691            if iph not in result: continue
1692            generalData = data['General']
1693            SGData = generalData['SGData']
1694            UseList = data['Histograms']
1695            NShkl = len(G2spc.MustrainNames(SGData))
1696            NDij = len(G2spc.HStrainNames(SGData))
1697            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
1698            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1699            refList = self.PatternTree.GetItemPyData(
1700                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1701            refList[generalData['Name']] = []
1702        return # success
1703       
1704    def OnPreferences(self,event):
1705        'Edit the GSAS-II configuration variables'
1706        dlg = G2G.SelectConfigSetting(self)
1707        dlg.ShowModal() == wx.ID_OK
1708        dlg.Destroy()
1709
1710    def _Add_ImportMenu_smallangle(self,parent):
1711        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1712        '''
1713        submenu = wx.Menu()
1714        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1715            submenu, help='Import small angle data')
1716        for reader in self.ImportSmallAngleReaderlist:
1717            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1718                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1719            self.ImportMenuId[item.GetId()] = reader
1720            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1721        # item = submenu.Append(wx.ID_ANY,
1722        #     help='Import small angle data, use file to try to determine format',
1723        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1724        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1725
1726    def OnImportSmallAngle(self,event):
1727        '''Called in response to an Import/Small Angle Data/... menu item
1728        to read a small angle diffraction data set.
1729        dict self.ImportMenuId is used to look up the specific
1730        reader item associated with the menu item, which will be
1731        None for the last menu item, which is the "guess" option
1732        where all appropriate formats will be tried.
1733
1734        '''
1735       
1736        def GetSASDIparm(reader):
1737            parm = reader.instdict
1738            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1739                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1740            return Iparm,{}
1741           
1742        # get a list of existing histograms
1743        SASDlist = []
1744        if self.PatternTree.GetCount():
1745            item, cookie = self.PatternTree.GetFirstChild(self.root)
1746            while item:
1747                name = self.PatternTree.GetItemText(item)
1748                if name.startswith('SASD ') and name not in SASDlist:
1749                    SASDlist.append(name)
1750                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1751        # look up which format was requested
1752        reqrdr = self.ImportMenuId.get(event.GetId()) 
1753        rdlist = self.OnImportGeneric(
1754            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1755        if len(rdlist) == 0: return
1756        self.CheckNotebook()
1757        Iparm = None
1758        lastdatafile = ''
1759        newHistList = []
1760        self.EnablePlot = False
1761        for rd in rdlist:
1762            lastdatafile = rd.smallangleentry[0]
1763            HistName = rd.idstring
1764            HistName = 'SASD '+HistName
1765            # make new histogram names unique
1766            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1767            print 'Read small angle data '+str(HistName)+ \
1768                ' from file '+str(self.lastimport)
1769            # data are read, now store them in the tree
1770            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1771            Iparm1,Iparm2 = GetSASDIparm(rd)
1772#            if 'T' in Iparm1['Type'][0]:
1773#                if not rd.clockWd and rd.GSAS:
1774#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1775#                cw = np.diff(rd.powderdata[0])
1776#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1777#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1778#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1779#                if 'Itype' in Iparm2:
1780#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1781#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1782#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1783#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1784#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1785#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1786#                    var += WYI*rd.powderdata[1]**2
1787#                    var /= YI**2
1788#                    rd.powderdata[2] = 1./var
1789#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1790#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1791#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1792            Tmin = min(rd.smallangledata[0])
1793            Tmax = max(rd.smallangledata[0])
1794            valuesdict = {
1795                'wtFactor':1.0,
1796                'Dummy':False,
1797                'ranId':ran.randint(0,sys.maxint),
1798                }
1799            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1800            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1801            self.PatternTree.SetItemPyData(
1802                self.PatternTree.AppendItem(Id,text='Comments'),
1803                rd.comments)
1804            self.PatternTree.SetItemPyData(
1805                self.PatternTree.AppendItem(Id,text='Limits'),
1806                [(Tmin,Tmax),[Tmin,Tmax]])
1807            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1808            self.PatternTree.SetItemPyData(
1809                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1810                [Iparm1,Iparm2])
1811            self.PatternTree.SetItemPyData(
1812                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1813            self.PatternTree.SetItemPyData(
1814                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1815                rd.Sample)
1816            self.PatternTree.SetItemPyData(
1817                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1818            newHistList.append(HistName)
1819        else:
1820            self.EnablePlot = True
1821            self.PatternTree.Expand(Id)
1822            self.PatternTree.SelectItem(Id)
1823           
1824        if not newHistList: return # somehow, no new histograms
1825        return # success
1826
1827    def OnMacroRecordStatus(self,event,setvalue=None):
1828        '''Called when the record macro menu item is used which toggles the
1829        value. Alternately a value to be set can be provided. Note that this
1830        routine is made more complex because on the Mac there are lots of menu
1831        items (listed in self.MacroStatusList) and this loops over all of them.
1832        '''
1833        nextvalue = log.ShowLogStatus() != True
1834        if setvalue is not None:
1835            nextvalue = setvalue
1836        if nextvalue:
1837            log.LogOn()
1838            set2 = True
1839        else:
1840            log.LogOff()
1841            set2 = False
1842        for menuitem in self.MacroStatusList:
1843            menuitem.Check(set2)
1844
1845    def _init_Macro(self):
1846        '''Define the items in the macro menu.
1847        '''
1848        menu = self.MacroMenu
1849        item = menu.Append(
1850                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
1851                kind=wx.ITEM_CHECK,text='Record actions')
1852        self.MacroStatusList.append(item)
1853        item.Check(log.ShowLogStatus())
1854        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
1855
1856        # this may only be of value for development work
1857        item = menu.Append(
1858            help='Show logged commands', id=wx.ID_ANY,
1859            kind=wx.ITEM_NORMAL,text='Show log')
1860        def OnShowLog(event):
1861            print 70*'='
1862            print 'List of logged actions'
1863            for i,line in enumerate(log.G2logList):
1864                if line: print i,line
1865            print 70*'='
1866        self.Bind(wx.EVT_MENU, OnShowLog, item)
1867
1868        item = menu.Append(
1869            help='Clear logged commands', id=wx.ID_ANY,
1870            kind=wx.ITEM_NORMAL,text='Clear log')
1871        def OnClearLog(event): log.G2logList=[None]
1872        self.Bind(wx.EVT_MENU, OnClearLog, item)
1873       
1874        item = menu.Append(
1875            help='Save logged commands to file', id=wx.ID_ANY,
1876            kind=wx.ITEM_NORMAL,text='Save log')
1877        def OnSaveLog(event):
1878            import cPickle
1879            defnam = os.path.splitext(
1880                os.path.split(self.GSASprojectfile)[1]
1881                )[0]+'.gcmd'
1882            dlg = wx.FileDialog(self,
1883                'Choose an file to save past actions', '.', defnam, 
1884                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1885                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1886            dlg.CenterOnParent()
1887            try:
1888                if dlg.ShowModal() == wx.ID_OK:
1889                    filename = dlg.GetPath()
1890                    # make sure extension is correct
1891                    filename = os.path.splitext(filename)[0]+'.gcmd'
1892                else:
1893                    filename = None
1894            finally:
1895                dlg.Destroy()
1896            if filename:
1897                fp = open(filename,'wb')
1898                fp.write(str(len(log.G2logList)-1)+'\n')
1899                for item in log.G2logList:
1900                    if item: cPickle.dump(item,fp)
1901                fp.close()
1902        self.Bind(wx.EVT_MENU, OnSaveLog, item)
1903
1904        item = menu.Append(
1905            help='Load logged commands from file', id=wx.ID_ANY,
1906            kind=wx.ITEM_NORMAL,text='Load log')
1907        def OnLoadLog(event):
1908            # this appends. Perhaps we should ask to clear?
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 read saved actions', '.', defnam, 
1915                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1916                wx.OPEN)
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 and os.path.exists(filename):
1928                fp = open(filename,'rb')
1929                lines = fp.readline()
1930                for i in range(int(lines)):
1931                    log.G2logList.append(cPickle.load(fp))
1932                fp.close()
1933        self.Bind(wx.EVT_MENU, OnLoadLog, item)
1934
1935        item = menu.Append(
1936            help='Replay saved commands', id=wx.ID_ANY,
1937            kind=wx.ITEM_NORMAL,text='Replay log')
1938        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
1939
1940    def _init_Exports(self,menu):
1941        '''Find exporter routines and add them into menus
1942        '''
1943        # set up the top-level menus
1944        projectmenu = wx.Menu()
1945        item = menu.AppendMenu(
1946            wx.ID_ANY, 'Entire project as',
1947            projectmenu, help='Export entire project')
1948
1949        phasemenu = wx.Menu()
1950        item = menu.AppendMenu(
1951            wx.ID_ANY, 'Phase as',
1952            phasemenu, help='Export phase or sometimes phases')
1953
1954        powdermenu = wx.Menu()
1955        item = menu.AppendMenu(
1956            wx.ID_ANY, 'Powder data as',
1957            powdermenu, help='Export powder diffraction histogram(s)')
1958
1959        singlemenu = wx.Menu()
1960        item = menu.AppendMenu(
1961            wx.ID_ANY, 'Single crystal data as',
1962            singlemenu, help='Export single crystal histogram(s)')
1963
1964        imagemenu = wx.Menu()
1965        item = menu.AppendMenu(
1966            wx.ID_ANY, 'Image data as',
1967            imagemenu, help='Export powder image(s) data')
1968
1969        mapmenu = wx.Menu()
1970        item = menu.AppendMenu(
1971            wx.ID_ANY, 'Maps as',
1972            mapmenu, help='Export density map(s)')
1973
1974        # pdfmenu = wx.Menu()
1975        # item = menu.AppendMenu(
1976        #     wx.ID_ANY, 'PDFs as',
1977        #     pdfmenu, help='Export pair distribution function(s)')
1978
1979        # find all the exporter files
1980        pathlist = sys.path
1981        filelist = []
1982        for path in pathlist:
1983            for filename in glob.iglob(os.path.join(path,"G2export*.py")):
1984                filelist.append(filename)   
1985        filelist = sorted(list(set(filelist))) # remove duplicates
1986        self.exporterlist = []
1987        # go through the routines and import them, saving objects that
1988        # have export routines (method Exporter)
1989        for filename in filelist:
1990            path,rootname = os.path.split(filename)
1991            pkg = os.path.splitext(rootname)[0]
1992            try:
1993                fp = None
1994                fp, fppath,desc = imp.find_module(pkg,[path,])
1995                pkg = imp.load_module(pkg,fp,fppath,desc)
1996                for clss in inspect.getmembers(pkg): # find classes defined in package
1997                    if clss[0].startswith('_'): continue
1998                    if inspect.isclass(clss[1]):
1999                        # check if we have the required methods
2000                        for m in 'Exporter','loadParmDict':
2001                            if not hasattr(clss[1],m): break
2002                            if not callable(getattr(clss[1],m)): break
2003                        else:
2004                            exporter = clss[1](self) # create an export instance
2005                            self.exporterlist.append(exporter)
2006            except AttributeError:
2007                print 'Import_'+errprefix+': Attribute Error'+str(filename)
2008                pass
2009            except ImportError:
2010                print 'Import_'+errprefix+': Error importing file'+str(filename)
2011                pass
2012            if fp: fp.close()
2013        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
2014        for obj in self.exporterlist:
2015            #print 'exporter',obj
2016            for typ in obj.exporttype:
2017                if typ == "project":
2018                    submenu = projectmenu
2019                elif typ == "phase":
2020                    submenu = phasemenu
2021                elif typ == "powder":
2022                    submenu = powdermenu
2023                elif typ == "single":
2024                    submenu = singlemenu
2025                elif typ == "image":
2026                    submenu = imagemenu
2027                elif typ == "map":
2028                    submenu = mapmenu
2029                # elif typ == "pdf":
2030                #     submenu = pdfmenu
2031                else:
2032                    print("Error, unknown type in "+str(obj))
2033                    break
2034                item = submenu.Append(
2035                    wx.ID_ANY,
2036                    help=obj.longFormatName,
2037                    kind=wx.ITEM_NORMAL,
2038                    text=obj.formatName)
2039                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
2040                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
2041        item = imagemenu.Append(wx.ID_ANY,
2042                        help='Export image controls and masks for multiple images',
2043                        kind=wx.ITEM_NORMAL,
2044                        text='Multiple image controls and masks')
2045        self.Bind(wx.EVT_MENU, self.OnSaveMultipleImg, id=item.GetId())
2046        #code to debug an Exporter. hard-code the routine below, to allow a reload before use
2047        # def DebugExport(event):
2048        #      print 'start reload'
2049        #      reload(G2IO)
2050        #      import G2export_pwdr as dev
2051        #      reload(dev)
2052        #      dev.ExportPowderFXYE(self).Exporter(event)
2053        # item = menu.Append(
2054        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2055        #     help="debug exporter",text="test Export FXYE")
2056        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
2057        # # #self.ExportLookup[item.GetId()] = 'image'
2058        # self.ExportLookup[item.GetId()] = 'powder'
2059
2060    def _Add_ExportMenuItems(self,parent):
2061        # item = parent.Append(
2062        #     help='Select PWDR item to enable',id=wx.ID_ANY,
2063        #     kind=wx.ITEM_NORMAL,
2064        #     text='Export Powder Patterns...')
2065        # self.ExportPattern.append(item)
2066        # item.Enable(False)
2067        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
2068
2069        item = parent.Append(
2070            help='',id=wx.ID_ANY,
2071            kind=wx.ITEM_NORMAL,
2072            text='Export All Peak Lists...')
2073        self.ExportPeakList.append(item)
2074        item.Enable(True)
2075        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
2076
2077        item = parent.Append(
2078            help='',id=wx.ID_ANY,
2079            kind=wx.ITEM_NORMAL,
2080            text='Export HKLs...')
2081        self.ExportHKL.append(item)
2082        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
2083
2084        item = parent.Append(
2085            help='Select PDF item to enable',
2086            id=wx.ID_ANY,
2087            kind=wx.ITEM_NORMAL,
2088            text='Export PDF...')
2089        self.ExportPDF.append(item)
2090        item.Enable(False)
2091        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
2092
2093    def FillMainMenu(self,menubar):
2094        '''Define contents of the main GSAS-II menu for the (main) data tree window.
2095        For the mac, this is also called for the data item windows as well so that
2096        the main menu items are data menu as well.
2097        '''
2098        File = wx.Menu(title='')
2099        menubar.Append(menu=File, title='&File')
2100        self._Add_FileMenuItems(File)
2101        Data = wx.Menu(title='')
2102        menubar.Append(menu=Data, title='Data')
2103        self._Add_DataMenuItems(Data)
2104        Calculate = wx.Menu(title='')       
2105        menubar.Append(menu=Calculate, title='&Calculate')
2106        self._Add_CalculateMenuItems(Calculate)
2107        Import = wx.Menu(title='')       
2108        menubar.Append(menu=Import, title='Import')
2109        self._Add_ImportMenu_Image(Import)
2110        self._Add_ImportMenu_Phase(Import)
2111        self._Add_ImportMenu_powder(Import)
2112        self._Add_ImportMenu_Sfact(Import)
2113        self._Add_ImportMenu_smallangle(Import)
2114
2115        #======================================================================
2116        # Code to help develop/debug an importer, much is hard-coded below
2117        # but module is reloaded before each use, allowing faster testing
2118        # def DebugImport(event):
2119        #     print 'start reload'
2120        #     import G2phase_ISO as dev
2121        #     reload(dev)
2122        #     rd = dev.ISODISTORTPhaseReader()
2123        #     self.ImportMenuId[event.GetId()] = rd
2124        #     self.OnImportPhase(event)
2125            # or ----------------------------------------------------------------------
2126            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
2127            # special debug code
2128            # or ----------------------------------------------------------------------
2129            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
2130            # fp = open(filename,'Ur')
2131            # if not rd.ContentsValidator(fp):
2132            #     print 'not validated'
2133            #     # make a list of used phase ranId's
2134            # phaseRIdList = []
2135            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2136            # if sub:
2137            #     item, cookie = self.PatternTree.GetFirstChild(sub)
2138            #     while item:
2139            #         phaseName = self.PatternTree.GetItemText(item)
2140            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2141            #         if ranId: phaseRIdList.append(ranId)
2142            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2143            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2144            #     print 'read OK'
2145        # item = Import.Append(
2146        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2147        #     help="debug importer",text="test importer")
2148        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2149        #======================================================================
2150        self.ExportMenu = wx.Menu(title='')
2151        menubar.Append(menu=self.ExportMenu, title='Export')
2152        self._init_Exports(self.ExportMenu)
2153        self._Add_ExportMenuItems(self.ExportMenu)
2154        if GSASIIpath.GetConfigValue('Enable_logging'):
2155            self.MacroMenu = wx.Menu(title='')
2156            menubar.Append(menu=self.MacroMenu, title='Macro')
2157            self._init_Macro()
2158        HelpMenu=G2G.MyHelp(self,helpType='Data tree',
2159            morehelpitems=[
2160                           ('&Tutorials','Tutorials'), 
2161                           ])
2162        menubar.Append(menu=HelpMenu,title='&Help')
2163           
2164    def _init_ctrls(self, parent):
2165        wx.Frame.__init__(self, name='GSASII', parent=parent,
2166            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2167        clientSize = wx.ClientDisplayRect()
2168        Size = self.GetSize()
2169        xPos = clientSize[2]-Size[0]
2170        self.SetPosition(wx.Point(xPos,clientSize[1]))
2171        self._init_Imports()
2172        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2173        self.MakePDF = []
2174        self.Refine = []
2175        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2176        #self.ExportPattern = []
2177        self.ExportPeakList = []
2178        self.ExportHKL = []
2179        self.ExportPDF = []
2180        self.ExportPhase = []
2181        self.ExportCIF = []
2182        #
2183        self.GSASIIMenu = wx.MenuBar()
2184        # create a list of all dataframe menus (appended in PrefillDataMenu)
2185        self.dataMenuBars = [self.GSASIIMenu]
2186        self.MacroStatusList = []
2187        self.FillMainMenu(self.GSASIIMenu)
2188        self.SetMenuBar(self.GSASIIMenu)
2189        self.Bind(wx.EVT_SIZE, self.OnSize)
2190        self.Status = self.CreateStatusBar()
2191        self.mainPanel = wx.Panel(self,-1)
2192       
2193        wxID_PATTERNTREE = wx.NewId()
2194        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2195        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2196            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2197        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnPatternTreeSelChanged)
2198        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2199            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2200        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2201            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2202        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2203            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2204        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2205            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2206        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2207            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2208        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2209            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2210        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2211        self.root = self.PatternTree.root
2212        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2213            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2214        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
2215        plotFrame.Show()
2216       
2217        self.dataDisplay = None
2218       
2219    def __init__(self, parent):
2220        self.ExportLookup = {}
2221        self._init_ctrls(parent)
2222        self.Image = wx.Image(
2223            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2224            wx.BITMAP_TYPE_ICO)
2225        if "wxMSW" in wx.PlatformInfo:
2226            img = self.Image.Scale(16, 16).ConvertToBitmap()
2227        elif "wxGTK" in wx.PlatformInfo:
2228            img = self.Image.Scale(22, 22).ConvertToBitmap()
2229        else:
2230            img = self.Image.ConvertToBitmap()
2231        self.SetIcon(wx.IconFromBitmap(img))
2232        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2233        # various defaults
2234        self.oldFocus = None
2235        self.GSASprojectfile = ''
2236        self.undofile = ''
2237        self.TreeItemDelete = False
2238        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2239        self.Weight = False
2240        self.IfPlot = False
2241        self.DDShowAll = False
2242        self.atmSel = ''
2243        self.PatternId = 0
2244        self.PickId = 0
2245        self.PickIdText = None
2246        self.PeakTable = []
2247        self.LimitsTable = []
2248        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2249        self.HKL = []
2250        self.Lines = []
2251        self.itemPicked = None
2252        self.dataFrame = None
2253        self.Interpolate = 'nearest'
2254        self.ContourColor = 'Paired'
2255        self.VcovColor = 'RdYlGn'
2256        self.RamaColor = 'Blues'
2257        self.Projection = 'equal area'
2258        self.logPlot = False
2259        self.plusPlot = True
2260        self.sqPlot = False
2261        self.ErrorBars = False
2262        self.Contour = False
2263        self.Legend = False
2264        self.SinglePlot = True
2265        self.SubBack = False
2266        self.seqReverse = False
2267        self.seqLines = True #draw lines between points
2268        self.plotView = 0
2269        self.Image = 0
2270        self.oldImagefile = '' # the name of the last image file read
2271        self.oldImageTag = None # the name of the tag for multi-image files
2272        self.ImageZ = []  # this contains the image plotted and used for integration
2273        # self.ImageZ and self.oldImagefile are set in GSASIIplot.PlotImage
2274        # and GSASIIIO.GetImageData
2275        # any changes to self.ImageZ should initialize self.oldImagefile to force a reread
2276        self.Integrate = 0
2277        self.imageDefault = {}
2278        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2279        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2280        self.autoIntFrame = None
2281        self.IntegratedList = [] # list of already integrated IMG tree items
2282        self.Sngl = False
2283        self.ifGetRing = False
2284        self.MaskKey = ''           #trigger for making image masks
2285        self.StrainKey = ''         #ditto for new strain d-zeros
2286        self.EnablePlot = True
2287        self.hist = ''              # selected histogram in Phase/Data tab
2288        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
2289        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2290        self.LastImportDir = None # last-used directory where an import was done
2291        self.LastGPXdir = None    # directory where a GPX file was last read
2292        self.LastExportDir = None  # the last directory used for exports, if any.
2293       
2294        arg = sys.argv
2295        if len(arg) > 1 and arg[1]:
2296            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2297            self.dirname = os.path.dirname(arg[1])
2298            if self.dirname: os.chdir(self.dirname)
2299            try:
2300                self.StartProject()         #open the file if possible
2301                return
2302            except Exception:
2303                print 'Error opening or reading file',arg[1]
2304                import traceback
2305                print traceback.format_exc()
2306               
2307        if GSASIIpath.GetConfigValue('Starting_directory'):
2308            try:
2309                pth = GSASIIpath.GetConfigValue('Starting_directory')
2310                pth = os.path.expanduser(pth) 
2311                os.chdir(pth)
2312                self.LastGPXdir = pth
2313            except:
2314                print('Ignoring Config Starting_directory value: '+
2315                      GSASIIpath.GetConfigValue('Starting_directory'))
2316
2317    def GetTreeItemsList(self,item):
2318        return self.PatternTree._getTreeItemsList(item)
2319
2320    def OnSize(self,event):
2321        'Called when the main window is resized. Not sure why'
2322        w,h = self.GetClientSizeTuple()
2323        self.mainPanel.SetSize(wx.Size(w,h))
2324        self.PatternTree.SetSize(wx.Size(w,h))
2325                       
2326    def OnPatternTreeSelChanged(self, event):
2327        '''Called when a data tree item is selected'''
2328        if self.TreeItemDelete:
2329            self.TreeItemDelete = False
2330        else:
2331            pltNum = self.G2plotNB.nb.GetSelection()
2332            if pltNum >= 0:                         #to avoid the startup with no plot!
2333                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2334                pltPlot = pltPage.figure
2335            item = event.GetItem()
2336            G2gd.MovePatternTreeToGrid(self,item)
2337            if self.oldFocus:
2338                self.oldFocus.SetFocus()
2339       
2340    def OnPatternTreeItemCollapsed(self, event):
2341        'Called when a tree item is collapsed - all children will be collapsed'
2342        self.PatternTree.CollapseAllChildren(event.GetItem())
2343
2344    def OnPatternTreeItemExpanded(self, event):
2345        'Called when a tree item is expanded'
2346        self.OnPatternTreeSelChanged(event)
2347        event.Skip()
2348       
2349    def OnPatternTreeItemDelete(self, event):
2350        'Called when a tree item is deleted -- not sure what this does'
2351        self.TreeItemDelete = True
2352
2353    def OnPatternTreeItemActivated(self, event):
2354        'Called when a tree item is activated'
2355        event.Skip()
2356       
2357    def OnPatternTreeBeginRDrag(self,event):
2358        event.Allow()
2359        self.BeginDragId = event.GetItem()
2360        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2361        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2362        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2363        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2364        while item:     #G2 data tree has no sub children under a child of a tree item
2365            name = self.PatternTree.GetItemText(item)
2366            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2367            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2368       
2369    def OnPatternTreeEndDrag(self,event):
2370        event.Allow()
2371        self.EndDragId = event.GetItem()
2372        try:
2373            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2374        except:
2375            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2376            NewParent = self.root
2377        if self.ParentId != NewParent:
2378            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2379        else:
2380            Name,Item = self.DragData[0]
2381            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2382            self.PatternTree.SetItemPyData(NewId,Item)
2383            for name,item in self.DragData[1:]:     #loop over children
2384                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2385                self.PatternTree.SetItemPyData(Id,item)
2386            self.PatternTree.Delete(self.BeginDragId)
2387            G2gd.MovePatternTreeToGrid(self,NewId)
2388       
2389    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2390        'Allows stepping through the tree with the up/down arrow keys'
2391        self.oldFocus = wx.Window.FindFocus()
2392        keyevt = event.GetKeyEvent()
2393        key = event.GetKeyCode()
2394        item = self.PatternTree.GetSelection()
2395        if type(item) is int: return # is this the toplevel in tree?
2396        name = self.PatternTree.GetItemText(item)
2397        parent = self.PatternTree.GetItemParent(item)
2398        if key == wx.WXK_UP:
2399            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2400                if type(parent) is int: return # is this the toplevel in tree?
2401                prev = self.PatternTree.GetPrevSibling(parent)
2402                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2403                if NewId:
2404                    self.PatternTree.Collapse(parent)
2405                    self.PatternTree.Expand(prev)
2406                    self.oldFocus = wx.Window.FindFocus()
2407                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2408                else:
2409                    wx.CallAfter(self.PatternTree.SelectItem,item)
2410            else:   
2411                self.PatternTree.GetPrevSibling(item)
2412                self.PatternTree.SelectItem(item)
2413        elif key == wx.WXK_DOWN:
2414            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2415                next = self.PatternTree.GetNextSibling(parent)
2416                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2417                if NewId:
2418                    self.PatternTree.Collapse(parent)
2419                    self.PatternTree.Expand(next)
2420                    self.oldFocus = wx.Window.FindFocus()
2421                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2422                else:
2423                    wx.CallAfter(self.PatternTree.SelectItem,item)
2424            else:   
2425                self.PatternTree.GetNextSibling(item)
2426                self.PatternTree.SelectItem(item)
2427               
2428    def OnReadPowderPeaks(self,event):
2429        'Bound to menu Data/Read Powder Peaks'
2430        Cuka = 1.54052
2431        self.CheckNotebook()
2432        pth = G2G.GetImportPath(self)
2433        if not pth: pth = '.'
2434        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2435            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2436        try:
2437            if dlg.ShowModal() == wx.ID_OK:
2438                self.HKL = []
2439                self.powderfile = dlg.GetPath()
2440                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2441                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2442                data = ['PKS',wave,0.0]
2443                names = ['Type','Lam','Zero'] 
2444                codes = [0,0,0]
2445                inst = [G2IO.makeInstDict(names,data,codes),{}]
2446                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2447                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2448                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2449                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2450                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2451                self.PatternTree.Expand(Id)
2452                self.PatternTree.SelectItem(Id)
2453                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2454        finally:
2455            dlg.Destroy()
2456                       
2457    def OnImageRead(self,event):
2458        '''Called to read in an image in any known format. *** Depreciated. ***
2459        Note: When removed, G2IO.ReadLoadImage can also be removed
2460        '''
2461        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2462        self.CheckNotebook()
2463        dlg = wx.FileDialog(
2464            self, 'Choose image files', '.', '',
2465            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png)|'
2466            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png;*.zip|'
2467            'European detector file (*.edf)|*.edf|'
2468            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2469            'MAR file (*.mar*)|*.mar*|'
2470            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2471            'ADSC Image (*.img)|*.img|'
2472            'Rigaku R-Axis IV (*.stl)|*.stl|'
2473            'GSAS-II Image (*.G2img)|*.G2img|'
2474            'Portable Network Graphics image (*.png)|*.png|'
2475            'Zip archive (*.zip)|*.zip|'
2476            'All files (*.*)|*.*',
2477            wx.OPEN | wx.MULTIPLE)
2478        try:
2479            if dlg.ShowModal() == wx.ID_OK:
2480                imagefiles = dlg.GetPaths()
2481                imagefiles.sort()
2482                for imagefile in imagefiles:
2483                    G2IO.ReadLoadImage(imagefile,self)
2484                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2485                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,self.Image,'Image Controls'))             #show last image to be read
2486        finally:
2487            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2488            os.chdir(path)
2489            dlg.Destroy()
2490
2491    def CheckNotebook(self):
2492        '''Make sure the data tree has the minimally expected controls.
2493        '''
2494        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2495            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2496            self.PatternTree.SetItemPyData(sub,[''])
2497        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2498            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2499            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2500        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2501            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2502            self.PatternTree.SetItemPyData(sub,{})
2503        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2504            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2505            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2506        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2507            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2508            self.PatternTree.SetItemPyData(sub,{})
2509        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2510            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2511            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2512                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2513               
2514    class CopyDialog(wx.Dialog):
2515        '''Creates a dialog for copying control settings between
2516        data tree items'''
2517        def __init__(self,parent,title,text,data):
2518            wx.Dialog.__init__(self,parent,-1,title, 
2519                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2520            self.data = data
2521            panel = wx.Panel(self)
2522            mainSizer = wx.BoxSizer(wx.VERTICAL)
2523            topLabl = wx.StaticText(panel,-1,text)
2524            mainSizer.Add((10,10),1)
2525            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2526            mainSizer.Add((10,10),1)
2527            ncols = len(data)/40+1
2528            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2529            for id,item in enumerate(self.data):
2530                ckbox = wx.CheckBox(panel,id,item[1])
2531                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2532                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2533            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2534            OkBtn = wx.Button(panel,-1,"Ok")
2535            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2536            cancelBtn = wx.Button(panel,-1,"Cancel")
2537            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2538            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2539            btnSizer.Add((20,20),1)
2540            btnSizer.Add(OkBtn)
2541            btnSizer.Add((20,20),1)
2542            btnSizer.Add(cancelBtn)
2543            btnSizer.Add((20,20),1)
2544           
2545            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2546            panel.SetSizer(mainSizer)
2547            panel.Fit()
2548            self.Fit()
2549       
2550        def OnCopyChange(self,event):
2551            id = event.GetId()
2552            self.data[id][0] = self.FindWindowById(id).GetValue()       
2553           
2554        def OnOk(self,event):
2555            parent = self.GetParent()
2556            parent.Raise()
2557            self.EndModal(wx.ID_OK)             
2558           
2559        def OnCancel(self,event):
2560            parent = self.GetParent()
2561            parent.Raise()
2562            self.EndModal(wx.ID_CANCEL)             
2563           
2564        def GetData(self):
2565            return self.data
2566       
2567    class SumDialog(wx.Dialog):
2568        'Allows user to supply scale factor(s) when summing data'
2569        def __init__(self,parent,title,text,dataType,data):
2570            wx.Dialog.__init__(self,parent,-1,title, 
2571                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2572            self.data = data
2573            panel = wx.Panel(self)
2574            mainSizer = wx.BoxSizer(wx.VERTICAL)
2575            topLabl = wx.StaticText(panel,-1,text)
2576            mainSizer.Add((10,10),1)
2577            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2578            mainSizer.Add((10,10),1)
2579            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2580            for id,item in enumerate(self.data[:-1]):
2581                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2582                name.SetEditable(False)
2583                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2584                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2585                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2586                dataGridSizer.Add(scale,0,wx.LEFT,10)
2587                dataGridSizer.Add(name,0,wx.RIGHT,10)
2588            if dataType:
2589                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2590                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2591                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2592                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2593                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2594                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2595            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2596            OkBtn = wx.Button(panel,-1,"Ok")
2597            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2598            cancelBtn = wx.Button(panel,-1,"Cancel")
2599            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2600            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2601            btnSizer.Add((20,20),1)
2602            btnSizer.Add(OkBtn)
2603            btnSizer.Add((20,20),1)
2604            btnSizer.Add(cancelBtn)
2605            btnSizer.Add((20,20),1)
2606           
2607            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2608            panel.SetSizer(mainSizer)
2609            panel.Fit()
2610            self.Fit()
2611
2612        def OnScaleChange(self,event):
2613            id = event.GetId()
2614            value = self.FindWindowById(id).GetValue()
2615            try:
2616                self.data[id][0] = float(value)
2617                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2618            except ValueError:
2619                if value and '-' not in value[0]:
2620                    print 'bad input - numbers only'
2621                    self.FindWindowById(id).SetValue('0.000')
2622           
2623        def OnNameChange(self,event):
2624            self.data[-1] = self.name.GetValue() 
2625           
2626        def OnOk(self,event):
2627            parent = self.GetParent()
2628            parent.Raise()
2629            self.EndModal(wx.ID_OK)             
2630           
2631        def OnCancel(self,event):
2632            parent = self.GetParent()
2633            parent.Raise()
2634            self.EndModal(wx.ID_CANCEL)             
2635           
2636        def GetData(self):
2637            return self.data
2638                       
2639    def OnPwdrSum(self,event):
2640        'Sum together powder data(?)'
2641        TextList = []
2642        DataList = []
2643        SumList = []
2644        Names = []
2645        Inst = None
2646        SumItemList = []
2647        Comments = ['Sum equals: \n']
2648        if self.PatternTree.GetCount():
2649            item, cookie = self.PatternTree.GetFirstChild(self.root)
2650            while item:
2651                name = self.PatternTree.GetItemText(item)
2652                Names.append(name)
2653                if 'PWDR' in name:
2654                    TextList.append([0.0,name])
2655                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2656                    if not Inst:
2657                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2658                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2659            if len(TextList) < 2:
2660                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2661                return
2662            TextList.append('default_sum_name')               
2663            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2664            try:
2665                if dlg.ShowModal() == wx.ID_OK:
2666                    lenX = 0
2667                    Xminmax = [0,0]
2668                    Xsum = []
2669                    Ysum = []
2670                    Vsum = []
2671                    result = dlg.GetData()
2672                    for i,item in enumerate(result[:-1]):
2673                        scale,name = item
2674                        data = DataList[i]
2675                        if scale:
2676                            Comments.append("%10.3f %s" % (scale,' * '+name))
2677                            x,y,w,yc,yb,yd = data   #numpy arrays!
2678                            v = 1./w
2679                            if lenX:
2680                                if lenX != len(x):
2681                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2682                                        '\nExpected:'+str(lenX)+ \
2683                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2684                                    return
2685                            else:
2686                                lenX = len(x)
2687                            if Xminmax[1]:
2688                                if Xminmax != [x[0],x[-1]]:
2689                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2690                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2691                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2692                                    return
2693                                else:
2694                                    for j,yi in enumerate(y):
2695                                         Ysum[j] += scale*yi
2696                                         Vsum[j] += abs(scale)*v[j]
2697                            else:
2698                                Xminmax = [x[0],x[-1]]
2699                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2700                                for j,yi in enumerate(y):
2701                                    Xsum.append(x[j])
2702                                    Ysum.append(scale*yi)
2703                                    Vsum.append(abs(scale*v[j]))
2704                    Wsum = 1./np.array(Vsum)
2705                    outname = 'PWDR '+result[-1]
2706                    Id = 0
2707                    if outname in Names:
2708                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2709                        try:
2710                            if dlg2.ShowModal() == wx.ID_OK:
2711                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2712                                self.PatternTree.Delete(Id)
2713                        finally:
2714                            dlg2.Destroy()
2715                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2716                    if Id:
2717                        Sample = G2pdG.SetDefaultSample()
2718                        valuesdict = {
2719                            'wtFactor':1.0,
2720                            'Dummy':False,
2721                            'ranId':ran.randint(0,sys.maxint),
2722                            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
2723                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2724                            }
2725                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2726                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2727                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2728                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2729                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2730                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2731                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2732                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2733                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2734                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2735                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2736                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2737                        self.PatternTree.SelectItem(Id)
2738                        self.PatternTree.Expand(Id)
2739            finally:
2740                dlg.Destroy()
2741
2742    def OnImageSum(self,event):
2743        'Sum together image data(?)'
2744        TextList = []
2745        DataList = []
2746        SumList = []
2747        Names = []
2748        Inst = []
2749        SumItemList = []
2750        Comments = ['Sum equals: \n']
2751        if self.PatternTree.GetCount():
2752            item, cookie = self.PatternTree.GetFirstChild(self.root)
2753            while item:
2754                name = self.PatternTree.GetItemText(item)
2755                Names.append(name)
2756                if 'IMG' in name:
2757                    TextList.append([0.0,name])
2758                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2759                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2760                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2761            if len(TextList) < 2:
2762                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2763                return
2764            TextList.append('default_sum_name')               
2765            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2766            try:
2767                if dlg.ShowModal() == wx.ID_OK:
2768                    imSize = 0
2769                    result = dlg.GetData()
2770                    First = True
2771                    Found = False
2772                    for i,item in enumerate(result[:-1]):
2773                        scale,name = item
2774                        if scale:
2775                            Found = True                               
2776                            Comments.append("%10.3f %s" % (scale,' * '+name))
2777                            Npix,imagefile,imagetag = DataList[i]
2778                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2779                            if First:
2780                                newImage = np.zeros_like(image)
2781                                First = False
2782                            if imSize:
2783                                if imSize != Npix:
2784                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2785                                        '\nExpected:'+str(imSize)+ \
2786                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2787                                    return
2788                                newImage = newImage+scale*image
2789                            else:
2790                                imSize = Npix
2791                                newImage = newImage+scale*image
2792                            del(image)
2793                    if not Found:
2794                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2795                        return
2796                       
2797                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2798                    outname = 'IMG '+result[-1]
2799                    Id = 0
2800                    if outname in Names:
2801                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2802                        try:
2803                            if dlg2.ShowModal() == wx.ID_OK:
2804                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2805                        finally:
2806                            dlg2.Destroy()
2807                    else:
2808                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2809                    if Id:
2810                        pth = G2G.GetExportPath(self)
2811                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth, '', 
2812                            'G2img files (*.G2img)|*.G2img', 
2813                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
2814                        if dlg.ShowModal() == wx.ID_OK:
2815                            newimagefile = dlg.GetPath()
2816                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2817                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2818                            Imax = np.amax(newImage)
2819                            Imin = np.amin(newImage)
2820                            newImage = []
2821                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2822                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2823                        del(newImage)
2824                        if self.imageDefault:
2825                            Data = copy.copy(self.imageDefault)
2826                        Data['showLines'] = True
2827                        Data['ring'] = []
2828                        Data['rings'] = []
2829                        Data['cutoff'] = 10
2830                        Data['pixLimit'] = 20
2831                        Data['ellipses'] = []
2832                        Data['calibrant'] = ''
2833                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2834                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2835                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2836                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2837                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2838                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2839                        self.PatternTree.SelectItem(Id)
2840                        self.PatternTree.Expand(Id)
2841                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2842                        self.Image = self.PickId
2843            finally:
2844                dlg.Destroy()
2845                     
2846    def OnAddPhase(self,event):
2847        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2848        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2849            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2850        else:
2851            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2852        PhaseName = ''
2853        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2854            style=wx.OK)
2855        if dlg.ShowModal() == wx.ID_OK:
2856            PhaseName = dlg.GetValue()
2857        dlg.Destroy()
2858        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2859        E,SGData = G2spc.SpcGroup('P 1')
2860        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2861       
2862    def OnDeletePhase(self,event):
2863        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2864        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2865        if self.dataFrame:
2866            self.dataFrame.Clear() 
2867        TextList = []
2868        DelList = []
2869        DelItemList = []
2870        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2871            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2872        else:
2873            return
2874        if sub:
2875            item, cookie = self.PatternTree.GetFirstChild(sub)
2876            while item:
2877                TextList.append(self.PatternTree.GetItemText(item))
2878                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2879            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2880            try:
2881                if dlg.ShowModal() == wx.ID_OK:
2882                    result = dlg.GetSelections()
2883                    for i in result: DelList.append([i,TextList[i]])
2884                    item, cookie = self.PatternTree.GetFirstChild(sub)
2885                    i = 0
2886                    while item:
2887                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2888                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2889                        i += 1
2890                    for item in DelItemList:
2891                        name = self.PatternTree.GetItemText(item)
2892                        self.PatternTree.Delete(item)
2893                        self.G2plotNB.Delete(name)
2894                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2895                    while item:
2896                        name = self.PatternTree.GetItemText(item)
2897                        if 'PWDR' in name:
2898                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2899                            refList = self.PatternTree.GetItemPyData(Id)
2900                            if len(refList):
2901                                for i,item in DelList:
2902                                    if item in refList:
2903                                        del(refList[item])
2904                            self.PatternTree.SetItemPyData(Id,refList)
2905                        elif 'HKLF' in name:
2906                            data = self.PatternTree.GetItemPyData(item)
2907                            data[0] = {}
2908                            self.PatternTree.SetItemPyData(item,data)
2909                           
2910                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2911            finally:
2912                dlg.Destroy()
2913               
2914    def OnRenameData(self,event):
2915        'Renames an existing phase. Called by Data/Rename Phase menu'
2916        name = self.PatternTree.GetItemText(self.PickId)     
2917        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2918            dataType = name[:name.index(' ')+1]                 #includes the ' '
2919            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2920                defaultValue=name[name.index(' ')+1:])
2921            try:
2922                if dlg.ShowModal() == wx.ID_OK:
2923                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2924            finally:
2925                dlg.Destroy()
2926       
2927    def GetFileList(self,fileType,skip=None):        #potentially useful?
2928        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2929        fileList = []
2930        Source = ''
2931        id, cookie = self.PatternTree.GetFirstChild(self.root)
2932        while id:
2933            name = self.PatternTree.GetItemText(id)
2934            if fileType in name:
2935                if id == skip:
2936                    Source = name
2937                else:
2938                    fileList.append([False,name,id])
2939            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2940        if skip:
2941            return fileList,Source
2942        else:
2943            return fileList
2944           
2945    def OnDataDelete(self, event):
2946        '''Delete one or more histograms from data tree. Called by the
2947        Data/DeleteData menu
2948        '''
2949        TextList = []
2950        DelList = []
2951        DelItemList = []
2952        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
2953        ifPWDR = False
2954        ifSASD = False
2955        ifIMG = False
2956        ifHKLF = False
2957        ifPDF = False
2958        if self.PatternTree.GetCount():
2959            item, cookie = self.PatternTree.GetFirstChild(self.root)
2960            while item:
2961                name = self.PatternTree.GetItemText(item)
2962                if name not in ['Notebook','Controls','Covariance','Constraints',
2963                    'Restraints','Phases','Rigid bodies','Sequential results']:
2964                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
2965                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
2966                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
2967                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
2968                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
2969                    TextList.append(name)
2970                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2971            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2972            try:
2973                if dlg.ShowModal() == wx.ID_OK:
2974                    result = dlg.GetSelections()
2975                    for i in result: DelList.append(TextList[i])
2976                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2977                    while item:
2978                        itemName = self.PatternTree.GetItemText(item)
2979                        if itemName in DelList:
2980                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
2981                            elif 'SASD' in itemName: nItems['SASD'] -= 1
2982                            elif 'IMG' in itemName: nItems['IMG'] -= 1
2983                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
2984                            elif 'PDF' in itemName: nItems['PDF'] -= 1
2985                            DelItemList.append(item)
2986                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2987                    for item in DelItemList:
2988                        self.PatternTree.Delete(item)
2989                    self.PickId = 0
2990                    self.PickIdText = None
2991                    self.PatternId = 0
2992                    if nItems['PWDR']:
2993                        wx.CallAfter(G2plt.PlotPatterns,self,True)
2994                    else:
2995                        self.G2plotNB.Delete('Powder Patterns')
2996                    if not nItems['IMG']:
2997                        self.G2plotNB.Delete('2D Powder Image')
2998                    if not nItems['HKLF']:
2999                        self.G2plotNB.Delete('Structure Factors')
3000                        if '3D Structure Factors' in self.G2plotNB.plotList:
3001                            self.G2plotNB.Delete('3D Structure Factors')
3002            finally:
3003                dlg.Destroy()
3004
3005    def OnFileOpen(self, event, filename=None):
3006        '''Gets a GSAS-II .gpx project file in response to the
3007        File/Open Project menu button
3008        '''
3009        result = wx.ID_OK
3010        self.EnablePlot = False
3011        if self.PatternTree.GetChildrenCount(self.root,False):
3012            if self.dataFrame:
3013                self.dataFrame.Clear() 
3014            dlg = wx.MessageDialog(
3015                self,
3016                'Do you want to overwrite the current project? '+
3017                'Any unsaved changes in current project will be lost. Press OK to continue.',
3018                'Overwrite?',  wx.OK | wx.CANCEL)
3019            try:
3020                result = dlg.ShowModal()
3021                if result == wx.ID_OK:
3022                    self.PatternTree.DeleteChildren(self.root)
3023                    self.GSASprojectfile = ''
3024                    self.HKL = []
3025                    if self.G2plotNB.plotList:
3026                        self.G2plotNB.clear()
3027            finally:
3028                dlg.Destroy()
3029        if result != wx.ID_OK: return
3030
3031        if not filename:
3032            if self.dataDisplay: self.dataDisplay.Destroy()
3033            if self.LastGPXdir:
3034                pth = self.LastGPXdir
3035            else:
3036                pth = '.'
3037            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3038                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3039            try:
3040                if dlg.ShowModal() != wx.ID_OK: return
3041                self.GSASprojectfile = dlg.GetPath()
3042                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3043                self.dirname = dlg.GetDirectory()
3044            finally:
3045                dlg.Destroy()
3046        else:
3047            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3048            self.dirname = os.path.split(filename)[0]
3049
3050        try:
3051            self.StartProject()         #open the file if possible
3052        except:
3053            print '\nError opening file ',filename
3054            import traceback
3055            print traceback.format_exc()
3056       
3057    def StartProject(self):
3058        '''Opens a GSAS-II project file & selects the 1st available data set to
3059        display (PWDR, HKLF or SASD)
3060        '''
3061       
3062        Id = 0
3063        G2IO.ProjFileOpen(self)
3064        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3065        self.PatternTree.Expand(self.root)
3066        self.HKL = []
3067        item, cookie = self.PatternTree.GetFirstChild(self.root)
3068        while item and not Id:
3069            name = self.PatternTree.GetItemText(item)
3070            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3071                Id = item
3072            elif name == 'Controls':
3073                data = self.PatternTree.GetItemPyData(item)
3074                if data:
3075                    for item in self.Refine: item.Enable(True)
3076                    self.EnableSeqRefineMenu()
3077            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3078        if Id:
3079            self.EnablePlot = True
3080            self.PatternTree.SelectItem(Id)
3081        self.CheckNotebook()
3082        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3083        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3084        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3085        self.LastGPXdir = pth
3086
3087    def OnFileClose(self, event):
3088        '''Clears the data tree in response to the
3089        File/New Project menu button. User is given option to save
3090        the project.
3091        '''
3092        if self.dataFrame:
3093            self.dataFrame.Clear()
3094            self.dataFrame.SetLabel('GSAS-II data display') 
3095        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3096        try:
3097            result = dlg.ShowModal()
3098            if result == wx.ID_OK:
3099                self.OnFileSaveMenu(event)
3100            if result != wx.ID_CANCEL:
3101                self.GSASprojectfile = ''
3102                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3103                self.PatternTree.DeleteChildren(self.root)
3104                if self.HKL: self.HKL = []
3105                if self.G2plotNB.plotList:
3106                    self.G2plotNB.clear()
3107        finally:
3108            dlg.Destroy()
3109
3110    def OnFileSave(self, event):
3111        '''Save the current project in response to the
3112        File/Save Project menu button
3113        '''
3114       
3115        if self.GSASprojectfile: 
3116            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3117            self.CheckNotebook()
3118            G2IO.ProjFileSave(self)
3119        else:
3120            self.OnFileSaveas(event)
3121
3122    def OnFileSaveas(self, event):
3123        '''Save the current project in response to the
3124        File/Save as menu button
3125        '''
3126        if GSASIIpath.GetConfigValue('Starting_directory'):
3127            pth = GSASIIpath.GetConfigValue('Starting_directory')
3128            pth = os.path.expanduser(pth) 
3129        elif self.LastGPXdir:
3130            pth = self.LastGPXdir
3131        else:
3132            pth = '.'
3133        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3134            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3135        try:
3136            if dlg.ShowModal() == wx.ID_OK:
3137                self.GSASprojectfile = dlg.GetPath()
3138                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3139                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3140                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3141                self.CheckNotebook()
3142                G2IO.ProjFileSave(self)
3143                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3144        finally:
3145            dlg.Destroy()
3146
3147    def ExitMain(self, event):
3148        '''Called if the main window is closed'''
3149        if self.G2plotNB:
3150            self.G2plotNB.Destroy()
3151        if self.dataFrame:
3152            self.dataFrame.Clear() 
3153            self.dataFrame.Destroy()
3154        if self.undofile:
3155            os.remove(self.undofile)
3156        sys.exit()
3157       
3158    def OnFileExit(self, event):
3159        '''Called in response to the File/Quit menu button'''
3160        if self.G2plotNB:
3161            self.G2plotNB.Destroy()
3162        if self.dataFrame:
3163            self.dataFrame.Clear() 
3164            self.dataFrame.Destroy()
3165        self.Close()
3166       
3167    def OnExportPeakList(self,event):
3168        nptand = lambda x: np.tan(x*math.pi/180.)
3169        pth = G2G.GetExportPath(self)
3170        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3171            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3172        try:
3173            if dlg.ShowModal() == wx.ID_OK:
3174                self.peaklistfile = dlg.GetPath()
3175                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3176                file = open(self.peaklistfile,'w')               
3177                item, cookie = self.PatternTree.GetFirstChild(self.root)
3178                while item:
3179                    name = self.PatternTree.GetItemText(item)
3180                    if 'PWDR' in name:
3181                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3182                        wave = 0.0
3183                        while item2:
3184                            name2 = self.PatternTree.GetItemText(item2)
3185                            if name2 == 'Instrument Parameters':
3186                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3187                                Type = Inst['Type'][0]
3188                                if 'T' not in Type:
3189                                    wave = G2mth.getWave(Inst)
3190                            elif name2 == 'Peak List':
3191                                pkdata = self.PatternTree.GetItemPyData(item2)
3192                                peaks = pkdata['peaks']
3193                                sigDict = pkdata['sigDict']
3194                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3195                        file.write("#%s \n" % (name+' Peak List'))
3196                        if wave:
3197                            file.write('#wavelength = %10.6f\n'%(wave))
3198                        if 'T' in Type:
3199                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3200                        else:
3201                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3202                        for ip,peak in enumerate(peaks):
3203                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3204                            if 'T' in Type:  #TOF - more cols
3205                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3206                                for name in esds.keys():
3207                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3208                                sig = np.sqrt(peak[8])
3209                                gam = peak[10]
3210                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3211                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3212                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3213                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3214                            else:               #CW
3215                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3216                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3217                                for name in esds.keys():
3218                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3219                                sig = np.sqrt(peak[4]) #var -> sig
3220                                gam = peak[6]
3221                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3222                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3223                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3224                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3225                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3226                file.close()
3227        finally:
3228            dlg.Destroy()
3229       
3230    def OnExportHKL(self,event):
3231        pth = G2G.GetExportPath(self)
3232        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3233            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3234        try:
3235            if dlg.ShowModal() == wx.ID_OK:
3236                self.peaklistfile = dlg.GetPath()
3237                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3238                file = open(self.peaklistfile,'w')               
3239                item, cookie = self.PatternTree.GetFirstChild(self.root)
3240                while item:
3241                    name = self.PatternTree.GetItemText(item)
3242                    if 'PWDR' in name:
3243                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3244                        while item2:
3245                            name2 = self.PatternTree.GetItemText(item2)
3246                            if name2 == 'Reflection Lists':
3247                                data = self.PatternTree.GetItemPyData(item2)
3248                                phases = data.keys()
3249                                for phase in phases:
3250                                    peaks = data[phase]
3251                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3252                                    if 'T' in peaks.get('Type','PXC'):
3253                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3254                                    else:               
3255                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3256                                    for peak in peaks['RefList']:
3257                                        if 'T' in peaks.get('Type','PXC'):
3258                                            sig = np.sqrt(peak[6])
3259                                            gam = peak[7]
3260                                            FWHM = G2pwd.getgamFW(gam,sig)
3261                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3262                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3263                                        else:
3264                                            sig = np.sqrt(peak[6])
3265                                            gam = peak[7]
3266                                            FWHM = G2pwd.getgamFW(gam,sig)
3267                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3268                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3269                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3270                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3271                file.close()
3272        finally:
3273            dlg.Destroy()
3274       
3275    def OnExportPDF(self,event):
3276        #need S(Q) and G(R) to be saved here - probably best from selection?
3277        names = ['All']
3278        exports = []
3279        item, cookie = self.PatternTree.GetFirstChild(self.root)
3280        while item:
3281            name = self.PatternTree.GetItemText(item)
3282            if 'PDF' in name:
3283                names.append(name)
3284            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3285        if names:
3286            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3287            if dlg.ShowModal() == wx.ID_OK:
3288                sel = dlg.GetSelections()
3289                if sel[0] == 0:
3290                    exports = names[1:]
3291                else:
3292                    for x in sel:
3293                        exports.append(names[x])
3294            dlg.Destroy()
3295        if exports:
3296            G2IO.PDFSave(self,exports)
3297       
3298    def OnMakePDFs(self,event):
3299        '''Calculates PDFs
3300        '''
3301        sind = lambda x: math.sin(x*math.pi/180.)
3302        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3303        TextList = ['All PWDR']
3304        PDFlist = []
3305        Names = []
3306        if self.PatternTree.GetCount():
3307            id, cookie = self.PatternTree.GetFirstChild(self.root)
3308            while id:
3309                name = self.PatternTree.GetItemText(id)
3310                Names.append(name)
3311                if 'PWDR' in name:
3312                    TextList.append(name)
3313                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3314            if len(TextList) == 1:
3315                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3316                return
3317            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3318            try:
3319                if dlg.ShowModal() == wx.ID_OK:
3320                    result = dlg.GetSelections()
3321                    for i in result: PDFlist.append(TextList[i])
3322                    if 0 in result:
3323                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3324                    for item in PDFlist:
3325                        PWDRname = item[4:]
3326                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3327                        Data = {
3328                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3329                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3330                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3331                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3332                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3333                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3334                            'Lorch':True,}
3335                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3336                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3337                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3338                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3339                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3340                for item in self.ExportPDF: item.Enable(True)
3341            finally:
3342                dlg.Destroy()
3343               
3344    def GetPWDRdatafromTree(self,PWDRname):
3345        ''' Returns powder data from GSASII tree
3346
3347        :param str PWDRname: a powder histogram name as obtained from
3348          :meth:`GSASIIstruct.GetHistogramNames`
3349
3350        :returns: PWDRdata = powder data dictionary with
3351          Powder data arrays, Limits, Instrument Parameters,
3352          Sample Parameters           
3353        '''
3354        PWDRdata = {}
3355        try:
3356            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3357        except ValueError:
3358            PWDRdata['wtFactor'] = 1.0
3359        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3360        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3361        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3362        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3363        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3364        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3365        if 'ranId' not in PWDRdata:  # patch, add a random Id
3366            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3367        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3368            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3369        return PWDRdata
3370
3371    def GetHKLFdatafromTree(self,HKLFname):
3372        ''' Returns single crystal data from GSASII tree
3373
3374        :param str HKLFname: a single crystal histogram name as obtained
3375          from
3376          :meth:`GSASIIstruct.GetHistogramNames`
3377
3378        :returns: HKLFdata = single crystal data list of reflections
3379
3380        '''
3381        HKLFdata = {}
3382        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3383#        try:
3384#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3385#        except ValueError:
3386#            HKLFdata['wtFactor'] = 1.0
3387        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3388        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3389        return HKLFdata
3390       
3391    def GetPhaseData(self):
3392        '''Returns a dict with defined phases.
3393        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3394        get same info from GPX file.
3395        '''
3396        phaseData = {}
3397        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3398            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3399        else:
3400            print 'no phases found in GetPhaseData'
3401            sub = None
3402        if sub:
3403            item, cookie = self.PatternTree.GetFirstChild(sub)
3404            while item:
3405                phaseName = self.PatternTree.GetItemText(item)
3406                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3407                if 'ranId' not in phaseData[phaseName]:
3408                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3409                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3410        return phaseData
3411
3412    def GetPhaseInfofromTree(self):
3413        '''Get the phase names and their rId values,
3414        also the histograms used in each phase.
3415
3416        :returns: (phaseRIdList, usedHistograms) where
3417
3418          * phaseRIdList is a list of random Id values for each phase
3419          * usedHistograms is a dict where the keys are the phase names
3420            and the values for each key are a list of the histogram names
3421            used in each phase.
3422        '''
3423        phaseRIdList = []
3424        usedHistograms = {}
3425        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3426        if sub:
3427            item, cookie = self.PatternTree.GetFirstChild(sub)
3428            while item:
3429                phaseName = self.PatternTree.GetItemText(item)
3430                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3431                if ranId: phaseRIdList.append(ranId)
3432                data = self.PatternTree.GetItemPyData(item)
3433                UseList = data['Histograms']
3434                usedHistograms[phaseName] = UseList.keys()
3435                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3436        return phaseRIdList,usedHistograms
3437
3438    def GetPhaseNames(self):
3439        '''Returns a list of defined phases.
3440        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3441        get same info from GPX file.
3442        '''
3443        phaseNames = []
3444        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3445            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3446        else:
3447            print 'no phases found in GetPhaseNames'
3448            sub = None
3449        if sub:
3450            item, cookie = self.PatternTree.GetFirstChild(sub)
3451            while item:
3452                phase = self.PatternTree.GetItemText(item)
3453                phaseNames.append(phase)
3454                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3455        return phaseNames
3456   
3457    def GetHistogramNames(self,hType):
3458        """ Returns a list of histogram names found in the GSASII data tree
3459        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3460        get same info from GPX file.
3461       
3462        :param str hType: list of histogram types
3463        :return: list of histogram names
3464       
3465        """
3466        HistogramNames = []
3467        if self.PatternTree.GetCount():
3468            item, cookie = self.PatternTree.GetFirstChild(self.root)
3469            while item:
3470                name = self.PatternTree.GetItemText(item)
3471                if name[:4] in hType:
3472                    HistogramNames.append(name)       
3473                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3474
3475        return HistogramNames
3476                   
3477    def GetUsedHistogramsAndPhasesfromTree(self):
3478        ''' Returns all histograms that are found in any phase
3479        and any phase that uses a histogram.
3480        This also assigns numbers to used phases and histograms by the
3481        order they appear in the file.
3482        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3483        get same info from GPX file.
3484
3485        :returns: (Histograms,Phases)
3486
3487            * Histograms = dictionary of histograms as {name:data,...}
3488            * Phases = dictionary of phases that use histograms
3489        '''
3490        Histograms = {}
3491        Phases = {}
3492        phaseNames = self.GetPhaseNames()
3493        phaseData = self.GetPhaseData()
3494        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3495
3496        for phase in phaseData:
3497            Phase = phaseData[phase]
3498            pId = phaseNames.index(phase)
3499            Phase['pId'] = pId
3500            if Phase['Histograms']:
3501                if phase not in Phases:
3502                    Phases[phase] = Phase
3503                for hist in Phase['Histograms']:
3504                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3505                        Phase['Histograms'][hist]['Use'] = True         
3506                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3507                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3508                        if item:
3509                            if 'PWDR' in hist[:4]: 
3510                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3511                            elif 'HKLF' in hist[:4]:
3512                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3513                            hId = histoList.index(hist)
3514                            Histograms[hist]['hId'] = hId
3515                        else: # would happen if a referenced histogram were renamed or deleted
3516                            print('For phase "'+str(phase)+
3517                                  '" unresolved reference to histogram "'+str(hist)+'"')
3518        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3519        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3520        return Histograms,Phases
3521       
3522    def MakeLSParmDict(self):
3523        '''Load all parameters used for computation from the tree into a
3524        dict of paired values [value, refine flag]. Note that this is
3525        different than the parmDict used in the refinement, which only has
3526        values.
3527
3528        Note that similar things are done in
3529        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3530        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3531        a GPX file).
3532
3533        :returns: (parmDict,varyList) where:
3534
3535         * parmDict is a dict with values and refinement flags
3536           for each parameter and
3537         * varyList is a list of variables (refined parameters).
3538        '''
3539        parmDict = {}
3540        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3541        for phase in Phases:
3542            if 'pId' not in Phases[phase]:
3543                self.ErrorDialog('View parameter error','You must run least squares at least once')
3544                raise Exception,'No pId for phase '+str(phase)
3545        rigidbodyDict = self.PatternTree.GetItemPyData(   
3546            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3547        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3548        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3549        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3550        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3551        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3552        varyList = rbVary+phaseVary+hapVary+histVary
3553        parmDict.update(rbDict)
3554        parmDict.update(phaseDict)
3555        parmDict.update(hapDict)
3556        parmDict.update(histDict)
3557        for parm in parmDict:
3558            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3559                'Omega','Chi','Phi','nDebye','nPeaks']:
3560                parmDict[parm] = [parmDict[parm],'-']
3561            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3562                parmDict[parm] = [parmDict[parm],'-']
3563            elif parm in varyList:
3564                parmDict[parm] = [parmDict[parm],'T']
3565            else:
3566                parmDict[parm] = [parmDict[parm],'F']
3567        # for i in parmDict: print i,'\t',parmDict[i]
3568        # fl = open('parmDict.dat','wb')
3569        # import cPickle
3570        # cPickle.dump(parmDict,fl,1)
3571        # fl.close()
3572        return parmDict,varyList
3573
3574    def ShowLSParms(self,event):
3575        '''Displays a window showing all parameters in the refinement.
3576        Called from the Calculate/View LS Parms menu.
3577        '''
3578        parmDict,varyList = self.MakeLSParmDict()
3579        parmValDict = {}
3580        for i in parmDict:
3581            parmValDict[i] = parmDict[i][0]
3582           
3583        reqVaryList = tuple(varyList) # save requested variables
3584        try:
3585            # process constraints
3586            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3587            Constraints = self.PatternTree.GetItemPyData(sub)
3588            constList = []
3589            for item in Constraints:
3590                if item.startswith('_'): continue
3591                constList += Constraints[item]
3592            G2mv.InitVars()
3593            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3594            groups,parmlist = G2mv.GroupConstraints(constrDict)
3595            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3596            G2mv.Map2Dict(parmValDict,varyList)
3597        except:
3598            pass
3599        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3600        dlg.ShowModal()
3601        dlg.Destroy()
3602       
3603    def OnRefine(self,event):
3604        '''Perform a refinement.
3605        Called from the Calculate/Refine menu.
3606        '''       
3607        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3608        if Id:
3609            dlg = wx.MessageDialog(
3610                self,
3611                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3612                'Remove sequential results?',wx.OK|wx.CANCEL)
3613            if dlg.ShowModal() == wx.ID_OK:
3614                self.PatternTree.Delete(Id)
3615                dlg.Destroy()
3616            else:
3617                dlg.Destroy()
3618                return
3619        self.OnFileSave(event)
3620        # check that constraints are OK here
3621        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3622        if errmsg:
3623            self.ErrorDialog('Refinement error',errmsg)
3624            return
3625        if warnmsg:
3626            print('Conflict between refinment flag settings and constraints:\n'+
3627                warnmsg+'\nRefinement not possible')
3628            self.ErrorDialog('Refinement Flag Error',
3629                'Conflict between refinement flag settings and constraints:\n'+
3630                warnmsg+'\nRefinement not possible')
3631            return
3632        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3633            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3634            parent=self)
3635        Size = dlg.GetSize()
3636        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3637            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3638        dlg.CenterOnParent()
3639        Rw = 100.00
3640        oldId =  self.PatternTree.GetSelection()        #retain current selection
3641        oldPath = self.GetTreeItemsList(oldId)
3642        parentName = ''
3643        oldName = self.PatternTree.GetItemText(oldId)
3644        parentId = self.PatternTree.GetItemParent(oldId)
3645        if parentId:
3646            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3647            if 'Phases' in parentName:
3648                tabId = self.dataDisplay.GetSelection()
3649        try:
3650            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)
3651        finally:
3652            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3653            dlg.Destroy()
3654            wx.Yield()
3655        if OK:
3656            Rw = Msg
3657            dlg2 = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3658            try:
3659                if dlg2.ShowModal() == wx.ID_OK:
3660                    Id = 0
3661                    self.PatternTree.DeleteChildren(self.root)
3662                    self.HKL = []
3663                    G2IO.ProjFileOpen(self)
3664                    Id =  self.root
3665                    txt = None
3666                    for txt in oldPath:
3667                        Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3668                    self.PickIdText = None  #force reload of page
3669                    self.PickId = Id
3670                    self.PatternTree.SelectItem(Id)
3671                    G2gd.MovePatternTreeToGrid(self,Id)
3672            finally:
3673                dlg2.Destroy()
3674        else:
3675            self.ErrorDialog('Refinement error',Msg)
3676
3677    def OnSeqRefine(self,event):
3678        '''Perform a sequential refinement.
3679        Called from the Calculate/Sequential refine menu.
3680        '''       
3681        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3682        if not Id:
3683            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3684            self.PatternTree.SetItemPyData(Id,{})           
3685        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3686        Controls['ShowCell'] = True
3687        self.OnFileSave(event)
3688        # check that constraints are OK here
3689        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3690        if errmsg:
3691            self.ErrorDialog('Refinement error',errmsg)
3692            return
3693        if warnmsg:
3694            print('Conflict between refinment flag settings and constraints:\n'+
3695                  warnmsg+'\nRefinement not possible')
3696            self.ErrorDialog('Refinement Flag Error',
3697                             'Conflict between refinment flag settings and constraints:\n'+
3698                             warnmsg+'\nRefinement not possible')
3699            return
3700        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3701            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3702            parent=self)           
3703        Size = dlg.GetSize()
3704        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3705            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3706        dlg.CenterOnParent()
3707        try:
3708            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3709        finally:
3710            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3711            dlg.Destroy()
3712            wx.Yield()
3713        if OK:
3714            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3715            try:
3716                if dlg.ShowModal() == wx.ID_OK:
3717                    Id = 0
3718                    self.PickIdText = None  #force reload of PickId contents
3719                    self.PatternTree.DeleteChildren(self.root)
3720                    if len(self.HKL): self.HKL = []
3721                    if self.G2plotNB.plotList:
3722                        self.G2plotNB.clear()
3723                    G2IO.ProjFileOpen(self)
3724                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3725                    self.PatternTree.SelectItem(Id)
3726   
3727            finally:
3728                dlg.Destroy()
3729        else:
3730            self.ErrorDialog('Sequential refinement error',Msg)
3731       
3732    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3733        'Display an error message'
3734        result = None
3735        if parent is None:
3736            dlg = wx.MessageDialog(self, message, title,  wtype)
3737        else:
3738            dlg = wx.MessageDialog(parent, message, title,  wtype)
3739            dlg.CenterOnParent() # not working on Mac
3740        try:
3741            result = dlg.ShowModal()
3742        finally:
3743            dlg.Destroy()
3744        return result
3745   
3746    def OnSaveMultipleImg(self,event):
3747        '''Select and save multiple image parameter and mask files
3748        '''
3749        G2IO.SaveMultipleImg(self)
3750       
3751class GSASIImain(wx.App):
3752    '''Defines a wxApp for GSAS-II
3753
3754    Creates a wx frame (self.main) which contains the display of the
3755    data tree.
3756    '''
3757    def OnInit(self):
3758        '''Called automatically when the app is created.'''
3759        import platform
3760        if '2.7' not in sys.version[:5]:
3761            dlg = wx.MessageDialog(None, 
3762                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
3763                'Python version error',  wx.OK)
3764            try:
3765                result = dlg.ShowModal()
3766            finally:
3767                dlg.Destroy()
3768            sys.exit()
3769        self.main = GSASII(None)
3770        self.main.Show()
3771        self.SetTopWindow(self.main)
3772        # save the current package versions
3773        self.main.PackageVersions = []
3774        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
3775        for p in (wx,mpl,np,sp,ogl):
3776            self.main.PackageVersions.append([p.__name__,p.__version__])
3777        try:
3778            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
3779        except:
3780            try:
3781                from PIL import PILLOW_VERSION
3782                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
3783            except:
3784                pass
3785        self.main.PackageVersions.append([' Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
3786       
3787        return True
3788    # def MacOpenFile(self, filename):
3789    #     '''Called on Mac every time a file is dropped on the app when it is running,
3790    #     treat this like a File/Open project menu action.
3791    #     Should be ignored on other platforms
3792    #     '''
3793    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3794    #     print 'MacOpen',filename
3795    #     if os.path.splitext(filename)[1] == '.py': return
3796    #     # end PATCH
3797    #     self.main.OnFileOpen(None,filename)
3798    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3799       
3800def main():
3801    '''Start up the GSAS-II application'''
3802    #application = GSASIImain() # don't redirect output, someday we
3803    # may want to do this if we can
3804    application = GSASIImain(0)
3805    if GSASIIpath.GetConfigValue('wxInspector'):
3806        import wx.lib.inspection as wxeye
3807        wxeye.InspectionTool().Show()
3808
3809    #application.main.OnRefine(None)
3810    application.MainLoop()
3811   
3812if __name__ == '__main__':
3813    # print versions
3814    print "Python module versions loaded:"
3815    print "python:     ",sys.version.split()[0]
3816    print "wxpython:   ",wx.__version__
3817    print "matplotlib: ",mpl.__version__
3818    print "numpy:      ",np.__version__
3819    print "scipy:      ",sp.__version__
3820    print "OpenGL:     ",ogl.__version__
3821    try:
3822        from PIL import Image
3823        try:
3824            from PIL import PILLOW_VERSION
3825            version = PILLOW_VERSION
3826        except:
3827            version = Image.VERSION
3828        print "pillow:     ",version
3829    except ImportError:
3830        try:
3831            import Image
3832            print "Image (PIL):",Image.VERSION
3833        except ImportError:
3834            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3835    try:
3836        import mkl
3837        print "Max threads ",mkl.get_max_threads()
3838    except:
3839        pass
3840    import platform
3841    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3842    #print "wxPython description",wx.PlatformInfo
3843    print "This is GSAS-II version:     ",__version__,' revision #'+str(GSASIIpath.GetVersionNumber())
3844    GSASIIpath.InvokeDebugOpts()
3845    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.