source: trunk/GSASII.py @ 2363

Last change on this file since 2363 was 2363, checked in by toby, 5 years ago

fix minor bugs in sequential refinement

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