source: trunk/GSASII.py @ 2341

Last change on this file since 2341 was 2341, checked in by toby, 7 years ago

improve powder imports: suppress printing of binary data; improve validation of old GSAS binary files

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