source: trunk/GSASII.py @ 2788

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

make I/Ib floats in ImageCalibrants?.py
fix issue for SASD seq. refinement
fix some issues with SASD image integrations

  • 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.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2017-04-17 19:12:29 +0000 (Mon, 17 Apr 2017) $
6# $Author: vondreele $
7# $Revision: 2788 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2788 2017-04-17 19:12:29Z 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: 2788 $")
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            ifDQ = np.any(rd.reflectometrydata[5])
2063            valuesdict = {
2064                'wtFactor':1.0,
2065                'Dummy':False,
2066                'ranId':ran.randint(0,sys.maxint),
2067                'Offset':[0.0,0.0],
2068                'ifDQ':ifDQ
2069                }
2070            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
2071            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.reflectometrydata])
2072            self.PatternTree.SetItemPyData(
2073                self.PatternTree.AppendItem(Id,text='Comments'),
2074                rd.comments)
2075            self.PatternTree.SetItemPyData(
2076                self.PatternTree.AppendItem(Id,text='Limits'),
2077                [(Tmin,Tmax),[Tmin,Tmax]])
2078            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
2079            self.PatternTree.SetItemPyData(
2080                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
2081                [Iparm1,Iparm2])
2082            self.PatternTree.SetItemPyData(
2083                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
2084            self.PatternTree.SetItemPyData(
2085                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
2086                rd.Sample)
2087            self.PatternTree.SetItemPyData(
2088                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultREFDModel())
2089            newHistList.append(HistName)
2090        else:
2091            self.EnablePlot = True
2092            self.PatternTree.Expand(Id)
2093            self.PatternTree.SelectItem(Id)
2094           
2095        if not newHistList: return # somehow, no new histograms
2096        return # success
2097
2098    def _Add_ImportMenu_PDF(self,parent):
2099        '''configure the PDF Data menus accord to the readers found in _init_Imports
2100        '''
2101        submenu = wx.Menu()
2102        item = parent.AppendMenu(wx.ID_ANY, 'PDF G(R) Data',
2103            submenu, help='Import PDF G(R) data')
2104        for reader in self.ImportPDFReaderlist:
2105            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
2106                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
2107            self.ImportMenuId[item.GetId()] = reader
2108            self.Bind(wx.EVT_MENU, self.OnImportPDF, id=item.GetId())
2109        # item = submenu.Append(wx.ID_ANY,
2110        #     help='Import reflectometry data, use file to try to determine format',
2111        #     kind=wx.ITEM_NORMAL,text='guess format from file')
2112        # self.Bind(wx.EVT_MENU, self.OnImportReflectometry, id=item.GetId())
2113       
2114    def OnImportPDF(self,event):
2115        '''Called in response to an Import/PDF G(R) Data/... menu item
2116        to read a PDF G(R) data set.
2117        dict self.ImportMenuId is used to look up the specific
2118        reader item associated with the menu item, which will be
2119        None for the last menu item, which is the "guess" option
2120        where all appropriate formats will be tried.
2121
2122        '''
2123       
2124        # get a list of existing histograms
2125        PDFlist = []
2126        if self.PatternTree.GetCount():
2127            item, cookie = self.PatternTree.GetFirstChild(self.root)
2128            while item:
2129                name = self.PatternTree.GetItemText(item)
2130                if name.startswith('PDF ') and name not in PDFlist:
2131                    PDFlist.append(name)
2132                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2133        # look up which format was requested
2134        reqrdr = self.ImportMenuId.get(event.GetId()) 
2135        rdlist = self.OnImportGeneric(
2136            reqrdr,self.ImportPDFReaderlist,'PDF G(R) Data',multiple=True)
2137        if len(rdlist) == 0: return
2138        self.CheckNotebook()
2139        newHistList = []
2140        self.EnablePlot = False
2141        for rd in rdlist:
2142            HistName = rd.idstring
2143            HistName = 'PDF '+HistName
2144            # make new histogram names unique
2145            HistName = G2obj.MakeUniqueLabel(HistName,PDFlist)
2146            print 'Read PDF G(R) data '+HistName+ \
2147                ' from file '+self.lastimport
2148            # data are read, now store them in the tree
2149            Id = self.PatternTree.AppendItem(self.root,text=HistName)
2150            Ymin = np.min(rd.pdfdata[1])                 
2151            Ymax = np.max(rd.pdfdata[1])                 
2152            valuesdict = {
2153                'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),
2154                'Offset':[0.0,0.0],'delOffset':0.02*Ymax,
2155                'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
2156                }
2157            self.PatternTree.SetItemPyData(
2158                self.PatternTree.AppendItem(Id,text='PDF Controls'),
2159                    {'G(R)':[valuesdict,rd.pdfdata,HistName],'diffGRname':'','diffMult':1.0})
2160            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Peaks'),
2161                {'Limits':[1.,5.],'Background':[2,[0.,-0.2*np.pi],False],'Peaks':[]})
2162        else:
2163            self.EnablePlot = True
2164            self.PatternTree.Expand(Id)
2165            self.PatternTree.SelectItem(Id)
2166           
2167        if not newHistList: return # somehow, no new histograms
2168        return # success
2169       
2170###############################################################################
2171#Command logging
2172###############################################################################
2173
2174    def OnMacroRecordStatus(self,event,setvalue=None):
2175        '''Called when the record macro menu item is used which toggles the
2176        value. Alternately a value to be set can be provided. Note that this
2177        routine is made more complex because on the Mac there are lots of menu
2178        items (listed in self.MacroStatusList) and this loops over all of them.
2179        '''
2180        nextvalue = log.ShowLogStatus() != True
2181        if setvalue is not None:
2182            nextvalue = setvalue
2183        if nextvalue:
2184            log.LogOn()
2185            set2 = True
2186        else:
2187            log.LogOff()
2188            set2 = False
2189        for menuitem in self.MacroStatusList:
2190            menuitem.Check(set2)
2191
2192    def _init_Macro(self):
2193        '''Define the items in the macro menu.
2194        '''
2195        menu = self.MacroMenu
2196        item = menu.Append(
2197                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
2198                kind=wx.ITEM_CHECK,text='Record actions')
2199        self.MacroStatusList.append(item)
2200        item.Check(log.ShowLogStatus())
2201        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
2202
2203        # this may only be of value for development work
2204        item = menu.Append(
2205            help='Show logged commands', id=wx.ID_ANY,
2206            kind=wx.ITEM_NORMAL,text='Show log')
2207        def OnShowLog(event):
2208            print 70*'='
2209            print 'List of logged actions'
2210            for i,line in enumerate(log.G2logList):
2211                if line: print i,line
2212            print 70*'='
2213        self.Bind(wx.EVT_MENU, OnShowLog, item)
2214
2215        item = menu.Append(
2216            help='Clear logged commands', id=wx.ID_ANY,
2217            kind=wx.ITEM_NORMAL,text='Clear log')
2218        def OnClearLog(event): log.G2logList=[None]
2219        self.Bind(wx.EVT_MENU, OnClearLog, item)
2220       
2221        item = menu.Append(
2222            help='Save logged commands to file', id=wx.ID_ANY,
2223            kind=wx.ITEM_NORMAL,text='Save log')
2224        def OnSaveLog(event):
2225            import cPickle
2226            defnam = os.path.splitext(
2227                os.path.split(self.GSASprojectfile)[1]
2228                )[0]+'.gcmd'
2229            dlg = wx.FileDialog(self,
2230                'Choose an file to save past actions', '.', defnam, 
2231                'GSAS-II cmd output (*.gcmd)|*.gcmd',
2232                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2233            dlg.CenterOnParent()
2234            try:
2235                if dlg.ShowModal() == wx.ID_OK:
2236                    filename = dlg.GetPath()
2237                    # make sure extension is correct
2238                    filename = os.path.splitext(filename)[0]+'.gcmd'
2239                else:
2240                    filename = None
2241            finally:
2242                dlg.Destroy()
2243            if filename:
2244                fp = open(filename,'wb')
2245                fp.write(str(len(log.G2logList)-1)+'\n')
2246                for item in log.G2logList:
2247                    if item: cPickle.dump(item,fp)
2248                fp.close()
2249        self.Bind(wx.EVT_MENU, OnSaveLog, item)
2250
2251        item = menu.Append(
2252            help='Load logged commands from file', id=wx.ID_ANY,
2253            kind=wx.ITEM_NORMAL,text='Load log')
2254        def OnLoadLog(event):
2255            # this appends. Perhaps we should ask to clear?
2256            import cPickle
2257            defnam = os.path.splitext(
2258                os.path.split(self.GSASprojectfile)[1]
2259                )[0]+'.gcmd'
2260            dlg = wx.FileDialog(self,
2261                'Choose an file to read saved actions', '.', defnam, 
2262                'GSAS-II cmd output (*.gcmd)|*.gcmd',
2263                wx.OPEN)
2264            dlg.CenterOnParent()
2265            try:
2266                if dlg.ShowModal() == wx.ID_OK:
2267                    filename = dlg.GetPath()
2268                    # make sure extension is correct
2269                    filename = os.path.splitext(filename)[0]+'.gcmd'
2270                else:
2271                    filename = None
2272            finally:
2273                dlg.Destroy()
2274            if filename and os.path.exists(filename):
2275                fp = open(filename,'rb')
2276                lines = fp.readline()
2277                for i in range(int(lines)):
2278                    log.G2logList.append(cPickle.load(fp))
2279                fp.close()
2280        self.Bind(wx.EVT_MENU, OnLoadLog, item)
2281
2282        item = menu.Append(
2283            help='Replay saved commands', id=wx.ID_ANY,
2284            kind=wx.ITEM_NORMAL,text='Replay log')
2285        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
2286
2287    def _init_Exports(self,menu):
2288        '''Find exporter routines and add them into menus
2289        '''
2290        # set up the top-level menus
2291        projectmenu = wx.Menu()
2292        item = menu.AppendMenu(
2293            wx.ID_ANY, 'Entire project as',
2294            projectmenu, help='Export entire project')
2295
2296        phasemenu = wx.Menu()
2297        item = menu.AppendMenu(
2298            wx.ID_ANY, 'Phase as',
2299            phasemenu, help='Export phase or sometimes phases')
2300
2301        powdermenu = wx.Menu()
2302        item = menu.AppendMenu(
2303            wx.ID_ANY, 'Powder data as',
2304            powdermenu, help='Export powder diffraction histogram(s)')
2305
2306        singlemenu = wx.Menu()
2307        item = menu.AppendMenu(
2308            wx.ID_ANY, 'Single crystal data as',
2309            singlemenu, help='Export single crystal histogram(s)')
2310
2311        imagemenu = wx.Menu()
2312        item = menu.AppendMenu(
2313            wx.ID_ANY, 'Image data as',
2314            imagemenu, help='Export powder image(s) data')
2315
2316        mapmenu = wx.Menu()
2317        item = menu.AppendMenu(
2318            wx.ID_ANY, 'Maps as',
2319            mapmenu, help='Export density map(s)')
2320
2321        # pdfmenu = wx.Menu()
2322        # item = menu.AppendMenu(
2323        #     wx.ID_ANY, 'PDFs as',
2324        #     pdfmenu, help='Export pair distribution function(s)')
2325
2326        # find all the exporter files
2327        if not self.exporterlist: # this only needs to be done once
2328            pathlist = sys.path
2329            filelist = []
2330            for path in pathlist:
2331                for filename in glob.iglob(os.path.join(path,"G2export*.py")):
2332                    filelist.append(filename)   
2333            filelist = sorted(list(set(filelist))) # remove duplicates
2334            # go through the routines and import them, saving objects that
2335            # have export routines (method Exporter)
2336            for filename in filelist:
2337                path,rootname = os.path.split(filename)
2338                pkg = os.path.splitext(rootname)[0]
2339#                try:
2340                fp = None
2341                fp, fppath,desc = imp.find_module(pkg,[path,])
2342                pkg = imp.load_module(pkg,fp,fppath,desc)
2343                for clss in inspect.getmembers(pkg): # find classes defined in package
2344                    if clss[0].startswith('_'): continue
2345                    if inspect.isclass(clss[1]):
2346                        # check if we have the required methods
2347                        for m in 'Exporter','loadParmDict':
2348                            if not hasattr(clss[1],m): break
2349                            if not callable(getattr(clss[1],m)): break
2350                        else:
2351                            exporter = clss[1](self) # create an export instance
2352                            self.exporterlist.append(exporter)
2353#                except AttributeError:
2354#                    print 'Import_'+errprefix+': Attribute Error'+str(filename)
2355#                    pass
2356#                except ImportError:
2357#                    print 'Import_'+errprefix+': Error importing file'+str(filename)
2358#                    pass
2359                if fp: fp.close()
2360        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
2361        for obj in self.exporterlist:
2362            #print 'exporter',obj
2363            for typ in obj.exporttype:
2364                if typ == "project":
2365                    submenu = projectmenu
2366                elif typ == "phase":
2367                    submenu = phasemenu
2368                elif typ == "powder":
2369                    submenu = powdermenu
2370                elif typ == "single":
2371                    submenu = singlemenu
2372                elif typ == "image":
2373                    submenu = imagemenu
2374                elif typ == "map":
2375                    submenu = mapmenu
2376                # elif typ == "pdf":
2377                #     submenu = pdfmenu
2378                else:
2379                    print("Error, unknown type in "+str(obj))
2380                    break
2381                item = submenu.Append(
2382                    wx.ID_ANY,
2383                    help=obj.longFormatName,
2384                    kind=wx.ITEM_NORMAL,
2385                    text=obj.formatName)
2386                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
2387                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
2388        item = imagemenu.Append(wx.ID_ANY,
2389                        help='Export image controls and masks for multiple images',
2390                        kind=wx.ITEM_NORMAL,
2391                        text='Multiple image controls and masks')
2392        self.Bind(wx.EVT_MENU, self.OnSaveMultipleImg, id=item.GetId())
2393        #code to debug an Exporter. hard-code the routine below, to allow a reload before use
2394        # def DebugExport(event):
2395        #      print 'start reload'
2396        #      reload(G2IO)
2397        #      import G2export_pwdr as dev
2398        #      reload(dev)
2399        #      dev.ExportPowderFXYE(self).Exporter(event)
2400        # item = menu.Append(
2401        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2402        #     help="debug exporter",text="test Export FXYE")
2403        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
2404        # # #self.ExportLookup[item.GetId()] = 'image'
2405        # self.ExportLookup[item.GetId()] = 'powder'
2406
2407###############################################################################
2408# Exporters
2409###############################################################################
2410                           
2411    def _Add_ExportMenuItems(self,parent):
2412        # item = parent.Append(
2413        #     help='Select PWDR item to enable',id=wx.ID_ANY,
2414        #     kind=wx.ITEM_NORMAL,
2415        #     text='Export Powder Patterns...')
2416        # self.ExportPattern.append(item)
2417        # item.Enable(False)
2418        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
2419
2420        item = parent.Append(
2421            help='',id=wx.ID_ANY,
2422            kind=wx.ITEM_NORMAL,
2423            text='Export All Peak Lists...')
2424        self.ExportPeakList.append(item)
2425        item.Enable(True)
2426        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
2427
2428        item = parent.Append(
2429            help='',id=wx.ID_ANY,
2430            kind=wx.ITEM_NORMAL,
2431            text='Export HKLs...')
2432        self.ExportHKL.append(item)
2433        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
2434
2435        item = parent.Append(
2436            help='Select PDF item to enable',
2437            id=wx.ID_ANY,
2438            kind=wx.ITEM_NORMAL,
2439            text='Export PDF...')
2440        self.ExportPDF.append(item)
2441        item.Enable(False)
2442        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
2443
2444    def FillMainMenu(self,menubar,addhelp=True):
2445        '''Define contents of the main GSAS-II menu for the (main) data tree window.
2446        For the mac, this is also called for the data item windows as well so that
2447        the main menu items are data menu as well.
2448        '''
2449        File = wx.Menu(title='')
2450        menubar.Append(menu=File, title='&File')
2451        self._Add_FileMenuItems(File)
2452        Data = wx.Menu(title='')
2453        menubar.Append(menu=Data, title='Data')
2454        self._Add_DataMenuItems(Data)
2455        Calculate = wx.Menu(title='')       
2456        menubar.Append(menu=Calculate, title='&Calculate')
2457        self._Add_CalculateMenuItems(Calculate)
2458        Import = wx.Menu(title='')       
2459        menubar.Append(menu=Import, title='Import')
2460        self._Add_ImportMenu_Image(Import)
2461        self._Add_ImportMenu_Phase(Import)
2462        self._Add_ImportMenu_powder(Import)
2463        self._Add_ImportMenu_Sfact(Import)
2464        self._Add_ImportMenu_smallangle(Import)
2465        self._Add_ImportMenu_reflectometry(Import)
2466        self._Add_ImportMenu_PDF(Import)
2467
2468        #======================================================================
2469        # Code to help develop/debug an importer, much is hard-coded below
2470        # but module is reloaded before each use, allowing faster testing
2471        # def DebugImport(event):
2472        #     print 'start reload'
2473        #     import G2phase_ISO as dev
2474        #     reload(dev)
2475        #     rd = dev.ISODISTORTPhaseReader()
2476        #     self.ImportMenuId[event.GetId()] = rd
2477        #     self.OnImportPhase(event)
2478            # or ----------------------------------------------------------------------
2479            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
2480            # special debug code
2481            # or ----------------------------------------------------------------------
2482            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
2483            # fp = open(filename,'Ur')
2484            # if not rd.ContentsValidator(fp):
2485            #     print 'not validated'
2486            #     # make a list of used phase ranId's
2487            # phaseRIdList = []
2488            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2489            # if sub:
2490            #     item, cookie = self.PatternTree.GetFirstChild(sub)
2491            #     while item:
2492            #         phaseName = self.PatternTree.GetItemText(item)
2493            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2494            #         if ranId: phaseRIdList.append(ranId)
2495            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2496            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2497            #     print 'read OK'
2498        # item = Import.Append(
2499        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2500        #     help="debug importer",text="test importer")
2501        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2502        #======================================================================
2503        self.ExportMenu = wx.Menu(title='')
2504        menubar.Append(menu=self.ExportMenu, title='Export')
2505        self._init_Exports(self.ExportMenu)
2506        self._Add_ExportMenuItems(self.ExportMenu)
2507        if GSASIIpath.GetConfigValue('Enable_logging'):
2508            self.MacroMenu = wx.Menu(title='')
2509            menubar.Append(menu=self.MacroMenu, title='Macro')
2510            self._init_Macro()
2511        if addhelp:
2512            HelpMenu=G2G.MyHelp(self,includeTree=True,
2513                morehelpitems=[('&Tutorials','Tutorials'),])
2514            menubar.Append(menu=HelpMenu,title='&Help')
2515           
2516    def _init_ctrls(self, parent):
2517        wx.Frame.__init__(self, name='GSASII', parent=parent,
2518            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2519        clientSize = wx.ClientDisplayRect()
2520        Size = self.GetSize()
2521        xPos = clientSize[2]-Size[0]
2522        self.SetPosition(wx.Point(xPos,clientSize[1]))
2523        self._init_Imports()
2524        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2525        self.MakePDF = []
2526        self.Refine = []
2527        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2528        #self.ExportPattern = []
2529        self.ExportPeakList = []
2530        self.ExportHKL = []
2531        self.ExportPDF = []
2532        self.ExportPhase = []
2533        self.ExportCIF = []
2534        #
2535        self.GSASIIMenu = wx.MenuBar()
2536        # create a list of all dataframe menus (appended in PrefillDataMenu)
2537        self.dataMenuBars = [self.GSASIIMenu]
2538        self.MacroStatusList = []
2539        self.FillMainMenu(self.GSASIIMenu)
2540        self.SetMenuBar(self.GSASIIMenu)
2541        self.Bind(wx.EVT_SIZE, self.OnSize)
2542        self.Status = self.CreateStatusBar()
2543        self.mainPanel = wx.Panel(self,-1)
2544       
2545        wxID_PATTERNTREE = wx.NewId()
2546        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2547        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2548            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2549        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED,self.OnDataTreeSelChanged)
2550        self.PatternTree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK,self.OnDataTreeSelChanged)
2551        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2552            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2553        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2554            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2555        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2556            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2557        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2558            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2559        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2560            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2561        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2562            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2563        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2564        self.root = self.PatternTree.root
2565        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2566            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2567        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame,G2frame=self)
2568        plotFrame.Show()
2569       
2570        self.dataDisplay = None
2571       
2572    def __init__(self, parent):
2573        self.ExportLookup = {}
2574        self.exporterlist = []
2575        self._init_ctrls(parent)
2576        self.Image = wx.Image(
2577            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2578            wx.BITMAP_TYPE_ICO)
2579        if "wxMSW" in wx.PlatformInfo:
2580            img = self.Image.Scale(16, 16).ConvertToBitmap()
2581        elif "wxGTK" in wx.PlatformInfo:
2582            img = self.Image.Scale(22, 22).ConvertToBitmap()
2583        else:
2584            img = self.Image.ConvertToBitmap()
2585        self.SetIcon(wx.IconFromBitmap(img))
2586        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2587        # various defaults
2588        self.oldFocus = None
2589        self.GSASprojectfile = ''
2590        self.undofile = ''
2591        self.TreeItemDelete = False
2592        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False,'sqPlot':False}
2593        self.Weight = False
2594        self.IfPlot = False
2595        self.DDShowAll = False
2596        self.atmSel = ''
2597        self.PatternId = 0
2598        self.PickId = 0
2599        self.PickIdText = None
2600        self.PeakTable = []
2601        self.LimitsTable = []
2602        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2603        self.HKL = []
2604        self.Lines = []
2605        self.itemPicked = None
2606        self.dataFrame = None
2607        self.Interpolate = 'nearest'
2608        self.ContourColor = 'Paired'
2609        self.VcovColor = 'RdYlGn'
2610        self.RamaColor = 'Blues'
2611        self.Projection = 'equal area'
2612        self.logPlot = False
2613        self.plusPlot = True
2614        self.ErrorBars = False
2615        self.Contour = False
2616        self.Legend = False
2617        self.SinglePlot = True
2618        self.Waterfall = False
2619        self.selections= None
2620        self.PDFselections = None
2621        self.SubBack = False
2622        self.seqReverse = False
2623        self.seqLines = True #draw lines between points
2624        self.plotView = 0
2625        self.Image = 0
2626        self.oldImagefile = '' # the name of the last image file read
2627        self.oldImageTag = None # the name of the tag for multi-image files
2628        self.PauseIntegration = False
2629        self.ImageZ = []
2630        self.Integrate = 0
2631        self.imageDefault = {}
2632        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2633        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2634        self.autoIntFrame = None
2635        self.IntegratedList = [] # list of already integrated IMG tree items
2636        self.Sngl = False
2637        self.ifGetRing = False
2638        self.MaskKey = ''           #trigger for making image masks
2639        self.MskDelete = False      #trigger for mask delete
2640        self.StrainKey = ''         #ditto for new strain d-zeros
2641        self.EnablePlot = True
2642        self.hist = ''              # selected histogram in Phase/Data tab
2643        self.dirname = os.path.abspath(os.path.expanduser('~'))       #start in the users home directory by default; may be meaningless
2644        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2645        self.LastImportDir = None # last-used directory where an import was done
2646        self.LastGPXdir = None    # directory where a GPX file was last read
2647        self.LastExportDir = None  # the last directory used for exports, if any.
2648        self.dataDisplayPhaseText = ''
2649        self.lastTreeSetting = []
2650        self.ExpandingAll = False
2651               
2652        arg = sys.argv
2653        if len(arg) > 1 and arg[1]:
2654            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2655            self.dirname = os.path.abspath(os.path.dirname(arg[1]))
2656            if self.dirname: os.chdir(self.dirname)
2657            try:
2658                self.StartProject()         #open the file if possible
2659                return
2660            except Exception:
2661                print 'Error opening or reading file',arg[1]
2662                import traceback
2663                print traceback.format_exc()
2664               
2665        if GSASIIpath.GetConfigValue('Starting_directory'):
2666            try:
2667                pth = GSASIIpath.GetConfigValue('Starting_directory')
2668                pth = os.path.expanduser(pth) 
2669                os.chdir(pth)
2670                self.LastGPXdir = pth
2671            except:
2672                print('Ignoring Config Starting_directory value: '+
2673                      GSASIIpath.GetConfigValue('Starting_directory'))
2674
2675    def GetTreeItemsList(self,item):
2676        return self.PatternTree._getTreeItemsList(item)
2677
2678    def OnSize(self,event):
2679        'Called to make PatternTree fill mainPanel'
2680        w,h = self.GetClientSizeTuple()
2681        self.mainPanel.SetSize(wx.Size(w,h))
2682        self.PatternTree.SetSize(wx.Size(w,h))
2683                       
2684    def OnDataTreeSelChanged(self, event):
2685        '''Called when a data tree item is selected'''
2686        if self.TreeItemDelete:
2687            self.TreeItemDelete = False
2688        else:
2689            if self.ExpandingAll:
2690                if GSASIIpath.GetConfigValue('debug'): print('Skipping Tree selection due to ExpandAll')
2691                return
2692            pltNum = self.G2plotNB.nb.GetSelection()
2693            if pltNum >= 0:                         #to avoid the startup with no plot!
2694                self.G2plotNB.nb.GetPage(pltNum)
2695            item = event.GetItem()
2696            wx.CallAfter(G2gd.SelectDataTreeItem,self,item)
2697            if self.oldFocus: # Why do this?
2698                self.oldFocus.SetFocus()
2699       
2700    def OnPatternTreeItemCollapsed(self, event):
2701        'Called when a tree item is collapsed - all children will be collapsed'
2702        self.PatternTree.CollapseAllChildren(event.GetItem())
2703
2704    def OnPatternTreeItemExpanded(self, event):
2705        'Called when a tree item is expanded'
2706        self.OnDataTreeSelChanged(event)
2707        event.Skip()
2708       
2709    def OnPatternTreeItemDelete(self, event):
2710        'Called when a tree item is deleted -- not sure what this does'
2711        self.TreeItemDelete = True
2712
2713    def OnPatternTreeItemActivated(self, event):
2714        'Called when a tree item is activated'
2715        event.Skip()
2716       
2717    def OnPatternTreeBeginRDrag(self,event):
2718        event.Allow()
2719        self.BeginDragId = event.GetItem()
2720        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2721        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2722        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2723        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2724        while item:     #G2 data tree has no sub children under a child of a tree item
2725            name = self.PatternTree.GetItemText(item)
2726            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2727            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2728       
2729    def OnPatternTreeEndDrag(self,event):
2730        event.Allow()
2731        self.EndDragId = event.GetItem()
2732        try:
2733            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2734        except:
2735            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2736            NewParent = self.root
2737        if self.ParentId != NewParent:
2738            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2739        else:
2740            Name,Item = self.DragData[0]
2741            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2742            self.PatternTree.SetItemPyData(NewId,Item)
2743            for name,item in self.DragData[1:]:     #loop over children
2744                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2745                self.PatternTree.SetItemPyData(Id,item)
2746            self.PatternTree.Delete(self.BeginDragId)
2747            G2gd.SelectDataTreeItem(self,NewId)
2748       
2749    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2750        'Allows stepping through the tree with the up/down arrow keys'
2751        self.oldFocus = wx.Window.FindFocus()
2752        keyevt = event.GetKeyEvent()
2753        key = event.GetKeyCode()
2754        item = self.PatternTree.GetSelection()
2755        if type(item) is int: return # is this the toplevel in tree?
2756        name = self.PatternTree.GetItemText(item)
2757        parent = self.PatternTree.GetItemParent(item)
2758        if key == wx.WXK_UP:
2759            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2760                if type(parent) is int: return # is this the toplevel in tree?
2761                prev = self.PatternTree.GetPrevSibling(parent)
2762                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2763                if NewId:
2764                    self.PatternTree.Collapse(parent)
2765                    self.PatternTree.Expand(prev)
2766                    self.oldFocus = wx.Window.FindFocus()
2767                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2768                else:
2769                    wx.CallAfter(self.PatternTree.SelectItem,item)
2770            else:   
2771                self.PatternTree.GetPrevSibling(item)
2772                self.PatternTree.SelectItem(item)
2773        elif key == wx.WXK_DOWN:
2774            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2775                next = self.PatternTree.GetNextSibling(parent)
2776                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2777                if NewId:
2778                    self.PatternTree.Collapse(parent)
2779                    self.PatternTree.Expand(next)
2780                    self.oldFocus = wx.Window.FindFocus()
2781                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2782                else:
2783                    wx.CallAfter(self.PatternTree.SelectItem,item)
2784            else:   
2785                self.PatternTree.GetNextSibling(item)
2786                self.PatternTree.SelectItem(item)
2787               
2788    def OnReadPowderPeaks(self,event):
2789        'Bound to menu Data/Read Powder Peaks'
2790        self.CheckNotebook()
2791        pth = G2G.GetImportPath(self)
2792        if not pth: pth = '.'
2793        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2794            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2795        try:
2796            if dlg.ShowModal() == wx.ID_OK:
2797                self.HKL = []
2798                self.powderfile = dlg.GetPath()
2799                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2800                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2801                data = ['PKS',wave,0.0]
2802                names = ['Type','Lam','Zero'] 
2803                codes = [0,0,0]
2804                inst = [G2IO.makeInstDict(names,data,codes),{}]
2805                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2806                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2807                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2808                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2809                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2810                self.PatternTree.Expand(Id)
2811                self.PatternTree.SelectItem(Id)
2812                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2813        finally:
2814            dlg.Destroy()
2815                       
2816    def OnImageRead(self,event):
2817        '''Called to read in an image in any known format. *** Depreciated. ***
2818        '''
2819        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2820
2821    def CheckNotebook(self):
2822        '''Make sure the data tree has the minimally expected controls.
2823        '''
2824        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2825            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2826            self.PatternTree.SetItemPyData(sub,[''])
2827        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2828            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2829            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2830        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2831            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2832            self.PatternTree.SetItemPyData(sub,{})
2833        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2834            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2835            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2836        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2837            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2838            self.PatternTree.SetItemPyData(sub,{})
2839        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2840            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2841            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2842                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2843               
2844    class CopyDialog(wx.Dialog):
2845        '''Creates a dialog for copying control settings between
2846        data tree items'''
2847        def __init__(self,parent,title,text,data):
2848            wx.Dialog.__init__(self,parent,-1,title, 
2849                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2850            self.data = data
2851            panel = wx.Panel(self)
2852            mainSizer = wx.BoxSizer(wx.VERTICAL)
2853            topLabl = wx.StaticText(panel,-1,text)
2854            mainSizer.Add((10,10),1)
2855            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2856            mainSizer.Add((10,10),1)
2857            ncols = len(data)/40+1
2858            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2859            for id,item in enumerate(self.data):
2860                ckbox = wx.CheckBox(panel,id,item[1])
2861                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2862                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2863            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2864            OkBtn = wx.Button(panel,-1,"Ok")
2865            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2866            cancelBtn = wx.Button(panel,-1,"Cancel")
2867            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2868            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2869            btnSizer.Add((20,20),1)
2870            btnSizer.Add(OkBtn)
2871            btnSizer.Add((20,20),1)
2872            btnSizer.Add(cancelBtn)
2873            btnSizer.Add((20,20),1)
2874           
2875            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2876            panel.SetSizer(mainSizer)
2877            panel.Fit()
2878            self.Fit()
2879       
2880        def OnCopyChange(self,event):
2881            id = event.GetId()
2882            self.data[id][0] = self.FindWindowById(id).GetValue()       
2883           
2884        def OnOk(self,event):
2885            parent = self.GetParent()
2886            parent.Raise()
2887            self.EndModal(wx.ID_OK)             
2888           
2889        def OnCancel(self,event):
2890            parent = self.GetParent()
2891            parent.Raise()
2892            self.EndModal(wx.ID_CANCEL)             
2893           
2894        def GetData(self):
2895            return self.data
2896       
2897    class SumDialog(wx.Dialog):
2898        '''Allows user to supply scale factor(s) when summing data -
2899        TODO: CAN WE PREVIEW RESULT HERE?'''
2900        def __init__(self,parent,title,text,dataType,data,dataList):
2901            wx.Dialog.__init__(self,parent,-1,title,size=(400,250),
2902                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
2903            self.plotFrame = wx.Frame(self,-1,'Sum Plots',size=wx.Size(700,600), \
2904                style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2905            self.G2plotNB = G2plt.G2PlotNoteBook(self.plotFrame,G2frame=self)
2906            self.data = data
2907            self.dataList = dataList
2908            self.dataType = dataType
2909            size = (450,350)
2910            panel = wxscroll.ScrolledPanel(self, wx.ID_ANY,size=size,
2911                style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
2912            mainSizer = wx.BoxSizer(wx.VERTICAL)
2913            topLabl = wx.StaticText(panel,-1,text)
2914            mainSizer.Add((10,10),1)
2915            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2916            mainSizer.Add((10,10),1)
2917            self.dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2918            for id,item in enumerate(self.data[:-1]):
2919                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(300,20))
2920                name.SetEditable(False)
2921#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2922                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2923                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2924                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2925                self.dataGridSizer.Add(scale,0,wx.LEFT,10)
2926                self.dataGridSizer.Add(name,0,wx.RIGHT,10)
2927            if self.dataType:
2928                self.dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+self.dataType),0, \
2929                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2930                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(300,20),style=wx.TE_PROCESS_ENTER)
2931                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2932                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2933                self.dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2934                self.dataGridSizer.Add(wx.StaticText(panel,label='All scales value: '),0,  \
2935                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2936#        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),typeHint=float,OnLeave=OnAzmthOff)
2937                allScale = wx.TextCtrl(panel,value='',style=wx.TE_PROCESS_ENTER)
2938                allScale.Bind(wx.EVT_TEXT_ENTER,self.OnAllScale)
2939                allScale.Bind(wx.EVT_KILL_FOCUS,self.OnAllScale)
2940                self.dataGridSizer.Add(allScale,0,WACV)
2941            mainSizer.Add(self.dataGridSizer,0,wx.EXPAND)
2942            OkBtn = wx.Button(panel,-1,"Ok")
2943            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2944            cancelBtn = wx.Button(panel,-1,"Cancel")
2945            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2946            btnSizer = wx.FlexGridSizer(0,3,10,20)
2947            if self.dataType =='PWDR':
2948                TestBtn = wx.Button(panel,-1,"Test")
2949                TestBtn.Bind(wx.EVT_BUTTON, self.OnTest)
2950                btnSizer.Add(TestBtn)
2951            btnSizer.Add(OkBtn)
2952            btnSizer.Add(cancelBtn)
2953           
2954            panel.SetSizer(mainSizer)
2955            panel.SetAutoLayout(1)
2956            panel.SetupScrolling()
2957            mainSizer.Add((10,10),1)
2958            mainSizer.Add(btnSizer,0,wx.CENTER)
2959            panel.SetSizer(mainSizer)
2960            panel.Fit()
2961            self.Fit()
2962
2963        def OnScaleChange(self,event):
2964            event.Skip()
2965            id = event.GetId()
2966            value = self.FindWindowById(id).GetValue()
2967            try:
2968                self.data[id][0] = float(value)
2969                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2970            except ValueError:
2971                if value and '-' not in value[0]:
2972                    print 'bad input - numbers only'
2973                    self.FindWindowById(id).SetValue('0.000')
2974                   
2975        def OnAllScale(self,event):
2976            event.Skip()
2977            id = event.GetId()
2978            try:
2979                scale = float(self.FindWindowById(id).GetValue())
2980                self.FindWindowById(id).SetValue('%.3f'%(scale))
2981                entries = self.dataGridSizer.GetChildren()
2982                for i,item in enumerate(self.data[:-1]):
2983                    item[0] = scale
2984                    entries[2*i].GetWindow().SetValue('%.3f'%(scale))
2985                 
2986            except ValueError:
2987                print 'bad input - numbers only'
2988                self.FindWindowById(id).SetValue('')
2989                   
2990           
2991        def OnNameChange(self,event):
2992            event.Skip()
2993            self.data[-1] = self.name.GetValue()
2994           
2995        def OnTest(self,event):
2996            lenX = 0
2997            Xminmax = [0,0]
2998            XY = []
2999            Xsum = []
3000            Ysum = []
3001            Vsum = []
3002            result = self.data
3003            for i,item in enumerate(result[:-1]):
3004                scale,name = item
3005                data = self.dataList[i]
3006                if scale:
3007                    x,y,w,yc,yb,yd = data   #numpy arrays!
3008                    XY.append([x,scale*y])
3009                    v = 1./w
3010                    if lenX:
3011                        if lenX != len(x):
3012                            self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
3013                                '\nExpected:'+str(lenX)+ \
3014                                '\nFound:   '+str(len(x))+'\nfor '+name)
3015                            self.OnCancel(event)
3016                    else:
3017                        lenX = len(x)
3018                    if Xminmax[1]:
3019                        if Xminmax != [x[0],x[-1]]:
3020                            self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
3021                                '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
3022                                '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
3023                            self.OnCancel(event)
3024                        else:
3025                            for j,yi in enumerate(y):
3026                                 Ysum[j] += scale*yi
3027                                 Vsum[j] += abs(scale)*v[j]
3028                    else:
3029                        Xminmax = [x[0],x[-1]]
3030                        Xsum = x
3031                        Ysum = scale*y
3032                        Vsum = abs(scale*v)
3033            Wsum = 1./np.array(Vsum)
3034            YCsum = np.zeros(lenX)
3035            YBsum = np.zeros(lenX)
3036            YDsum = np.zeros(lenX)
3037            XY.append([Xsum,Ysum])
3038            self.result = [Xsum,Ysum,Wsum,YCsum,YBsum,YDsum]
3039            # N.B. PlotXY expects the first arg to point to G2frame. In this case, we
3040            # create a duplicate (temporary) Plot notebook window that is a child of the
3041            # modal SumDialog dialog (self). This nicely gets deleted when the dialog is destroyed,
3042            # but the plot window is not fully functional, at least on the Mac.
3043            G2plt.PlotXY(self,XY,lines=True,Title='Sum:'+self.data[-1],labelY='Intensity',)
3044            self.plotFrame.Show()
3045                       
3046        def OnOk(self,event):
3047            if self.dataType == 'PWDR': self.OnTest(event)
3048            parent = self.GetParent()
3049            parent.Raise()
3050            self.EndModal(wx.ID_OK)             
3051           
3052        def OnCancel(self,event):
3053            parent = self.GetParent()
3054            parent.Raise()
3055            self.EndModal(wx.ID_CANCEL)             
3056           
3057        def GetData(self):
3058            if self.dataType == 'PWDR':
3059                return self.data,self.result
3060            else:
3061                return self.data
3062                       
3063    def OnPwdrSum(self,event):
3064        'Sum together powder data(?)'
3065        TextList = []
3066        DataList = []
3067        Names = []
3068        Inst = None
3069        Comments = ['Sum equals: \n']
3070        if self.PatternTree.GetCount():
3071            item, cookie = self.PatternTree.GetFirstChild(self.root)
3072            while item:
3073                name = self.PatternTree.GetItemText(item)
3074                Names.append(name)
3075                if 'PWDR' in name:
3076                    TextList.append([0.0,name])
3077                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
3078                    if not Inst:
3079                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
3080                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3081            if len(TextList) < 2:
3082                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
3083                return
3084            TextList.append('default_sum_name')               
3085            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList,DataList)
3086            try:
3087                if dlg.ShowModal() == wx.ID_OK:
3088                    result,sumData = dlg.GetData()
3089                    Xsum,Ysum,Wsum,YCsum,YBsum,YDsum = sumData
3090                    Xminmax = [Xsum[0],Xsum[-1]]
3091                    outname = 'PWDR '+result[-1]
3092                    Id = 0
3093                    if outname in Names:
3094                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
3095                        try:
3096                            if dlg2.ShowModal() == wx.ID_OK:
3097                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
3098                                self.PatternTree.Delete(Id)
3099                        finally:
3100                            dlg2.Destroy()
3101                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
3102                    if Id:
3103                        Sample = G2pdG.SetDefaultSample()
3104                        Ymin = np.min(Ysum)
3105                        Ymax = np.max(Ysum)
3106                        valuesdict = {
3107                            'wtFactor':1.0,
3108                            'Dummy':False,
3109                            'ranId':ran.randint(0,sys.maxint),
3110                            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-.1*Ymax,'refDelt':0.1*Ymax,
3111                            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
3112                            }
3113                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
3114                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
3115                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
3116                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
3117                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
3118                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
3119                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
3120                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
3121                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
3122                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
3123                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
3124                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
3125                        self.PatternTree.SelectItem(Id)
3126                        self.PatternTree.Expand(Id)
3127            finally:
3128                dlg.Destroy()
3129
3130    def OnImageSum(self,event):
3131        'Sum together image data'
3132        TextList = []
3133        DataList = []
3134        IdList = []
3135        Names = []
3136        Comments = ['Sum equals: \n']
3137        if self.PatternTree.GetCount():
3138            item, cookie = self.PatternTree.GetFirstChild(self.root)
3139            while item:
3140                name = self.PatternTree.GetItemText(item)
3141                Names.append(name)
3142                if 'IMG' in name:
3143                    TextList.append([0.0,name])
3144                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
3145                    IdList.append(item)
3146                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
3147                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3148            if len(TextList) < 2:
3149                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
3150                return
3151            TextList.append('default_sum_name')               
3152            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList,DataList)
3153            try:
3154                if dlg.ShowModal() == wx.ID_OK:
3155                    imSize = 0
3156                    result = dlg.GetData()
3157                    First = True
3158                    Found = False
3159                    for i,item in enumerate(result[:-1]):
3160                        scale,name = item
3161                        if scale:
3162                            Found = True                               
3163                            Comments.append("%10.3f %s" % (scale,' * '+name))
3164                            Npix,imagefile,imagetag = DataList[i]
3165                            imagefile = G2IO.GetCheckImageFile(self,IdList[i])[1]
3166                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
3167                            if First:
3168                                newImage = np.zeros_like(image)
3169                                First = False
3170                            if imSize:
3171                                if imSize != Npix:
3172                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
3173                                        '\nExpected:'+str(imSize)+ \
3174                                        '\nFound:   '+str(Npix)+'\nfor '+name)
3175                                    return
3176                                newImage = newImage+scale*image
3177                            else:
3178                                imSize = Npix
3179                                newImage = newImage+scale*image
3180                            del(image)
3181                    if not Found:
3182                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
3183                        return
3184                       
3185                       
3186                    newImage = np.array(newImage,dtype=np.int32)                       
3187                    outname = 'IMG '+result[-1]
3188                    Id = 0
3189                    if outname in Names:
3190                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
3191                        try:
3192                            if dlg2.ShowModal() == wx.ID_OK:
3193                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
3194                        finally:
3195                            dlg2.Destroy()
3196                    else:
3197                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
3198                    if Id:
3199                        pth = G2G.GetExportPath(self)
3200                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth,outname.split('IMG ')[1], 
3201                            'G2img files (*.G2img)|*.G2img', 
3202                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
3203                        if dlg.ShowModal() == wx.ID_OK:
3204                            newimagefile = dlg.GetPath()
3205                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
3206                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
3207                            Imax = np.amax(newImage)
3208                            Imin = np.amin(newImage)
3209                            newImage = []
3210                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
3211                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
3212                        del(newImage)
3213                        if self.imageDefault:
3214                            Data = copy.copy(self.imageDefault)
3215                        Data['formatName'] = 'GSAS-II image'
3216                        Data['showLines'] = True
3217                        Data['ring'] = []
3218                        Data['rings'] = []
3219                        Data['cutoff'] = 10
3220                        Data['pixLimit'] = 20
3221                        Data['ellipses'] = []
3222                        Data['calibrant'] = ''
3223                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
3224                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
3225                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
3226                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
3227                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
3228                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
3229                        self.PatternTree.SelectItem(Id)
3230                        self.PatternTree.Expand(Id)
3231                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
3232                        self.Image = self.PickId
3233            finally:
3234                dlg.Destroy()
3235                     
3236    def OnAddPhase(self,event):
3237        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
3238        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3239            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
3240        else:
3241            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3242        PhaseName = ''
3243        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
3244            style=wx.OK)
3245        if dlg.ShowModal() == wx.ID_OK:
3246            PhaseName = dlg.GetValue()
3247        dlg.Destroy()
3248        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
3249        E,SGData = G2spc.SpcGroup('P 1')
3250        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
3251        G2gd.SelectDataTreeItem(self,sub) #bring up new phase General tab
3252       
3253    def OnDeletePhase(self,event):
3254        'Delete a phase from the tree. Called by Data/Delete Phase menu'
3255        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
3256        if self.dataFrame:
3257            self.dataFrame.Clear() 
3258        TextList = []
3259        DelList = []
3260        DelItemList = []
3261        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3262            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3263        else:
3264            return
3265        if sub:
3266            item, cookie = self.PatternTree.GetFirstChild(sub)
3267            while item:
3268                TextList.append(self.PatternTree.GetItemText(item))
3269                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
3270            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
3271            try:
3272                if dlg.ShowModal() == wx.ID_OK:
3273                    result = dlg.GetSelections()
3274                    for i in result: DelList.append([i,TextList[i]])
3275                    item, cookie = self.PatternTree.GetFirstChild(sub)
3276                    i = 0
3277                    while item:
3278                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
3279                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3280                        i += 1
3281                    for item in DelItemList:
3282                        name = self.PatternTree.GetItemText(item)
3283                        self.PatternTree.Delete(item)
3284                        self.G2plotNB.Delete(name)
3285                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3286                    while item:
3287                        name = self.PatternTree.GetItemText(item)
3288                        if 'PWDR' in name:
3289                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
3290                            refList = self.PatternTree.GetItemPyData(Id)
3291                            if len(refList):
3292                                for i,item in DelList:
3293                                    if item in refList:
3294                                        del(refList[item])
3295#                            self.PatternTree.SetItemPyData(Id,refList)
3296                        elif 'HKLF' in name:
3297                            data = self.PatternTree.GetItemPyData(item)
3298                            data[0] = {}
3299#                            self.PatternTree.SetItemPyData(item,data)
3300                           
3301                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3302            finally:
3303                dlg.Destroy()
3304               
3305    def OnRenameData(self,event):
3306        'Renames an existing phase. Called by Data/Rename Phase menu'
3307        name = self.PatternTree.GetItemText(self.PickId)     
3308        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
3309            if 'Bank' in name:
3310                names = name.split('Bank')
3311                names[1] = ' Bank'+names[1]
3312            elif 'Azm' in name:
3313                names = name.split('Azm')
3314                names[1] = ' Azm'+names[1]
3315            else:
3316                names = [name,'']
3317            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
3318            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
3319                defaultValue=names[0][names[0].index(' ')+1:])
3320            try:
3321                if dlg.ShowModal() == wx.ID_OK:
3322                    name = dataType+dlg.GetValue()+names[1]
3323                    self.PatternTree.SetItemText(self.PickId,name)
3324            finally:
3325                dlg.Destroy()
3326       
3327    def GetFileList(self,fileType,skip=None):        #potentially useful?
3328        'Appears unused. Note routine of same name in GSASIIpwdGUI'
3329        fileList = []
3330        Source = ''
3331        id, cookie = self.PatternTree.GetFirstChild(self.root)
3332        while id:
3333            name = self.PatternTree.GetItemText(id)
3334            if fileType in name:
3335                if id == skip:
3336                    Source = name
3337                else:
3338                    fileList.append([False,name,id])
3339            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3340        if skip:
3341            return fileList,Source
3342        else:
3343            return fileList
3344           
3345    def OnDataDelete(self, event):
3346        '''Delete one or more histograms from data tree. Called by the
3347        Data/DeleteData menu
3348        '''
3349        TextList = []
3350        DelList = []
3351        DelItemList = []
3352        nItems = {'PWDR':0,'SASD':0,'REFD':0,'IMG':0,'HKLF':0,'PDF':0}
3353        PDFnames = []
3354        if self.PatternTree.GetCount():
3355            item, cookie = self.PatternTree.GetFirstChild(self.root)
3356            while item:
3357                name = self.PatternTree.GetItemText(item)
3358                if name not in ['Notebook','Controls','Covariance','Constraints',
3359                    'Restraints','Phases','Rigid bodies'] and 'Sequential' not in name:
3360                    if 'PWDR' in name[:4]: nItems['PWDR'] += 1
3361                    if 'SASD' in name[:4]: nItems['SASD'] += 1
3362                    if 'REFD' in name[:4]: nItems['REFD'] += 1
3363                    if 'IMG' in name[:3]:  nItems['IMG'] += 1
3364                    if 'HKLF' in name[:4]: nItems['HKLF'] += 1
3365                    if 'PDF' in name[:3]:
3366                        PDFnames.append(name)
3367                        nItems['PDF'] += 1
3368                    TextList.append(name)
3369                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3370            for pdfName in PDFnames:
3371                try:
3372                    TextList.remove('PWDR'+pdfName[4:])
3373                except ValueError:
3374                    print 'PWDR'+pdfName[4:]+' for '+pdfName+' not found'
3375            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
3376            try:
3377                if dlg.ShowModal() == wx.ID_OK:
3378                    result = dlg.GetSelections()
3379                    for i in result: DelList.append(TextList[i])
3380                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3381                    while item:
3382                        itemName = self.PatternTree.GetItemText(item)
3383                        if itemName in DelList:
3384                            if 'PWDR' in itemName[:4]: nItems['PWDR'] -= 1
3385                            elif 'SASD' in itemName[:4]: nItems['SASD'] -= 1
3386                            elif 'REFD' in itemName[:4]: nItems['REFD'] -= 1
3387                            elif 'IMG' in itemName[:3]: nItems['IMG'] -= 1
3388                            elif 'HKLF' in itemName[:4]: nItems['HKLF'] -= 1
3389                            elif 'PDF' in itemName[:3]: nItems['PDF'] -= 1
3390                            DelItemList.append(item)
3391                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3392                    for item in DelItemList:
3393                        self.PatternTree.Delete(item)
3394                    self.PickId = 0
3395                    self.PickIdText = None
3396                    self.PatternId = 0
3397                    if nItems['PWDR']:
3398                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3399                    else:
3400                        self.G2plotNB.Delete('Powder Patterns')
3401                    if not nItems['IMG']:
3402                        self.G2plotNB.Delete('2D Powder Image')
3403                    if not nItems['HKLF']:
3404                        self.G2plotNB.Delete('Structure Factors')
3405                        if '3D Structure Factors' in self.G2plotNB.plotList:
3406                            self.G2plotNB.Delete('3D Structure Factors')
3407            finally:
3408                dlg.Destroy()
3409
3410    def OnFileOpen(self, event, filename=None):
3411        '''Gets a GSAS-II .gpx project file in response to the
3412        File/Open Project menu button
3413        '''
3414        result = wx.ID_OK
3415        self.EnablePlot = False
3416        if self.PatternTree.GetChildrenCount(self.root,False):
3417            if self.dataFrame:
3418                self.dataFrame.Clear() 
3419            dlg = wx.MessageDialog(
3420                self,
3421                'Do you want to overwrite the current project? '+
3422                'Any unsaved changes in current project will be lost. Press OK to continue.',
3423                'Overwrite?',  wx.OK | wx.CANCEL)
3424            try:
3425                result = dlg.ShowModal()
3426                if result == wx.ID_OK:
3427                    self.PatternTree.DeleteChildren(self.root)
3428                    self.GSASprojectfile = ''
3429                    self.HKL = []
3430                    if self.G2plotNB.plotList:
3431                        self.G2plotNB.clear()
3432            finally:
3433                dlg.Destroy()
3434        if result != wx.ID_OK: return
3435
3436        if not filename:
3437            if self.dataDisplay: self.dataDisplay.Destroy()
3438            if self.LastGPXdir:
3439                pth = self.LastGPXdir
3440            else:
3441                pth = '.'
3442            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3443                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3444            try:
3445                if dlg.ShowModal() != wx.ID_OK: return
3446                self.GSASprojectfile = dlg.GetPath()
3447                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3448                self.dirname = dlg.GetDirectory()
3449            finally:
3450                dlg.Destroy()
3451        else:
3452            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3453            self.dirname = os.path.split(filename)[0]
3454
3455        try:
3456            self.StartProject()         #open the file if possible
3457        except:
3458            print '\nError opening file ',filename
3459            import traceback
3460            print traceback.format_exc()
3461       
3462    def StartProject(self):
3463        '''Opens a GSAS-II project file & selects the 1st available data set to
3464        display (PWDR, HKLF, REFD or SASD)
3465        '''
3466       
3467        Id = 0
3468        phaseId = None
3469        G2IO.ProjFileOpen(self)
3470        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3471        self.PatternTree.Expand(self.root)
3472        self.HKL = []
3473        item, cookie = self.PatternTree.GetFirstChild(self.root)
3474        while item and not Id:
3475            name = self.PatternTree.GetItemText(item)
3476            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD','REFD']:
3477                Id = item
3478            elif name == "Phases":
3479                phaseId = item
3480            elif name == 'Controls':
3481                data = self.PatternTree.GetItemPyData(item)
3482                if data:
3483                    for item in self.Refine: item.Enable(True)
3484                    self.EnableSeqRefineMenu()
3485            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3486        if phaseId: # show all phases
3487            self.PatternTree.Expand(phaseId)
3488        if Id:
3489            self.EnablePlot = True
3490            self.PatternTree.SelectItem(Id)
3491            self.PatternTree.Expand(Id)
3492        elif phaseId:
3493            self.PatternTree.SelectItem(phaseId)
3494        self.CheckNotebook()
3495        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3496        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3497        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3498        self.LastGPXdir = pth
3499
3500    def OnFileClose(self, event):
3501        '''Clears the data tree in response to the
3502        File/New Project menu button. User is given option to save
3503        the project.
3504        '''
3505        if self.dataFrame:
3506            self.dataFrame.Clear()
3507            self.dataFrame.SetLabel('GSAS-II data display') 
3508        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3509        try:
3510            result = dlg.ShowModal()
3511            if result == wx.ID_OK:
3512                self.OnFileSaveMenu(event)
3513            if result != wx.ID_CANCEL:
3514                self.GSASprojectfile = ''
3515                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3516                self.PatternTree.DeleteChildren(self.root)
3517                if self.HKL: self.HKL = []
3518                if self.G2plotNB.plotList:
3519                    self.G2plotNB.clear()
3520        finally:
3521            dlg.Destroy()
3522
3523    def OnFileSave(self, event):
3524        '''Save the current project in response to the
3525        File/Save Project menu button
3526        '''
3527       
3528        if self.GSASprojectfile: 
3529            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3530            self.CheckNotebook()
3531            G2IO.ProjFileSave(self)
3532        else:
3533            self.OnFileSaveas(event)
3534
3535    def OnFileSaveas(self, event):
3536        '''Save the current project in response to the
3537        File/Save as menu button
3538        '''
3539        if GSASIIpath.GetConfigValue('Starting_directory'):
3540            pth = GSASIIpath.GetConfigValue('Starting_directory')
3541            pth = os.path.expanduser(pth) 
3542        elif self.LastGPXdir:
3543            pth = self.LastGPXdir
3544        else:
3545            pth = '.'
3546        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3547            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3548        try:
3549            if dlg.ShowModal() == wx.ID_OK:
3550                self.GSASprojectfile = dlg.GetPath()
3551                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3552                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3553                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3554                self.CheckNotebook()
3555                G2IO.ProjFileSave(self)
3556                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3557        finally:
3558            dlg.Destroy()
3559           
3560    def ExpandAll(self,event):
3561        '''Expand all tree items or those of a single type
3562        '''
3563        txt = self.GetMenuBar().GetLabel(event.Id)
3564        if txt == 'all':
3565            self.ExpandingAll = True
3566            try:
3567                self.PatternTree.ExpandAll()
3568            finally:
3569                self.ExpandingAll = False
3570        else:
3571            self.ExpandingAll = True
3572            try:
3573                item, cookie = self.PatternTree.GetFirstChild(self.root)
3574                while item:
3575                    name = self.PatternTree.GetItemText(item)
3576                    if name.startswith(txt+' '): self.PatternTree.Expand(item)
3577                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3578            finally:
3579                self.ExpandingAll = False
3580
3581    def MoveTreeItems(self,event):
3582        '''Move tree items of a single type to the end of the tree
3583        '''
3584        txt = self.GetMenuBar().GetLabel(event.Id)
3585        # make a list of items to copy
3586        copyList = []
3587        item, cookie = self.PatternTree.GetFirstChild(self.root)
3588        while item:
3589            if self.PatternTree.GetItemText(item).startswith(txt+' '):
3590                copyList.append(item)
3591            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3592       
3593        self.ExpandingAll = True
3594        try:
3595            for item in copyList:
3596                name = self.PatternTree.GetItemText(item)
3597                newId = self.PatternTree.AppendItem(self.root,name)
3598                self.PatternTree.SetItemPyData(newId,self.PatternTree.GetItemPyData(item))
3599                chld, chldcookie = self.PatternTree.GetFirstChild(item)
3600                while chld:
3601                    chname = self.PatternTree.GetItemText(chld)
3602                    newCh = self.PatternTree.AppendItem(newId,chname)
3603                    self.PatternTree.SetItemPyData(newCh,self.PatternTree.GetItemPyData(chld))
3604                    chld, chldcookie = self.PatternTree.GetNextChild(item, chldcookie)
3605                self.PatternTree.Delete(item)
3606        finally:
3607            self.ExpandingAll = False
3608        G2gd.SelectDataTreeItem(self,self.root)
3609           
3610    def ExitMain(self, event):
3611        '''Called if the main window is closed'''
3612        if self.G2plotNB:
3613            self.G2plotNB.Destroy()
3614        if self.dataFrame:
3615            self.dataFrame.Clear() 
3616            self.dataFrame.Destroy()
3617        if self.undofile:
3618            os.remove(self.undofile)
3619        sys.exit()
3620       
3621    def OnFileExit(self, event):
3622        '''Called in response to the File/Quit menu button'''
3623        if self.G2plotNB:
3624            self.G2plotNB.Destroy()
3625        if self.dataFrame:
3626            self.dataFrame.Clear() 
3627            self.dataFrame.Destroy()
3628        self.Close()
3629       
3630    def OnExportPeakList(self,event):
3631        nptand = lambda x: np.tan(x*math.pi/180.)
3632        pth = G2G.GetExportPath(self)
3633        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3634            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3635        try:
3636            if dlg.ShowModal() == wx.ID_OK:
3637                self.peaklistfile = dlg.GetPath()
3638                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3639                file = open(self.peaklistfile,'w')               
3640                item, cookie = self.PatternTree.GetFirstChild(self.root)
3641                while item:
3642                    name = self.PatternTree.GetItemText(item)
3643                    if 'PWDR' in name:
3644                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3645                        wave = 0.0
3646                        while item2:
3647                            name2 = self.PatternTree.GetItemText(item2)
3648                            if name2 == 'Instrument Parameters':
3649                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3650                                Type = Inst['Type'][0]
3651                                if 'T' not in Type:
3652                                    wave = G2mth.getWave(Inst)
3653                            elif name2 == 'Peak List':
3654                                pkdata = self.PatternTree.GetItemPyData(item2)
3655                                peaks = pkdata['peaks']
3656                                sigDict = pkdata['sigDict']
3657                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3658                        file.write("#%s \n" % (name+' Peak List'))
3659                        if wave:
3660                            file.write('#wavelength = %10.6f\n'%(wave))
3661                        if 'T' in Type:
3662                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3663                        else:
3664                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3665                        for ip,peak in enumerate(peaks):
3666                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3667                            if 'T' in Type:  #TOF - more cols
3668                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3669                                for name in esds.keys():
3670                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3671                                sig = np.sqrt(peak[8])
3672                                gam = peak[10]
3673                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3674                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3675                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3676                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3677                            else:               #CW
3678                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3679                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3680                                for name in esds.keys():
3681                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3682                                sig = np.sqrt(peak[4]) #var -> sig
3683                                gam = peak[6]
3684                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3685                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3686                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3687                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3688                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3689                file.close()
3690        finally:
3691            dlg.Destroy()
3692       
3693    def OnExportHKL(self,event):
3694        pth = G2G.GetExportPath(self)
3695        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3696            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3697        try:
3698            if dlg.ShowModal() == wx.ID_OK:
3699                self.peaklistfile = dlg.GetPath()
3700                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3701                file = open(self.peaklistfile,'w')               
3702                item, cookie = self.PatternTree.GetFirstChild(self.root)
3703                while item:
3704                    name = self.PatternTree.GetItemText(item)
3705                    if 'PWDR' in name:
3706                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3707                        while item2:
3708                            name2 = self.PatternTree.GetItemText(item2)
3709                            if name2 == 'Reflection Lists':
3710                                data = self.PatternTree.GetItemPyData(item2)
3711                                phases = data.keys()
3712                                for phase in phases:
3713                                    peaks = data[phase]
3714                                    I100 = peaks['RefList'].T[8]*np.array([refl[11] for refl in peaks['RefList']])
3715                                    Imax = np.max(I100)
3716                                    if Imax:
3717                                        I100 *= 100.0/Imax
3718                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3719                                    if 'T' in peaks.get('Type','PXC'):
3720                                        file.write('%s \n'%('   h   k   l   m   d-space       TOF       wid     Fo**2     Fc**2     Icorr      Prfo     Trans      ExtP      I100'))
3721                                    else:               
3722                                        file.write('%s \n'%('   h   k   l   m   d-space   2-theta       wid     Fo**2     Fc**2     Icorr      Prfo     Trans      ExtP      I100'))
3723                                    for ipk,peak in enumerate(peaks['RefList']):
3724                                        if 'T' in peaks.get('Type','PXC'):
3725                                            sig = np.sqrt(peak[6])
3726                                            gam = peak[7]
3727                                            FWHM = G2pwd.getgamFW(gam,sig)
3728                                            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" % \
3729                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8],
3730                                                peak[9],peak[11],peak[12],peak[13],peak[14],I100[ipk]))
3731                                        else:
3732                                            sig = np.sqrt(peak[6])
3733                                            gam = peak[7]
3734                                            FWHM = G2pwd.getgamFW(gam,sig)
3735                                            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" % \
3736                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,
3737                                                peak[8],peak[9],peak[11],peak[12],peak[13],peak[14],I100[ipk]))
3738                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3739                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3740                file.close()
3741        finally:
3742            dlg.Destroy()
3743       
3744    def OnExportPDF(self,event):
3745        #need S(Q) and G(R) to be saved here - probably best from selection?
3746        names = G2pdG.GetFileList(self,'PDF')
3747        exports = []
3748        if names:
3749            od = {'label_1':'Export I(Q)','value_1':False,'label_2':'Export S(Q)','value_2':False,
3750                  'label_3':'Export F(Q)','value_3':False,'label_4':'Export G(R)','value_4':True,
3751                  'label_5':'Make G(R) for pdfGUI','value_5':False}
3752            dlg = G2G.G2MultiChoiceDialog(self,'Select','PDF patterns to export',names,extraOpts=od)
3753            if dlg.ShowModal() == wx.ID_OK:
3754                sel = dlg.GetSelections()
3755                for x in sel:
3756                    exports.append(names[x])
3757            dlg.Destroy()
3758        if exports:
3759            PDFsaves = [od['value_1'],od['value_2'],od['value_3'],od['value_4'],od['value_5']]
3760            G2IO.PDFSave(self,exports,PDFsaves)
3761       
3762    def OnMakePDFs(self,event):
3763        '''Sets up PDF data structure filled with defaults; if found chemical formula is inserted
3764        so a default PDF can be made.
3765        '''
3766        sind = lambda x: math.sin(x*math.pi/180.)
3767        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3768        tof2q = lambda t,C:2.0*math.pi*C/t
3769        TextList = []
3770        ElLists = []
3771        Qlimits = []
3772        Names = []
3773        if self.PatternTree.GetCount():
3774            id, cookie = self.PatternTree.GetFirstChild(self.root)
3775            while id:
3776                name = self.PatternTree.GetItemText(id)
3777                Names.append(name)
3778                if 'PWDR' in name:
3779                    TextList.append(name)
3780                    Data = self.PatternTree.GetItemPyData(id)[1]
3781                    pwdrMin = np.min(Data[1])
3782                    Comments = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Comments'))
3783                    Parms = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Instrument Parameters'))[0]
3784                    fullLimits = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,id,'Limits'))[0]
3785                    if 'C' in Parms['Type'][0]:
3786                        wave = G2mth.getWave(Parms)
3787                        qMax = tth2q(fullLimits[1],wave)
3788                    else:   #'T'of
3789                        qMax = tof2q(fullLimits[0],Parms['difC'][1])
3790                    Qlimits.append([0.9*qMax,qMax])
3791                    ElList = {}
3792                    sumnum = 1.
3793                    for item in Comments:           #grab chemical formula from Comments
3794                        if 'formula' in item[:15].lower():
3795                            formula = item.split('=')[1].split()
3796                            try:
3797                                elems = formula[::2]
3798                                nums = formula[1::2]
3799                                Formula = zip(elems,nums)
3800                                sumnum = 0.
3801                                for [elem,num] in Formula:
3802                                    ElData = G2elem.GetElInfo(elem,Parms)
3803                                    ElData['FormulaNo'] = float(num)
3804                                    sumnum += float(num)
3805                                    ElList[elem] = ElData
3806                               
3807                            except ValueError:
3808                                ElData = G2elem.GetElInfo(formula[0],Parms)
3809                                ElData['FormulaNo'] = 1.0
3810                                ElList[elem] = ElData
3811                    ElLists.append(ElList)
3812                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3813            if len(TextList) < 1:
3814                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3815                return
3816            dlg = G2G.G2MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3817            try:
3818                if dlg.ShowModal() == wx.ID_OK:
3819                    for i in dlg.GetSelections():
3820                        PDFnames = G2gd.GetPatternTreeDataNames(self,['PDF ',])
3821                        G2obj.CreatePDFitems(self,TextList[i],ElLists[i],Qlimits[i],sumnum,pwdrMin,PDFnames)
3822                for item in self.ExportPDF: item.Enable(True)
3823            finally:
3824                dlg.Destroy()
3825               
3826    def GetPWDRdatafromTree(self,PWDRname):
3827        ''' Returns powder data from GSASII tree
3828
3829        :param str PWDRname: a powder histogram name as obtained from
3830          :meth:`GSASIIstruct.GetHistogramNames`
3831
3832        :returns: PWDRdata = powder data dictionary with
3833          Powder data arrays, Limits, Instrument Parameters,
3834          Sample Parameters           
3835        '''
3836        PWDRdata = {}
3837        try:
3838            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3839        except ValueError:
3840            PWDRdata['wtFactor'] = 1.0
3841        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3842        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3843        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3844        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3845        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3846        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3847        if 'ranId' not in PWDRdata:  # patch, add a random Id
3848            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3849        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3850            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3851        return PWDRdata
3852
3853    def GetHKLFdatafromTree(self,HKLFname):
3854        ''' Returns single crystal data from GSASII tree
3855
3856        :param str HKLFname: a single crystal histogram name as obtained
3857          from
3858          :meth:`GSASIIstruct.GetHistogramNames`
3859
3860        :returns: HKLFdata = single crystal data list of reflections
3861
3862        '''
3863        HKLFdata = {}
3864        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3865#        try:
3866#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3867#        except ValueError:
3868#            HKLFdata['wtFactor'] = 1.0
3869        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3870        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3871        return HKLFdata
3872       
3873    def GetPhaseData(self):
3874        '''Returns a dict with defined phases.
3875        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3876        get same info from GPX file.
3877        '''
3878        phaseData = {}
3879        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3880            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3881        else:
3882            print 'no phases found in GetPhaseData'
3883            sub = None
3884        if sub:
3885            item, cookie = self.PatternTree.GetFirstChild(sub)
3886            while item:
3887                phaseName = self.PatternTree.GetItemText(item)
3888                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3889                if 'ranId' not in phaseData[phaseName]:
3890                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3891                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3892        return phaseData
3893
3894    def GetPhaseInfofromTree(self):
3895        '''Get the phase names and their rId values,
3896        also the histograms used in each phase.
3897
3898        :returns: (phaseRIdList, usedHistograms) where
3899
3900          * phaseRIdList is a list of random Id values for each phase
3901          * usedHistograms is a dict where the keys are the phase names
3902            and the values for each key are a list of the histogram names
3903            used in each phase.
3904        '''
3905        phaseRIdList = []
3906        usedHistograms = {}
3907        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3908        if sub:
3909            item, cookie = self.PatternTree.GetFirstChild(sub)
3910            while item:
3911                phaseName = self.PatternTree.GetItemText(item)
3912                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3913                if ranId: phaseRIdList.append(ranId)
3914                data = self.PatternTree.GetItemPyData(item)
3915                UseList = data['Histograms']
3916                usedHistograms[phaseName] = UseList.keys()
3917                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3918        return phaseRIdList,usedHistograms
3919
3920    def GetPhaseNames(self):
3921        '''Returns a list of defined phases.
3922        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3923        get same info from GPX file.
3924        '''
3925        phaseNames = []
3926        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3927            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3928        else:
3929            print 'no phases found in GetPhaseNames'
3930            sub = None
3931        if sub:
3932            item, cookie = self.PatternTree.GetFirstChild(sub)
3933            while item:
3934                phase = self.PatternTree.GetItemText(item)
3935                phaseNames.append(phase)
3936                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3937        return phaseNames
3938   
3939    def GetHistogramNames(self,hType):
3940        """ Returns a list of histogram names found in the GSASII data tree
3941        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3942        get same info from GPX file.
3943       
3944        :param str hType: list of histogram types
3945        :return: list of histogram names
3946       
3947        """
3948        HistogramNames = []
3949        if self.PatternTree.GetCount():
3950            item, cookie = self.PatternTree.GetFirstChild(self.root)
3951            while item:
3952                name = self.PatternTree.GetItemText(item)
3953                if name[:4] in hType:
3954                    HistogramNames.append(name)       
3955                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3956
3957        return HistogramNames
3958                   
3959    def GetUsedHistogramsAndPhasesfromTree(self):
3960        ''' Returns all histograms that are found in any phase
3961        and any phase that uses a histogram.
3962        This also assigns numbers to used phases and histograms by the
3963        order they appear in the file.
3964        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3965        get same info from GPX file.
3966
3967        :returns: (Histograms,Phases)
3968
3969            * Histograms = dictionary of histograms as {name:data,...}
3970            * Phases = dictionary of phases that use histograms
3971        '''
3972        Histograms = {}
3973        Phases = {}
3974        phaseNames = self.GetPhaseNames()
3975        phaseData = self.GetPhaseData()
3976        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3977
3978        for phase in phaseData:
3979            Phase = phaseData[phase]
3980            pId = phaseNames.index(phase)
3981            Phase['pId'] = pId
3982            if Phase['Histograms']:
3983                if phase not in Phases:
3984                    Phases[phase] = Phase
3985                for hist in Phase['Histograms']:
3986                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3987                        Phase['Histograms'][hist]['Use'] = True         
3988                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3989                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3990                        if item:
3991                            if 'PWDR' in hist[:4]: 
3992                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3993                            elif 'HKLF' in hist[:4]:
3994                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3995                            hId = histoList.index(hist)
3996                            Histograms[hist]['hId'] = hId
3997                        else: # would happen if a referenced histogram were renamed or deleted
3998                            print('For phase "'+phase+
3999                                  '" unresolved reference to histogram "'+hist+'"')
4000        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
4001        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
4002        return Histograms,Phases
4003       
4004    def MakeLSParmDict(self):
4005        '''Load all parameters used for computation from the tree into a
4006        dict of paired values [value, refine flag]. Note that this is
4007        different than the parmDict used in the refinement, which only has
4008        values.
4009
4010        Note that similar things are done in
4011        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
4012        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
4013        a GPX file).
4014
4015        :returns: (parmDict,varyList) where:
4016
4017         * parmDict is a dict with values and refinement flags
4018           for each parameter and
4019         * varyList is a list of variables (refined parameters).
4020        '''
4021        parmDict = {}
4022        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
4023        for phase in Phases:
4024            if 'pId' not in Phases[phase]:
4025                self.ErrorDialog('View parameter error','You must run least squares at least once')
4026                raise Exception,'No pId for phase '+phase
4027        rigidbodyDict = self.PatternTree.GetItemPyData(   
4028            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
4029        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
4030        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
4031        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
4032        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
4033        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
4034        varyList = rbVary+phaseVary+hapVary+histVary
4035        parmDict.update(rbDict)
4036        parmDict.update(phaseDict)
4037        parmDict.update(hapDict)
4038        parmDict.update(histDict)
4039        for parm in parmDict:
4040            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
4041                'Omega','Chi','Phi','nDebye','nPeaks']:
4042                parmDict[parm] = [parmDict[parm],'-']
4043            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
4044                parmDict[parm] = [parmDict[parm],'-']
4045            elif parm in varyList:
4046                parmDict[parm] = [parmDict[parm],'T']
4047            else:
4048                parmDict[parm] = [parmDict[parm],'F']
4049        # for i in parmDict: print i,'\t',parmDict[i]
4050        # fl = open('parmDict.dat','wb')
4051        # import cPickle
4052        # cPickle.dump(parmDict,fl,1)
4053        # fl.close()
4054        return parmDict,varyList
4055
4056    def OnShowLSParms(self,event):
4057        '''Displays a window showing all parameters in the refinement.
4058        Called from the Calculate/View LS Parms menu.
4059        '''
4060        parmDict,varyList = self.MakeLSParmDict()
4061        parmValDict = {}
4062        for i in parmDict:
4063            parmValDict[i] = parmDict[i][0]
4064           
4065        reqVaryList = tuple(varyList) # save requested variables
4066        try:
4067            # process constraints
4068            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
4069            Constraints = self.PatternTree.GetItemPyData(sub)
4070            constList = []
4071            for item in Constraints:
4072                if item.startswith('_'): continue
4073                constList += Constraints[item]
4074            G2mv.InitVars()
4075            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
4076            groups,parmlist = G2mv.GroupConstraints(constrDict)
4077            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
4078            G2mv.Map2Dict(parmValDict,varyList)
4079        except:
4080            pass
4081        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
4082        dlg.ShowModal()
4083        dlg.Destroy()
4084
4085    def OnRefine(self,event):
4086        '''Perform a refinement.
4087        Called from the Calculate/Refine menu.
4088        '''
4089        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4090        if Id:
4091            dlg = wx.MessageDialog(
4092                self,
4093                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
4094                'Remove sequential results?',wx.OK|wx.CANCEL)
4095            if dlg.ShowModal() == wx.ID_OK:
4096                self.PatternTree.Delete(Id)
4097                dlg.Destroy()
4098            else:
4099                dlg.Destroy()
4100                return
4101        self.OnFileSave(event)
4102        # check that constraints are OK here
4103        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
4104        if errmsg:
4105            self.ErrorDialog('Refinement error',errmsg)
4106            return
4107        if warnmsg:
4108            print('Conflict between refinment flag settings and constraints:\n'+
4109                warnmsg+'\nRefinement not possible')
4110            self.ErrorDialog('Refinement Flag Error',
4111                'Conflict between refinement flag settings and constraints:\n'+
4112                warnmsg+'\nRefinement not possible')
4113            return
4114        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
4115            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
4116            parent=self)
4117        Size = dlg.GetSize()
4118        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
4119            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
4120        dlg.CenterOnParent()
4121        Rw = 100.00
4122        self.SaveTreeSetting()
4123        self.PatternTree.SaveExposedItems()       
4124        try:
4125            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)    #Msg is Rvals dict if Ok=True
4126        finally:
4127            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
4128            dlg.Destroy()
4129            wx.Yield()
4130        if OK:
4131            Rw = Msg['Rwp']
4132            lamMax = Msg.get('lamMax',0.001)
4133            text = 'Load new result?'
4134            if lamMax >= 10.:
4135                text += '\nWARNING: Steepest descents dominates;'+   \
4136                ' minimum may not have been reached\nor result may be false minimum.'+  \
4137                ' You should reconsider your parameter suite'
4138            dlg2 = wx.MessageDialog(self,text,'Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
4139            try:
4140                if dlg2.ShowModal() == wx.ID_OK:
4141                    self.PatternTree.DeleteChildren(self.root)
4142                    self.HKL = []
4143                    G2IO.ProjFileOpen(self,False)
4144                    self.PatternTree.RestoreExposedItems()       
4145                    self.ResetPlots()
4146            finally:
4147                dlg2.Destroy()
4148        else:
4149            self.ErrorDialog('Refinement error',Msg)
4150       
4151    def SaveTreeSetting(self):
4152        'Save the last tree setting'
4153        oldId =  self.PatternTree.GetSelection()        #retain current selection
4154        oldPath = self.GetTreeItemsList(oldId)
4155        self.lastTreeSetting = oldPath
4156        # note that for reasons unclear, it does not seem necessary to reload the Atoms tab
4157        #parentName = ''
4158        #tabId = None
4159        # parentId = self.PatternTree.GetItemParent(oldId)
4160        # if parentId:
4161        #     parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
4162        #     if 'Phases' in parentName:
4163        #         tabId = self.dataDisplay.GetSelection()
4164        #self.lastTreeSetting = oldPath,tabId
4165        #GSASIIpath.IPyBreak()
4166       
4167    def TestResetPlot(self,event):
4168        '''Debug code to test cleaning up plots after a refinement'''
4169        #for i in range(self.G2plotNB.nb.GetPageCount()):
4170        #    [self.G2plotNB.nb.GetPageText(i)
4171        # save current tree item and (if needed) atoms tab
4172        self.SaveTreeSetting()
4173        self.ResetPlots()
4174       
4175    def ResetPlots(self):
4176        '''This reloads the current tree item, often drawing a plot. It refreshes any plots
4177        that have registered a refresh routine (see G2plotNB.RegisterRedrawRoutine)
4178        and deletes all plots that have not been refreshed and
4179        require one (see G2plotNB.SetNoDelete).
4180        '''
4181        lastRaisedPlotTab = self.G2plotNB.lastRaisedPlotTab # save the last page saved
4182        #print 'lastRaisedPlotTab=',lastRaisedPlotTab
4183        self.G2plotNB.lastRaisedPlotTab = None
4184        # mark displayed plots as invalid
4185        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
4186            frame.plotInvalid = True
4187        # reload current tree item, triggering the routine to redraw the data window and possibly a plot
4188        #oldPath,tabId = self.lastTreeSetting
4189        oldPath = self.lastTreeSetting
4190        Id = self.root
4191        for txt in oldPath:
4192            Id = G2gd.GetPatternTreeItemId(self, Id, txt)
4193        self.PickIdText = None  #force reload of page
4194        if Id:
4195            self.PickId = Id
4196            self.PatternTree.SelectItem(Id)
4197        # update other self-updating plots
4198#        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
4199#            if frame.plotInvalid and frame.replotFunction:
4200#                frame.replotFunction(*frame.replotArgs,**frame.replotKWargs)
4201        # delete any remaining plots that are still invalid and need a refresh
4202        for lbl,frame in zip(self.G2plotNB.plotList,self.G2plotNB.panelList):
4203            if frame.plotInvalid and frame.plotRequiresRedraw:
4204                self.G2plotNB.Delete(lbl)
4205        # put the previously last-raised tab on top, if present. If not, use the one corresponding to
4206        # the last tree item to be selected
4207        wx.CallAfter(self.G2plotNB.RaiseLastPage,lastRaisedPlotTab,self.G2plotNB.lastRaisedPlotTab)
4208       
4209    def OnSeqRefine(self,event):
4210        '''Perform a sequential refinement.
4211        Called from the Calculate/Sequential refine menu.
4212        '''
4213        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4214        if not Id:
4215            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
4216            self.PatternTree.SetItemPyData(Id,{})           
4217        self.G2plotNB.Delete('Sequential refinement')    #clear away probably invalid plot
4218        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
4219        if not Controls.get('Seq Data'):
4220            print('Error: a sequential refinement has not been set up')
4221            return
4222        Controls['ShowCell'] = True
4223        self.OnFileSave(event)
4224        # check that constraints are OK here
4225        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
4226        if errmsg:
4227            self.ErrorDialog('Refinement error',errmsg)
4228            return
4229        if warnmsg:
4230            print('Conflict between refinment flag settings and constraints:\n'+
4231                  warnmsg+'\nRefinement not possible')
4232            self.ErrorDialog('Refinement Flag Error',
4233                             'Conflict between refinment flag settings and constraints:\n'+
4234                             warnmsg+'\nRefinement not possible')
4235            return
4236        self.PatternTree.SaveExposedItems()       
4237        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
4238            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
4239            parent=self)           
4240        Size = dlg.GetSize()
4241        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
4242            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
4243        dlg.CenterOnParent()
4244        try:
4245            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)     #Msg is Rvals dict if Ok=True
4246        finally:
4247            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
4248            dlg.Destroy()
4249            wx.Yield()
4250        if OK:
4251            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
4252            try:
4253                if dlg.ShowModal() == wx.ID_OK:
4254                    self.PickIdText = None  #force reload of PickId contents
4255                    self.PatternTree.DeleteChildren(self.root)
4256                    if len(self.HKL): self.HKL = []
4257                    if self.G2plotNB.plotList:
4258                        self.G2plotNB.clear()
4259                    G2IO.ProjFileOpen(self,False)
4260                    self.PatternTree.RestoreExposedItems()
4261                    self.ResetPlots()
4262                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
4263                    self.PatternTree.SelectItem(Id)
4264            finally:
4265                dlg.Destroy()
4266        else:
4267            self.ErrorDialog('Sequential refinement error',Msg)
4268       
4269    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
4270        'Display an error message'
4271        result = None
4272        if parent is None:
4273            dlg = wx.MessageDialog(self, message, title,  wtype)
4274        else:
4275            dlg = wx.MessageDialog(parent, message, title,  wtype)
4276            dlg.CenterOnParent() # not working on Mac
4277        try:
4278            result = dlg.ShowModal()
4279        finally:
4280            dlg.Destroy()
4281        return result
4282   
4283    def OnSaveMultipleImg(self,event):
4284        '''Select and save multiple image parameter and mask files
4285        '''
4286        G2IO.SaveMultipleImg(self)
4287       
4288class GSASIImain(wx.App):
4289    '''Defines a wxApp for GSAS-II
4290
4291    Creates a wx frame (self.main) which contains the display of the
4292    data tree.
4293    '''
4294    def OnInit(self):
4295        '''Called automatically when the app is created.'''
4296        import platform
4297        if '2.7' not in sys.version[:5]:
4298            dlg = wx.MessageDialog(None, 
4299                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
4300                'Python version error',  wx.OK)
4301            try:
4302                dlg.ShowModal()
4303            finally:
4304                dlg.Destroy()
4305            sys.exit()
4306        self.main = GSASII(None)
4307        self.main.Show()
4308        self.SetTopWindow(self.main)
4309        # save the current package versions
4310        self.main.PackageVersions = []
4311        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
4312        for p in (wx,mpl,np,sp,ogl):
4313            self.main.PackageVersions.append([p.__name__,p.__version__])
4314        try:
4315            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
4316        except:
4317            try:
4318                from PIL import PILLOW_VERSION
4319                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
4320            except:
4321                pass
4322        self.main.PackageVersions.append(['Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
4323       
4324        return True
4325    # def MacOpenFile(self, filename):
4326    #     '''Called on Mac every time a file is dropped on the app when it is running,
4327    #     treat this like a File/Open project menu action.
4328    #     Should be ignored on other platforms
4329    #     '''
4330    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
4331    #     print 'MacOpen',filename
4332    #     if os.path.splitext(filename)[1] == '.py': return
4333    #     # end PATCH
4334    #     self.main.OnFileOpen(None,filename)
4335    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
4336       
4337def main():
4338    '''Start up the GSAS-II application'''
4339    #application = GSASIImain() # don't redirect output, someday we
4340    # may want to do this if we can
4341    application = GSASIImain(0)
4342    if GSASIIpath.GetConfigValue('wxInspector'):
4343        import wx.lib.inspection as wxeye
4344        wxeye.InspectionTool().Show()
4345
4346    #application.main.OnRefine(None)
4347    application.MainLoop()
4348   
4349if __name__ == '__main__':
4350    # print versions
4351    print "Python module versions loaded:"
4352    print "  Python:     ",sys.version.split()[0]
4353    print "  wx:         ",wx.__version__
4354    print "  matplotlib: ",mpl.__version__
4355    print "  numpy:      ",np.__version__
4356    print "  scipy:      ",sp.__version__
4357    print "  OpenGL:     ",ogl.__version__
4358    try:
4359        from PIL import Image
4360        try:
4361            from PIL import PILLOW_VERSION
4362            version = PILLOW_VERSION
4363        except:
4364            version = Image.VERSION
4365        print "  PIL.Image:  ",version
4366    except ImportError:
4367        try:
4368            import Image
4369            print "Image (PIL):",Image.VERSION
4370        except ImportError:
4371            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
4372    import platform
4373    print "  Platform:   ",sys.platform,platform.architecture()[0],platform.machine()
4374    try:
4375        import mkl
4376        print "  Max threads:",mkl.get_max_threads()
4377    except:
4378        pass
4379    #print "wxPython description",wx.PlatformInfo
4380    print "This is GSAS-II revision "+str(GSASIIpath.GetVersionNumber())+'\n'
4381    GSASIIpath.InvokeDebugOpts()
4382    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.