source: trunk/GSASII.py @ 2770

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

fix import new phase behavior - wasn't finishing properly
fix HKL display when cell parms changed in Unit Cells GUI
fix problem with newLeBail flag

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