source: trunk/GSASII.py @ 2754

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

fix "off-by-one" problem in powder profiles; calc missed last point - now fixed.
add import of reflectometry data - same as small angle data
implement plotting of reflectometry data & all the scale options
set up the Model page for reflectometry (Layers is missing)

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