source: trunk/GSASII.py @ 2150

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

incomplete mods to import powder - suggest not using until I fix it

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