source: trunk/GSASII.py @ 2646

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

added waterfall colors to single & multiple I(Q)..G(r) plots
a start on fitting stacking fault models - does nothing at present

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