source: trunk/GSASII.py @ 2152

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

all PWDR exporters will make file name from histogram name
allow read of multibank data
alert user to duplicate histograms (by name)
rename data will not change Bank or Azm part of histogram name
fix L&R plotting commands for TOF data

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