source: trunk/GSASII.py @ 2297

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

implement creation of summed file from multi-frame ge image files; creates a G2img file which is put on tree in place of multi frame file. Subsequent accesses get teg G2img file.
Insert skipPageChange = True to prevent plot tab change causing tree change from Strain plots & Image plots

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