source: trunk/GSASII.py @ 2777

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

implement import of getPDFx G(R) files NB: these have no matching PWDR entries
replace all scipy.fft with numpy.fft
add a plot SLD button for reflectometry
for PDF Peaks - Atom elements from periodic table - Bond No. still not working so doesn't really matter

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