source: trunk/GSASII.py @ 2246

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

trap another Id=0 problem after Refine finishes (G2.py)
rearrange routines in G2plot; refactor OnPageChanged? to allow (currently commented out) diagnostic prints. "fix" flashing problem by commenting out self.skipPageChange = False line.

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