source: trunk/GSASII.py @ 2412

Last change on this file since 2412 was 2412, checked in by vondreele, 5 years ago

fixes to PDF stuff

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