source: trunk/GSASII.py @ 2215

Last change on this file since 2215 was 2215, checked in by toby, 6 years ago

load images to tree as read in

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