source: trunk/GSASII.py @ 2674

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

prevent delete if PWDR if corresponding PDF exists
use PickElements? instead of PickElement? - allows multiple selection, blacks out ones already picked & can unpick a pick
add delete spots option to masks

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