source: trunk/GSASII.py @ 2753

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

minor shuffling about for atom constraints - no discernable impact
setup default reflectometry data stub in G2pwdrGUI
setup reflectometry (REFD) data import stub (no importers yet) & generic data handling for REFD tree entries in GGSAII

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