source: trunk/GSASII.py @ 2159

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

add importer for FullProf? .dat powder files

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