source: trunk/GSASII.py @ 2196

Last change on this file since 2196 was 2196, checked in by toby, 6 years ago

redo plot window graphics to redraw after refinement (need to update more plotting routines & save plot zoom stack, etc.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 185.5 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2016-04-05 22:24:38 +0000 (Tue, 05 Apr 2016) $
6# $Author: toby $
7# $Revision: 2196 $
8# $URL: trunk/GSASII.py $
9# $Id: GSASII.py 2196 2016-04-05 22:24:38Z toby $
10########### SVN repository information ###################
11'''
12*GSAS-II Main Module*
13=====================
14
15Main routines for the GSAS-II program
16'''
17
18import os
19import sys
20import math
21import copy
22import random as ran
23import time
24import copy
25import glob
26import imp
27import inspect
28import numpy as np
29import scipy as sp
30import wx
31import matplotlib as mpl
32try:
33    import OpenGL as ogl
34except ImportError:
35    print('*******************************************************')
36    print('PyOpenGL is missing from your python installation')
37    print('     - we will try to install it')
38    print('*******************************************************')
39    def install_with_easyinstall(package):
40        try: 
41            print "trying a system-wide PyOpenGl install"
42            easy_install.main(['-f',os.path.split(__file__)[0],package])
43            return
44        except:
45            pass
46        try: 
47            print "trying a user level PyOpenGl install"
48            easy_install.main(['-f',os.path.split(__file__)[0],'--user',package])
49            return
50        except:
51            print "Install of '+package+' failed. Please report this information:"
52            import traceback
53            print traceback.format_exc()
54            sys.exit()
55    from setuptools.command import easy_install
56    install_with_easyinstall('PyOpenGl')
57    print('*******************************************************')         
58    print('OpenGL has been installed. Restarting GSAS-II')
59    print('*******************************************************')         
60    loc = os.path.dirname(__file__)
61    import subprocess
62    subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
63    sys.exit()
64   
65# load the GSAS routines
66import GSASIIpath
67GSASIIpath.SetVersionNumber("$Revision: 2196 $")
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=True)
1253                if res is None: return None
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,numbanks,rd)
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,numbanks,rd)    #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 '.prm','.inst','.ins','.instprm':
1283            if self.zipfile:
1284                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1285                    selection=os.path.split(basename + ext)[1],parent=self)
1286                if instfile == None:
1287                    continue
1288            else:
1289                instfile = basename + ext
1290            if not os.path.exists(instfile):
1291                continue
1292            if 'instprm' in instfile:
1293                Lines = self.OpenPowderInstprm(instfile)
1294                instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1295                if 'list' in str(type(instParmList)):
1296                    rd.instfile = instfile
1297                    rd.instmsg = 'GSAS-II file '+instfile
1298                    return instParmList
1299                else:
1300                    #print 'debug: open/read failed',instfile
1301                    pass # fail silently
1302            else:
1303                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1304                if Iparm:
1305                    #print 'debug: success'
1306                    rd.instfile = instfile
1307                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1308                    return SetPowderInstParms(Iparm,rd)
1309                else:
1310                    #print 'debug: open/read failed',instfile
1311                    pass # fail silently
1312
1313        #2nd priority: is there an instrument parameter file defined for the current data set?
1314        # or if this is a read on a set of set of files, use the last one again
1315        #if rd.instparm as found in data file header or (lastdatafile == filename and lastIparmfile):
1316        if rd.instparm or lastIparmfile:
1317            if rd.instparm:
1318                instfile = os.path.join(os.path.split(filename)[0],rd.instparm)
1319            else:
1320                # for multiple reads of one data file, reuse the inst parm file
1321                instfile = lastIparmfile
1322#            if self.zipfile:
1323#                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1324#                    selection=os.path.split(instfile)[1],parent=self)
1325            if instfile != None and os.path.exists(instfile):
1326                #print 'debug: try read',instfile
1327                if 'instprm' in instfile:   #GSAS-II file must have .instprm as extension
1328                    Lines = self.OpenPowderInstprm(instfile)
1329                    if Lines is not None:
1330                        instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)   #this is [Inst1,Inst2] a pair of dicts
1331                else:   #old GSAS style iparm file - could be named anything!
1332                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1333                    if Iparm:
1334                        #print 'debug: success'
1335                        rd.instfile = instfile
1336                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1337                        instParmList = SetPowderInstParms(Iparm,rd)     #this is [Inst1,Inst2] a pair of dicts
1338                if 'list' in str(type(instParmList)):   #record stuff & return stuff
1339                    rd.instfile = instfile
1340                    rd.instmsg = 'GSAS-II file '+instfile
1341                    return instParmList
1342                else:   #bad iparms - try default
1343                    rd.instmsg = instParmList   #an error message
1344                    return GetDefaultParms(self,rd)
1345            else:
1346                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1347                    +str(instfile)+' requested by file '+ filename)
1348#        # did we read the data file from a zip? If so, look there for a
1349#        # instrument parameter file
1350#        if self.zipfile:
1351#            for ext in '.instprm','.prm','.inst','.ins':
1352#                instfile = G2IO.ExtractFileFromZip(self.zipfile,
1353#                    selection=os.path.split(basename + ext)[1],parent=self)
1354#                if instfile is not None and instfile != self.zipfile:
1355#                    print 'debug:',instfile,'created from ',self.zipfile
1356#                    Lines = self.OpenPowderInstprm(instfile)
1357#                    instParmList = None
1358#                    if Lines is not None:
1359#                        instParmList = self.ReadPowderInstprm(Lines,bank)    #this is [Inst1,Inst2] a pair of dicts
1360#                    if 'dict' in str(type(instParmList)):
1361#                        rd.instfile = instfile
1362#                        rd.instmsg = 'GSAS-II file '+instfile
1363#                        return instParmList
1364#                    else:
1365#                        rd.instmsg = instParmList   #an error message
1366#                        print 'three',instParmList
1367#                        return GetDefaultParms(self,rd)
1368#                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1369#                    if Iparm:
1370#                        rd.instfile = instfile
1371#                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1372#                        return SetPowderInstParms(Iparm,rd)
1373#                    else:
1374#                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
1375#                        pass # fail silently
1376#
1377        #Finally - ask user for Instrument parametrs file - seems it can't be in a zip file
1378        while True: # loop until we get a file that works or we get a cancel
1379            instfile = ''
1380            pth = G2G.GetImportPath(self)
1381            if not pth: pth = '.'
1382            dlg = wx.FileDialog(self,
1383                'Choose inst. param file for "'+rd.idstring+'" (or Cancel for default)',
1384                pth, '',
1385                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1386                'GSAS-II iparm file (*.instprm)|*.instprm|'
1387                'All files (*.*)|*.*', wx.OPEN)
1388            if os.path.exists(lastIparmfile):
1389                dlg.SetFilename(lastIparmfile)
1390            if dlg.ShowModal() == wx.ID_OK:
1391                instfile = dlg.GetPath()
1392            dlg.Destroy()
1393            if not instfile: 
1394                return GetDefaultParms(self,rd) #on Cancel/break
1395            if 'instprm' in instfile:
1396                Lines = self.OpenPowderInstprm(instfile)
1397                if Lines is not None:
1398                    instParmList = self.ReadPowderInstprm(Lines,bank,numbanks,rd)    #this is [Inst1,Inst2] a pair of dicts
1399                if 'list' in str(type(instParmList)):
1400                    rd.instfile = instfile
1401                    rd.instmsg = 'GSAS-II file '+instfile
1402                    return instParmList
1403                else:
1404                    rd.instmsg = instParmList   #an error message
1405                    return GetDefaultParms(self,rd)
1406            else:
1407                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1408                if Iparm:
1409                    #print 'debug: success with',instfile
1410                    rd.instfile = instfile
1411                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1412                    return SetPowderInstParms(Iparm,rd)
1413                else:
1414                    self.ErrorDialog('Read Error',
1415                                     'Error opening/reading file '+str(instfile))
1416       
1417    def OnImportPowder(self,event):
1418        '''Called in response to an Import/Powder Data/... menu item
1419        to read a powder diffraction data set.
1420        dict self.ImportMenuId is used to look up the specific
1421        reader item associated with the menu item, which will be
1422        None for the last menu item, which is the "guess" option
1423        where all appropriate formats will be tried.
1424
1425        Also reads an instrument parameter file for each dataset.
1426        '''
1427        # get a list of existing histograms
1428        PWDRlist = []
1429        if self.PatternTree.GetCount():
1430            item, cookie = self.PatternTree.GetFirstChild(self.root)
1431            while item:
1432                name = self.PatternTree.GetItemText(item)
1433                if name.startswith('PWDR ') and name not in PWDRlist:
1434                    PWDRlist.append(name)
1435                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1436        # look up which format was requested
1437        reqrdr = self.ImportMenuId.get(event.GetId()) 
1438        rdlist = self.OnImportGeneric(
1439            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1440        if len(rdlist) == 0: return
1441        self.CheckNotebook()
1442        Iparm = None
1443        lastIparmfile = ''
1444        lastdatafile = ''
1445        newHistList = []
1446        self.EnablePlot = False
1447        for rd in rdlist:
1448            if 'Instrument Parameters' not in rd.pwdparms:
1449                # get instrument parameters for each dataset, unless already set
1450                Iparms = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1451                if not Iparms:  #may have bailed out
1452                    Id = 0
1453                    continue
1454                Iparm1,Iparm2 = Iparms
1455                if rd.repeat_instparm: 
1456                    lastIparmfile = rd.instfile
1457                # override any keys in read instrument parameters with ones set in import
1458                for key in Iparm1: 
1459                    if key in rd.instdict:
1460                        Iparm1[key] = rd.instdict[key]
1461            else:
1462                Iparm1,Iparm2 = rd.pwdparms['Instrument Parameters']
1463            lastdatafile = rd.powderentry[0]
1464            HistName = rd.idstring
1465            HistName = 'PWDR '+HistName
1466            # make new histogram names unique
1467            if HistName in PWDRlist:
1468                dlg = wx.MessageDialog(self,'Skip %s?'%(HistName),'Duplicate data name',wx.YES_NO)
1469                try:
1470                    if dlg.ShowModal() == wx.ID_YES:
1471                        Id = 0
1472                        continue
1473                finally:
1474                    dlg.Destroy()
1475            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1476            print 'Read powder data '+str(HistName)+ \
1477                ' from file '+str(rd.readfilename) + \
1478                ' with parameters from '+str(rd.instmsg)
1479            # data are read, now store them in the tree
1480            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1481            if 'T' in Iparm1['Type'][0]:
1482                if not rd.clockWd and rd.GSAS:
1483                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1484                cw = np.diff(rd.powderdata[0])
1485                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1486                if rd.GSAS:     #NB: old GSAS wanted intensities*CW even if normalized!
1487                    npts = min(len(rd.powderdata[0]),len(rd.powderdata[1]),len(cw))
1488                    rd.powderdata[1] = rd.powderdata[1][:npts]/cw[:npts]
1489                    rd.powderdata[2] = rd.powderdata[2][:npts]*cw[:npts]**2  #1/var=w at this point
1490                else:       #NB: from topas/fullprof type files
1491                    rd.powderdata[1] = rd.powderdata[1][:-1]
1492                    rd.powderdata[2] = rd.powderdata[2][:-1]
1493                if 'Itype' in Iparm2:
1494                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1495                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1496                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1497                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1498                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1499                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1500                    var += WYI*rd.powderdata[1]**2
1501                    var /= YI**2
1502                    rd.powderdata[2] = 1./var
1503                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1504                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1505                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1506            valuesdict = {
1507                'wtFactor':1.0,
1508                'Dummy':False,
1509                'ranId':ran.randint(0,sys.maxint),
1510                'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1511                'qPlot':False,'dPlot':False,'sqrtPlot':False
1512                }
1513            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1514            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1515            self.PatternTree.SetItemPyData(
1516                self.PatternTree.AppendItem(Id,text='Comments'),
1517                rd.comments)
1518            Tmin = min(rd.powderdata[0])
1519            Tmax = max(rd.powderdata[0])
1520            self.PatternTree.SetItemPyData(
1521                self.PatternTree.AppendItem(Id,text='Limits'),
1522                rd.pwdparms.get('Limits',[(Tmin,Tmax),[Tmin,Tmax]])
1523                )
1524            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1525            self.PatternTree.SetItemPyData(
1526                self.PatternTree.AppendItem(Id,text='Background'),
1527                rd.pwdparms.get('Background',
1528                    [['chebyschev',True,3,1.0,0.0,0.0],{'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1529                    )
1530            self.PatternTree.SetItemPyData(
1531                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1532                [Iparm1,Iparm2])
1533            self.PatternTree.SetItemPyData(
1534                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1535                rd.Sample)
1536            self.PatternTree.SetItemPyData(
1537                self.PatternTree.AppendItem(Id,text='Peak List')
1538                ,{'peaks':[],'sigDict':{}})
1539            self.PatternTree.SetItemPyData(
1540                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1541                [[],[]])
1542            self.PatternTree.SetItemPyData(
1543                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1544                [])
1545            self.PatternTree.SetItemPyData(
1546                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1547                {})
1548            # if any Control values have been set, move them into tree
1549            Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
1550            Controls.update(rd.Controls)
1551            newHistList.append(HistName)
1552            rd.repeat_instparm = False  #clear the iparm reuse flag
1553        else:
1554            self.EnablePlot = True
1555            if Id:
1556                self.PatternTree.Expand(Id)
1557                self.PatternTree.SelectItem(Id)
1558
1559        if not newHistList: return # somehow, no new histograms
1560        # make a list of phase names
1561        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1562        phaseNameList = usedHistograms.keys() # phase names in use
1563        if not phaseNameList: return # no phases yet, nothing to do
1564        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1565        for Name in newHistList:
1566            header += '\n  '+str(Name)
1567
1568        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1569        if not result: return
1570        # connect new phases to histograms
1571        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1572        if not sub:
1573            raise Exception('ERROR -- why are there no phases here?')
1574        item, cookie = self.PatternTree.GetFirstChild(sub)
1575        iph = -1
1576        while item: # loop over (new) phases
1577            iph += 1
1578            phaseName = self.PatternTree.GetItemText(item)
1579            data = self.PatternTree.GetItemPyData(item)
1580            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1581            if iph not in result: continue
1582            generalData = data['General']
1583            SGData = generalData['SGData']
1584            UseList = data['Histograms']
1585            NShkl = len(G2spc.MustrainNames(SGData))
1586            NDij = len(G2spc.HStrainNames(SGData))
1587            for histoName in newHistList:
1588                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1589                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1590                refList = self.PatternTree.GetItemPyData(
1591                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1592                refList[generalData['Name']] = []
1593        return # success
1594
1595    def OnDummyPowder(self,event):
1596        '''Called in response to Import/Powder Data/Simulate menu item
1597        to create a Dummy powder diffraction data set.
1598
1599        Reads an instrument parameter file and then gets input from the user
1600        '''
1601        # get a list of existing histograms
1602        PWDRlist = []
1603        if self.PatternTree.GetCount():
1604            item, cookie = self.PatternTree.GetFirstChild(self.root)
1605            while item:
1606                name = self.PatternTree.GetItemText(item)
1607                if name.startswith('PWDR ') and name not in PWDRlist:
1608                    PWDRlist.append(name)
1609                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1610        # Initialize a base class reader
1611        rd = G2IO.ImportPowderData(
1612            extensionlist=tuple(),
1613            strictExtension=False,
1614            formatName = 'Simulate dataset',
1615            longFormatName = 'Compute a simulated pattern')
1616        rd.powderentry[0] = '' # no filename
1617        # #self.powderentry[1] = pos # bank offset (N/A here)
1618        rd.powderentry[2] = 1 # only one bank
1619        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1620        self.CheckNotebook()
1621        Iparm = None
1622        lastIparmfile = ''
1623        lastdatafile = ''
1624        self.zipfile = None
1625        # get instrument parameters for it
1626        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1627        if 'T' in Iparm1['Type'][0]:
1628            print('TOF simulation not supported yet')
1629            return False
1630        else:
1631            # need to get name, 2theta start, end, step
1632            rd.idstring = ' CW'
1633            if 'X' in Iparm1['Type'][0]:
1634                rd.idstring = 'CW x-ray simulation'
1635            else:
1636                rd.idstring = 'CW neutron simulation'
1637            # base initial range on wavelength
1638            wave = Iparm1.get('Lam')
1639            if wave:
1640                wave = wave[0]
1641            else:
1642                wave = Iparm1.get('Lam1')
1643                if wave:
1644                    wave = wave[0]
1645        N = 0
1646        while (N < 3): # insist on a dataset with a few points
1647            names = ('dataset name', 'start angle', 'end angle', 'step size')
1648            if not wave or wave < 1.0:
1649                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1650            else:
1651                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1652            dlg = G2G.ScrolledMultiEditor(
1653                self,[inp] * len(inp),range(len(inp)),names,
1654                header='Enter simulation name and range',
1655                minvals=(None,0.001,0.001,0.0001),
1656                maxvals=(None,180.,180.,.1),
1657                sizevals=((225,-1),)
1658                )
1659            dlg.CenterOnParent()
1660            if dlg.ShowModal() == wx.ID_OK:
1661                if inp[1] > inp[2]:
1662                    end,start,step = inp[1:]
1663                else:               
1664                    start,end,step = inp[1:]
1665                step = abs(step)
1666            else:
1667                return False
1668            N = int((end-start)/step)+1
1669            x = np.linspace(start,end,N,True)
1670            N = len(x)
1671        rd.powderdata = [
1672            np.array(x), # x-axis values
1673            np.zeros_like(x), # powder pattern intensities
1674            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1675            np.zeros_like(x), # calc. intensities (zero)
1676            np.zeros_like(x), # calc. background (zero)
1677            np.zeros_like(x), # obs-calc profiles
1678            ]
1679        Tmin = rd.powderdata[0][0]
1680        Tmax = rd.powderdata[0][-1]
1681        # data are read, now store them in the tree
1682        HistName = inp[0]
1683        HistName = 'PWDR '+HistName
1684        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1685        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1686        valuesdict = {
1687            'wtFactor':1.0,
1688            'Dummy':True,
1689            'ranId':ran.randint(0,sys.maxint),
1690            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
1691            'qPlot':False,'dPlot':False,'sqrtPlot':False
1692            }
1693        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1694        self.PatternTree.SetItemPyData(
1695            self.PatternTree.AppendItem(Id,text='Comments'),
1696            rd.comments)
1697        self.PatternTree.SetItemPyData(
1698            self.PatternTree.AppendItem(Id,text='Limits'),
1699            [(Tmin,Tmax),[Tmin,Tmax]])
1700        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1701        self.PatternTree.SetItemPyData(
1702            self.PatternTree.AppendItem(Id,text='Background'),
1703            [['chebyschev',True,3,1.0,0.0,0.0],
1704             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1705        self.PatternTree.SetItemPyData(
1706            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1707            [Iparm1,Iparm2])
1708        self.PatternTree.SetItemPyData(
1709            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1710            rd.Sample)
1711        self.PatternTree.SetItemPyData(
1712            self.PatternTree.AppendItem(Id,text='Peak List')
1713            ,{'peaks':[],'sigDict':{}})
1714        self.PatternTree.SetItemPyData(
1715            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1716            [[],[]])
1717        self.PatternTree.SetItemPyData(
1718            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1719            [])
1720        self.PatternTree.SetItemPyData(
1721            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1722            {})
1723        self.PatternTree.Expand(Id)
1724        self.PatternTree.SelectItem(Id)
1725        print('Added simulation powder data '+str(HistName)+ 
1726              ' with parameters from '+str(rd.instmsg))
1727
1728        # make a list of phase names
1729        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1730        phaseNameList = usedHistograms.keys() # phase names in use
1731        if not phaseNameList: return # no phases yet, nothing to do
1732        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1733        result = G2G.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1734        if not result: return
1735        # connect new phases to histograms
1736        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1737        if not sub:
1738            raise Exception('ERROR -- why are there no phases here?')
1739        item, cookie = self.PatternTree.GetFirstChild(sub)
1740        iph = -1
1741        while item: # loop over (new) phases
1742            iph += 1
1743            phaseName = self.PatternTree.GetItemText(item)
1744            data = self.PatternTree.GetItemPyData(item)
1745            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1746            if iph not in result: continue
1747            generalData = data['General']
1748            SGData = generalData['SGData']
1749            UseList = data['Histograms']
1750            NShkl = len(G2spc.MustrainNames(SGData))
1751            NDij = len(G2spc.HStrainNames(SGData))
1752            UseList[HistName] = SetDefaultDData('PWDR',HistName,NShkl=NShkl,NDij=NDij)
1753            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1754            refList = self.PatternTree.GetItemPyData(
1755                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1756            refList[generalData['Name']] = []
1757        return # success
1758       
1759    def OnPreferences(self,event):
1760        'Edit the GSAS-II configuration variables'
1761        dlg = G2G.SelectConfigSetting(self)
1762        dlg.ShowModal() == wx.ID_OK
1763        dlg.Destroy()
1764
1765    def _Add_ImportMenu_smallangle(self,parent):
1766        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1767        '''
1768        submenu = wx.Menu()
1769        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1770            submenu, help='Import small angle data')
1771        for reader in self.ImportSmallAngleReaderlist:
1772            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1773                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1774            self.ImportMenuId[item.GetId()] = reader
1775            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1776        # item = submenu.Append(wx.ID_ANY,
1777        #     help='Import small angle data, use file to try to determine format',
1778        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1779        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1780
1781    def OnImportSmallAngle(self,event):
1782        '''Called in response to an Import/Small Angle Data/... menu item
1783        to read a small angle diffraction data set.
1784        dict self.ImportMenuId is used to look up the specific
1785        reader item associated with the menu item, which will be
1786        None for the last menu item, which is the "guess" option
1787        where all appropriate formats will be tried.
1788
1789        '''
1790       
1791        def GetSASDIparm(reader):
1792            parm = reader.instdict
1793            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1794                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1795            return Iparm,{}
1796           
1797        # get a list of existing histograms
1798        SASDlist = []
1799        if self.PatternTree.GetCount():
1800            item, cookie = self.PatternTree.GetFirstChild(self.root)
1801            while item:
1802                name = self.PatternTree.GetItemText(item)
1803                if name.startswith('SASD ') and name not in SASDlist:
1804                    SASDlist.append(name)
1805                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1806        # look up which format was requested
1807        reqrdr = self.ImportMenuId.get(event.GetId()) 
1808        rdlist = self.OnImportGeneric(
1809            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1810        if len(rdlist) == 0: return
1811        self.CheckNotebook()
1812        Iparm = None
1813        lastdatafile = ''
1814        newHistList = []
1815        self.EnablePlot = False
1816        for rd in rdlist:
1817            lastdatafile = rd.smallangleentry[0]
1818            HistName = rd.idstring
1819            HistName = 'SASD '+HistName
1820            # make new histogram names unique
1821            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1822            print 'Read small angle data '+str(HistName)+ \
1823                ' from file '+str(self.lastimport)
1824            # data are read, now store them in the tree
1825            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1826            Iparm1,Iparm2 = GetSASDIparm(rd)
1827#            if 'T' in Iparm1['Type'][0]:
1828#                if not rd.clockWd and rd.GSAS:
1829#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1830#                cw = np.diff(rd.powderdata[0])
1831#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1832#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1833#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1834#                if 'Itype' in Iparm2:
1835#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1836#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1837#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1838#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1839#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1840#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1841#                    var += WYI*rd.powderdata[1]**2
1842#                    var /= YI**2
1843#                    rd.powderdata[2] = 1./var
1844#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1845#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1846#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1847            Tmin = min(rd.smallangledata[0])
1848            Tmax = max(rd.smallangledata[0])
1849            valuesdict = {
1850                'wtFactor':1.0,
1851                'Dummy':False,
1852                'ranId':ran.randint(0,sys.maxint),
1853                }
1854            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1855            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1856            self.PatternTree.SetItemPyData(
1857                self.PatternTree.AppendItem(Id,text='Comments'),
1858                rd.comments)
1859            self.PatternTree.SetItemPyData(
1860                self.PatternTree.AppendItem(Id,text='Limits'),
1861                [(Tmin,Tmax),[Tmin,Tmax]])
1862            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1863            self.PatternTree.SetItemPyData(
1864                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1865                [Iparm1,Iparm2])
1866            self.PatternTree.SetItemPyData(
1867                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1868            self.PatternTree.SetItemPyData(
1869                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1870                rd.Sample)
1871            self.PatternTree.SetItemPyData(
1872                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1873            newHistList.append(HistName)
1874        else:
1875            self.EnablePlot = True
1876            self.PatternTree.Expand(Id)
1877            self.PatternTree.SelectItem(Id)
1878           
1879        if not newHistList: return # somehow, no new histograms
1880        return # success
1881
1882    def OnMacroRecordStatus(self,event,setvalue=None):
1883        '''Called when the record macro menu item is used which toggles the
1884        value. Alternately a value to be set can be provided. Note that this
1885        routine is made more complex because on the Mac there are lots of menu
1886        items (listed in self.MacroStatusList) and this loops over all of them.
1887        '''
1888        nextvalue = log.ShowLogStatus() != True
1889        if setvalue is not None:
1890            nextvalue = setvalue
1891        if nextvalue:
1892            log.LogOn()
1893            set2 = True
1894        else:
1895            log.LogOff()
1896            set2 = False
1897        for menuitem in self.MacroStatusList:
1898            menuitem.Check(set2)
1899
1900    def _init_Macro(self):
1901        '''Define the items in the macro menu.
1902        '''
1903        menu = self.MacroMenu
1904        item = menu.Append(
1905                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
1906                kind=wx.ITEM_CHECK,text='Record actions')
1907        self.MacroStatusList.append(item)
1908        item.Check(log.ShowLogStatus())
1909        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
1910
1911        # this may only be of value for development work
1912        item = menu.Append(
1913            help='Show logged commands', id=wx.ID_ANY,
1914            kind=wx.ITEM_NORMAL,text='Show log')
1915        def OnShowLog(event):
1916            print 70*'='
1917            print 'List of logged actions'
1918            for i,line in enumerate(log.G2logList):
1919                if line: print i,line
1920            print 70*'='
1921        self.Bind(wx.EVT_MENU, OnShowLog, item)
1922
1923        item = menu.Append(
1924            help='Clear logged commands', id=wx.ID_ANY,
1925            kind=wx.ITEM_NORMAL,text='Clear log')
1926        def OnClearLog(event): log.G2logList=[None]
1927        self.Bind(wx.EVT_MENU, OnClearLog, item)
1928       
1929        item = menu.Append(
1930            help='Save logged commands to file', id=wx.ID_ANY,
1931            kind=wx.ITEM_NORMAL,text='Save log')
1932        def OnSaveLog(event):
1933            import cPickle
1934            defnam = os.path.splitext(
1935                os.path.split(self.GSASprojectfile)[1]
1936                )[0]+'.gcmd'
1937            dlg = wx.FileDialog(self,
1938                'Choose an file to save past actions', '.', defnam, 
1939                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1940                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1941            dlg.CenterOnParent()
1942            try:
1943                if dlg.ShowModal() == wx.ID_OK:
1944                    filename = dlg.GetPath()
1945                    # make sure extension is correct
1946                    filename = os.path.splitext(filename)[0]+'.gcmd'
1947                else:
1948                    filename = None
1949            finally:
1950                dlg.Destroy()
1951            if filename:
1952                fp = open(filename,'wb')
1953                fp.write(str(len(log.G2logList)-1)+'\n')
1954                for item in log.G2logList:
1955                    if item: cPickle.dump(item,fp)
1956                fp.close()
1957        self.Bind(wx.EVT_MENU, OnSaveLog, item)
1958
1959        item = menu.Append(
1960            help='Load logged commands from file', id=wx.ID_ANY,
1961            kind=wx.ITEM_NORMAL,text='Load log')
1962        def OnLoadLog(event):
1963            # this appends. Perhaps we should ask to clear?
1964            import cPickle
1965            defnam = os.path.splitext(
1966                os.path.split(self.GSASprojectfile)[1]
1967                )[0]+'.gcmd'
1968            dlg = wx.FileDialog(self,
1969                'Choose an file to read saved actions', '.', defnam, 
1970                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1971                wx.OPEN)
1972            dlg.CenterOnParent()
1973            try:
1974                if dlg.ShowModal() == wx.ID_OK:
1975                    filename = dlg.GetPath()
1976                    # make sure extension is correct
1977                    filename = os.path.splitext(filename)[0]+'.gcmd'
1978                else:
1979                    filename = None
1980            finally:
1981                dlg.Destroy()
1982            if filename and os.path.exists(filename):
1983                fp = open(filename,'rb')
1984                lines = fp.readline()
1985                for i in range(int(lines)):
1986                    log.G2logList.append(cPickle.load(fp))
1987                fp.close()
1988        self.Bind(wx.EVT_MENU, OnLoadLog, item)
1989
1990        item = menu.Append(
1991            help='Replay saved commands', id=wx.ID_ANY,
1992            kind=wx.ITEM_NORMAL,text='Replay log')
1993        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
1994
1995    def _init_Exports(self,menu):
1996        '''Find exporter routines and add them into menus
1997        '''
1998        # set up the top-level menus
1999        projectmenu = wx.Menu()
2000        item = menu.AppendMenu(
2001            wx.ID_ANY, 'Entire project as',
2002            projectmenu, help='Export entire project')
2003
2004        phasemenu = wx.Menu()
2005        item = menu.AppendMenu(
2006            wx.ID_ANY, 'Phase as',
2007            phasemenu, help='Export phase or sometimes phases')
2008
2009        powdermenu = wx.Menu()
2010        item = menu.AppendMenu(
2011            wx.ID_ANY, 'Powder data as',
2012            powdermenu, help='Export powder diffraction histogram(s)')
2013
2014        singlemenu = wx.Menu()
2015        item = menu.AppendMenu(
2016            wx.ID_ANY, 'Single crystal data as',
2017            singlemenu, help='Export single crystal histogram(s)')
2018
2019        imagemenu = wx.Menu()
2020        item = menu.AppendMenu(
2021            wx.ID_ANY, 'Image data as',
2022            imagemenu, help='Export powder image(s) data')
2023
2024        mapmenu = wx.Menu()
2025        item = menu.AppendMenu(
2026            wx.ID_ANY, 'Maps as',
2027            mapmenu, help='Export density map(s)')
2028
2029        # pdfmenu = wx.Menu()
2030        # item = menu.AppendMenu(
2031        #     wx.ID_ANY, 'PDFs as',
2032        #     pdfmenu, help='Export pair distribution function(s)')
2033
2034        # find all the exporter files
2035        pathlist = sys.path
2036        filelist = []
2037        for path in pathlist:
2038            for filename in glob.iglob(os.path.join(path,"G2export*.py")):
2039                filelist.append(filename)   
2040        filelist = sorted(list(set(filelist))) # remove duplicates
2041        self.exporterlist = []
2042        # go through the routines and import them, saving objects that
2043        # have export routines (method Exporter)
2044        for filename in filelist:
2045            path,rootname = os.path.split(filename)
2046            pkg = os.path.splitext(rootname)[0]
2047            try:
2048                fp = None
2049                fp, fppath,desc = imp.find_module(pkg,[path,])
2050                pkg = imp.load_module(pkg,fp,fppath,desc)
2051                for clss in inspect.getmembers(pkg): # find classes defined in package
2052                    if clss[0].startswith('_'): continue
2053                    if inspect.isclass(clss[1]):
2054                        # check if we have the required methods
2055                        for m in 'Exporter','loadParmDict':
2056                            if not hasattr(clss[1],m): break
2057                            if not callable(getattr(clss[1],m)): break
2058                        else:
2059                            exporter = clss[1](self) # create an export instance
2060                            self.exporterlist.append(exporter)
2061            except AttributeError:
2062                print 'Import_'+errprefix+': Attribute Error'+str(filename)
2063                pass
2064            except ImportError:
2065                print 'Import_'+errprefix+': Error importing file'+str(filename)
2066                pass
2067            if fp: fp.close()
2068        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
2069        for obj in self.exporterlist:
2070            #print 'exporter',obj
2071            for typ in obj.exporttype:
2072                if typ == "project":
2073                    submenu = projectmenu
2074                elif typ == "phase":
2075                    submenu = phasemenu
2076                elif typ == "powder":
2077                    submenu = powdermenu
2078                elif typ == "single":
2079                    submenu = singlemenu
2080                elif typ == "image":
2081                    submenu = imagemenu
2082                elif typ == "map":
2083                    submenu = mapmenu
2084                # elif typ == "pdf":
2085                #     submenu = pdfmenu
2086                else:
2087                    print("Error, unknown type in "+str(obj))
2088                    break
2089                item = submenu.Append(
2090                    wx.ID_ANY,
2091                    help=obj.longFormatName,
2092                    kind=wx.ITEM_NORMAL,
2093                    text=obj.formatName)
2094                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
2095                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
2096        item = imagemenu.Append(wx.ID_ANY,
2097                        help='Export image controls and masks for multiple images',
2098                        kind=wx.ITEM_NORMAL,
2099                        text='Multiple image controls and masks')
2100        self.Bind(wx.EVT_MENU, self.OnSaveMultipleImg, id=item.GetId())
2101        #code to debug an Exporter. hard-code the routine below, to allow a reload before use
2102        # def DebugExport(event):
2103        #      print 'start reload'
2104        #      reload(G2IO)
2105        #      import G2export_pwdr as dev
2106        #      reload(dev)
2107        #      dev.ExportPowderFXYE(self).Exporter(event)
2108        # item = menu.Append(
2109        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2110        #     help="debug exporter",text="test Export FXYE")
2111        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
2112        # # #self.ExportLookup[item.GetId()] = 'image'
2113        # self.ExportLookup[item.GetId()] = 'powder'
2114
2115    def _Add_ExportMenuItems(self,parent):
2116        # item = parent.Append(
2117        #     help='Select PWDR item to enable',id=wx.ID_ANY,
2118        #     kind=wx.ITEM_NORMAL,
2119        #     text='Export Powder Patterns...')
2120        # self.ExportPattern.append(item)
2121        # item.Enable(False)
2122        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
2123
2124        item = parent.Append(
2125            help='',id=wx.ID_ANY,
2126            kind=wx.ITEM_NORMAL,
2127            text='Export All Peak Lists...')
2128        self.ExportPeakList.append(item)
2129        item.Enable(True)
2130        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
2131
2132        item = parent.Append(
2133            help='',id=wx.ID_ANY,
2134            kind=wx.ITEM_NORMAL,
2135            text='Export HKLs...')
2136        self.ExportHKL.append(item)
2137        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
2138
2139        item = parent.Append(
2140            help='Select PDF item to enable',
2141            id=wx.ID_ANY,
2142            kind=wx.ITEM_NORMAL,
2143            text='Export PDF...')
2144        self.ExportPDF.append(item)
2145        item.Enable(False)
2146        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
2147
2148    def FillMainMenu(self,menubar):
2149        '''Define contents of the main GSAS-II menu for the (main) data tree window.
2150        For the mac, this is also called for the data item windows as well so that
2151        the main menu items are data menu as well.
2152        '''
2153        File = wx.Menu(title='')
2154        menubar.Append(menu=File, title='&File')
2155        self._Add_FileMenuItems(File)
2156        Data = wx.Menu(title='')
2157        menubar.Append(menu=Data, title='Data')
2158        self._Add_DataMenuItems(Data)
2159        Calculate = wx.Menu(title='')       
2160        menubar.Append(menu=Calculate, title='&Calculate')
2161        self._Add_CalculateMenuItems(Calculate)
2162        Import = wx.Menu(title='')       
2163        menubar.Append(menu=Import, title='Import')
2164        self._Add_ImportMenu_Image(Import)
2165        self._Add_ImportMenu_Phase(Import)
2166        self._Add_ImportMenu_powder(Import)
2167        self._Add_ImportMenu_Sfact(Import)
2168        self._Add_ImportMenu_smallangle(Import)
2169
2170        #======================================================================
2171        # Code to help develop/debug an importer, much is hard-coded below
2172        # but module is reloaded before each use, allowing faster testing
2173        # def DebugImport(event):
2174        #     print 'start reload'
2175        #     import G2phase_ISO as dev
2176        #     reload(dev)
2177        #     rd = dev.ISODISTORTPhaseReader()
2178        #     self.ImportMenuId[event.GetId()] = rd
2179        #     self.OnImportPhase(event)
2180            # or ----------------------------------------------------------------------
2181            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
2182            # special debug code
2183            # or ----------------------------------------------------------------------
2184            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
2185            # fp = open(filename,'Ur')
2186            # if not rd.ContentsValidator(fp):
2187            #     print 'not validated'
2188            #     # make a list of used phase ranId's
2189            # phaseRIdList = []
2190            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2191            # if sub:
2192            #     item, cookie = self.PatternTree.GetFirstChild(sub)
2193            #     while item:
2194            #         phaseName = self.PatternTree.GetItemText(item)
2195            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
2196            #         if ranId: phaseRIdList.append(ranId)
2197            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2198            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
2199            #     print 'read OK'
2200        # item = Import.Append(
2201        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
2202        #     help="debug importer",text="test importer")
2203        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
2204        #======================================================================
2205        self.ExportMenu = wx.Menu(title='')
2206        menubar.Append(menu=self.ExportMenu, title='Export')
2207        self._init_Exports(self.ExportMenu)
2208        self._Add_ExportMenuItems(self.ExportMenu)
2209        if GSASIIpath.GetConfigValue('Enable_logging'):
2210            self.MacroMenu = wx.Menu(title='')
2211            menubar.Append(menu=self.MacroMenu, title='Macro')
2212            self._init_Macro()
2213        HelpMenu=G2G.MyHelp(self,helpType='Data tree',
2214            morehelpitems=[
2215                           ('&Tutorials','Tutorials'), 
2216                           ])
2217        menubar.Append(menu=HelpMenu,title='&Help')
2218           
2219    def _init_ctrls(self, parent):
2220        wx.Frame.__init__(self, name='GSASII', parent=parent,
2221            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
2222        clientSize = wx.ClientDisplayRect()
2223        Size = self.GetSize()
2224        xPos = clientSize[2]-Size[0]
2225        self.SetPosition(wx.Point(xPos,clientSize[1]))
2226        self._init_Imports()
2227        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
2228        self.MakePDF = []
2229        self.Refine = []
2230        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
2231        #self.ExportPattern = []
2232        self.ExportPeakList = []
2233        self.ExportHKL = []
2234        self.ExportPDF = []
2235        self.ExportPhase = []
2236        self.ExportCIF = []
2237        #
2238        self.GSASIIMenu = wx.MenuBar()
2239        # create a list of all dataframe menus (appended in PrefillDataMenu)
2240        self.dataMenuBars = [self.GSASIIMenu]
2241        self.MacroStatusList = []
2242        self.FillMainMenu(self.GSASIIMenu)
2243        self.SetMenuBar(self.GSASIIMenu)
2244        self.Bind(wx.EVT_SIZE, self.OnSize)
2245        self.Status = self.CreateStatusBar()
2246        self.mainPanel = wx.Panel(self,-1)
2247       
2248        wxID_PATTERNTREE = wx.NewId()
2249        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
2250        self.PatternTree = G2G.G2TreeCtrl(id=wxID_PATTERNTREE,
2251            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
2252        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnPatternTreeSelChanged)
2253        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
2254            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
2255        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
2256            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
2257        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
2258            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
2259        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
2260            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
2261        self.PatternTree.Bind(wx.EVT_TREE_BEGIN_RDRAG,
2262            self.OnPatternTreeBeginRDrag, id=wxID_PATTERNTREE)       
2263        self.PatternTree.Bind(wx.EVT_TREE_END_DRAG,
2264            self.OnPatternTreeEndDrag, id=wxID_PATTERNTREE)       
2265        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
2266        self.root = self.PatternTree.root
2267        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
2268            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2269        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
2270        plotFrame.Show()
2271       
2272        self.dataDisplay = None
2273       
2274    def __init__(self, parent):
2275        self.ExportLookup = {}
2276        self._init_ctrls(parent)
2277        self.Image = wx.Image(
2278            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
2279            wx.BITMAP_TYPE_ICO)
2280        if "wxMSW" in wx.PlatformInfo:
2281            img = self.Image.Scale(16, 16).ConvertToBitmap()
2282        elif "wxGTK" in wx.PlatformInfo:
2283            img = self.Image.Scale(22, 22).ConvertToBitmap()
2284        else:
2285            img = self.Image.ConvertToBitmap()
2286        self.SetIcon(wx.IconFromBitmap(img))
2287        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2288        # various defaults
2289        self.oldFocus = None
2290        self.GSASprojectfile = ''
2291        self.undofile = ''
2292        self.TreeItemDelete = False
2293        self.plotStyle = {'qPlot':False,'dPlot':False,'sqrtPlot':False}
2294        self.Weight = False
2295        self.IfPlot = False
2296        self.DDShowAll = False
2297        self.atmSel = ''
2298        self.PatternId = 0
2299        self.PickId = 0
2300        self.PickIdText = None
2301        self.PeakTable = []
2302        self.LimitsTable = []
2303        self.ifX20 = True   #use M20 /= (1+X20) in powder indexing, etc.
2304        self.HKL = []
2305        self.Lines = []
2306        self.itemPicked = None
2307        self.dataFrame = None
2308        self.Interpolate = 'nearest'
2309        self.ContourColor = 'Paired'
2310        self.VcovColor = 'RdYlGn'
2311        self.RamaColor = 'Blues'
2312        self.Projection = 'equal area'
2313        self.logPlot = False
2314        self.plusPlot = True
2315        self.sqPlot = False
2316        self.ErrorBars = False
2317        self.Contour = False
2318        self.Legend = False
2319        self.SinglePlot = True
2320        self.SubBack = False
2321        self.seqReverse = False
2322        self.seqLines = True #draw lines between points
2323        self.plotView = 0
2324        self.Image = 0
2325        self.oldImagefile = '' # the name of the last image file read
2326        self.oldImageTag = None # the name of the tag for multi-image files
2327        self.ImageZ = []  # this contains the image plotted
2328        # self.ImageZ and self.oldImagefile are set in GSASIIplot.PlotImage
2329        # and GSASIIIO.GetImageData
2330        # any changes to self.ImageZ should initialize self.oldImagefile to force a reread
2331        self.Integrate = 0
2332        self.imageDefault = {}
2333        self.IntgOutList = [] # list of integration tree item Ids created in G2IO.SaveIntegration
2334        self.AutointPWDRnames = [] # list of autoint created PWDR tree item names (to be deleted on a reset)
2335        self.autoIntFrame = None
2336        self.IntegratedList = [] # list of already integrated IMG tree items
2337        self.Sngl = False
2338        self.ifGetRing = False
2339        self.MaskKey = ''           #trigger for making image masks
2340        self.StrainKey = ''         #ditto for new strain d-zeros
2341        self.EnablePlot = True
2342        self.hist = ''              # selected histogram in Phase/Data tab
2343        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
2344        self.TutorialImportDir = None  # location to read tutorial files, set when a tutorial is viewed
2345        self.LastImportDir = None # last-used directory where an import was done
2346        self.LastGPXdir = None    # directory where a GPX file was last read
2347        self.LastExportDir = None  # the last directory used for exports, if any.
2348       
2349        arg = sys.argv
2350        if len(arg) > 1 and arg[1]:
2351            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2352            self.dirname = os.path.dirname(arg[1])
2353            if self.dirname: os.chdir(self.dirname)
2354            try:
2355                self.StartProject()         #open the file if possible
2356                return
2357            except Exception:
2358                print 'Error opening or reading file',arg[1]
2359                import traceback
2360                print traceback.format_exc()
2361               
2362        if GSASIIpath.GetConfigValue('Starting_directory'):
2363            try:
2364                pth = GSASIIpath.GetConfigValue('Starting_directory')
2365                pth = os.path.expanduser(pth) 
2366                os.chdir(pth)
2367                self.LastGPXdir = pth
2368            except:
2369                print('Ignoring Config Starting_directory value: '+
2370                      GSASIIpath.GetConfigValue('Starting_directory'))
2371
2372    def GetTreeItemsList(self,item):
2373        return self.PatternTree._getTreeItemsList(item)
2374
2375    def OnSize(self,event):
2376        'Called to make PatternTree fill mainPanel'
2377        w,h = self.GetClientSizeTuple()
2378        self.mainPanel.SetSize(wx.Size(w,h))
2379        self.PatternTree.SetSize(wx.Size(w,h))
2380                       
2381    def OnPatternTreeSelChanged(self, event):
2382        '''Called when a data tree item is selected'''
2383        if self.TreeItemDelete:
2384            self.TreeItemDelete = False
2385        else:
2386            pltNum = self.G2plotNB.nb.GetSelection()
2387            if pltNum >= 0:                         #to avoid the startup with no plot!
2388                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2389                pltPlot = pltPage.figure
2390            item = event.GetItem()
2391            G2gd.MovePatternTreeToGrid(self,item)
2392            if self.oldFocus:
2393                self.oldFocus.SetFocus()
2394       
2395    def OnPatternTreeItemCollapsed(self, event):
2396        'Called when a tree item is collapsed - all children will be collapsed'
2397        self.PatternTree.CollapseAllChildren(event.GetItem())
2398
2399    def OnPatternTreeItemExpanded(self, event):
2400        'Called when a tree item is expanded'
2401        self.OnPatternTreeSelChanged(event)
2402        event.Skip()
2403       
2404    def OnPatternTreeItemDelete(self, event):
2405        'Called when a tree item is deleted -- not sure what this does'
2406        self.TreeItemDelete = True
2407
2408    def OnPatternTreeItemActivated(self, event):
2409        'Called when a tree item is activated'
2410        event.Skip()
2411       
2412    def OnPatternTreeBeginRDrag(self,event):
2413        event.Allow()
2414        self.BeginDragId = event.GetItem()
2415        self.ParentId = self.PatternTree.GetItemParent(self.BeginDragId)
2416        DragText = self.PatternTree.GetItemText(self.BeginDragId)
2417        self.DragData = [[DragText,self.PatternTree.GetItemPyData(self.BeginDragId)],]
2418        item, cookie = self.PatternTree.GetFirstChild(self.BeginDragId)
2419        while item:     #G2 data tree has no sub children under a child of a tree item
2420            name = self.PatternTree.GetItemText(item)
2421            self.DragData.append([name,self.PatternTree.GetItemPyData(item)])
2422            item, cookie = self.PatternTree.GetNextChild(self.BeginDragId, cookie)                           
2423       
2424    def OnPatternTreeEndDrag(self,event):
2425        event.Allow()
2426        self.EndDragId = event.GetItem()
2427        try:
2428            NewParent = self.PatternTree.GetItemParent(self.EndDragId)
2429        except:
2430            self.EndDragId = self.PatternTree.GetLastChild(self.root)
2431            NewParent = self.root
2432        if self.ParentId != NewParent:
2433            self.ErrorDialog('Drag not allowed','Wrong parent for item dragged')
2434        else:
2435            Name,Item = self.DragData[0]
2436            NewId = self.PatternTree.InsertItem(self.ParentId,self.EndDragId,Name,data=None)
2437            self.PatternTree.SetItemPyData(NewId,Item)
2438            for name,item in self.DragData[1:]:     #loop over children
2439                Id = self.PatternTree.AppendItem(parent=NewId,text=name)
2440                self.PatternTree.SetItemPyData(Id,item)
2441            self.PatternTree.Delete(self.BeginDragId)
2442            G2gd.MovePatternTreeToGrid(self,NewId)
2443       
2444    def OnPatternTreeKeyDown(self,event): #doesn't exactly work right with Shift key down
2445        'Allows stepping through the tree with the up/down arrow keys'
2446        self.oldFocus = wx.Window.FindFocus()
2447        keyevt = event.GetKeyEvent()
2448        key = event.GetKeyCode()
2449        item = self.PatternTree.GetSelection()
2450        if type(item) is int: return # is this the toplevel in tree?
2451        name = self.PatternTree.GetItemText(item)
2452        parent = self.PatternTree.GetItemParent(item)
2453        if key == wx.WXK_UP:
2454            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2455                if type(parent) is int: return # is this the toplevel in tree?
2456                prev = self.PatternTree.GetPrevSibling(parent)
2457                NewId = G2gd.GetPatternTreeItemId(self,prev,name)
2458                if NewId:
2459                    self.PatternTree.Collapse(parent)
2460                    self.PatternTree.Expand(prev)
2461                    self.oldFocus = wx.Window.FindFocus()
2462                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2463                else:
2464                    wx.CallAfter(self.PatternTree.SelectItem,item)
2465            else:   
2466                self.PatternTree.GetPrevSibling(item)
2467                self.PatternTree.SelectItem(item)
2468        elif key == wx.WXK_DOWN:
2469            if keyevt.GetModifiers() == wx.MOD_SHIFT and parent != self.root:
2470                next = self.PatternTree.GetNextSibling(parent)
2471                NewId = G2gd.GetPatternTreeItemId(self,next,name)
2472                if NewId:
2473                    self.PatternTree.Collapse(parent)
2474                    self.PatternTree.Expand(next)
2475                    self.oldFocus = wx.Window.FindFocus()
2476                    wx.CallAfter(self.PatternTree.SelectItem,NewId)
2477                else:
2478                    wx.CallAfter(self.PatternTree.SelectItem,item)
2479            else:   
2480                self.PatternTree.GetNextSibling(item)
2481                self.PatternTree.SelectItem(item)
2482               
2483    def OnReadPowderPeaks(self,event):
2484        'Bound to menu Data/Read Powder Peaks'
2485        Cuka = 1.54052
2486        self.CheckNotebook()
2487        pth = G2G.GetImportPath(self)
2488        if not pth: pth = '.'
2489        dlg = wx.FileDialog(self, 'Choose file with peak list', pth, '', 
2490            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN)
2491        try:
2492            if dlg.ShowModal() == wx.ID_OK:
2493                self.HKL = []
2494                self.powderfile = dlg.GetPath()
2495                comments,peaks,limits,wave = G2IO.GetPowderPeaks(self.powderfile)
2496                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2497                data = ['PKS',wave,0.0]
2498                names = ['Type','Lam','Zero'] 
2499                codes = [0,0,0]
2500                inst = [G2IO.makeInstDict(names,data,codes),{}]
2501                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2502                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2503                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(limits),limits])
2504                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2505                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2506                self.PatternTree.Expand(Id)
2507                self.PatternTree.SelectItem(Id)
2508                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2509        finally:
2510            dlg.Destroy()
2511                       
2512    def OnImageRead(self,event):
2513        '''Called to read in an image in any known format. *** Depreciated. ***
2514        Note: When removed, G2IO.ReadLoadImage can also be removed
2515        '''
2516        G2G.G2MessageBox(self,'Please use the Import/Image/... menu item rather than this','depreciating menu item')
2517        self.CheckNotebook()
2518        dlg = wx.FileDialog(
2519            self, 'Choose image files', '.', '',
2520            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png)|'
2521            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.stl;*.G2img;*.png;*.zip|'
2522            'European detector file (*.edf)|*.edf|'
2523            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2524            'MAR file (*.mar*)|*.mar*|'
2525            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2526            'ADSC Image (*.img)|*.img|'
2527            'Rigaku R-Axis IV (*.stl)|*.stl|'
2528            'GSAS-II Image (*.G2img)|*.G2img|'
2529            'Portable Network Graphics image (*.png)|*.png|'
2530            'Zip archive (*.zip)|*.zip|'
2531            'All files (*.*)|*.*',
2532            wx.OPEN | wx.MULTIPLE)
2533        try:
2534            if dlg.ShowModal() == wx.ID_OK:
2535                imagefiles = dlg.GetPaths()
2536                imagefiles.sort()
2537                for imagefile in imagefiles:
2538                    G2IO.ReadLoadImage(imagefile,self)
2539                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2540                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,self.Image,'Image Controls'))             #show last image to be read
2541        finally:
2542            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2543            os.chdir(path)
2544            dlg.Destroy()
2545
2546    def CheckNotebook(self):
2547        '''Make sure the data tree has the minimally expected controls.
2548        '''
2549        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2550            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2551            self.PatternTree.SetItemPyData(sub,[''])
2552        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2553            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2554            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2555        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2556            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2557            self.PatternTree.SetItemPyData(sub,{})
2558        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2559            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2560            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2561        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2562            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2563            self.PatternTree.SetItemPyData(sub,{})
2564        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2565            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2566            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2567                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2568               
2569    class CopyDialog(wx.Dialog):
2570        '''Creates a dialog for copying control settings between
2571        data tree items'''
2572        def __init__(self,parent,title,text,data):
2573            wx.Dialog.__init__(self,parent,-1,title, 
2574                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2575            self.data = data
2576            panel = wx.Panel(self)
2577            mainSizer = wx.BoxSizer(wx.VERTICAL)
2578            topLabl = wx.StaticText(panel,-1,text)
2579            mainSizer.Add((10,10),1)
2580            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2581            mainSizer.Add((10,10),1)
2582            ncols = len(data)/40+1
2583            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2584            for id,item in enumerate(self.data):
2585                ckbox = wx.CheckBox(panel,id,item[1])
2586                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2587                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2588            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2589            OkBtn = wx.Button(panel,-1,"Ok")
2590            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2591            cancelBtn = wx.Button(panel,-1,"Cancel")
2592            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2593            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2594            btnSizer.Add((20,20),1)
2595            btnSizer.Add(OkBtn)
2596            btnSizer.Add((20,20),1)
2597            btnSizer.Add(cancelBtn)
2598            btnSizer.Add((20,20),1)
2599           
2600            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2601            panel.SetSizer(mainSizer)
2602            panel.Fit()
2603            self.Fit()
2604       
2605        def OnCopyChange(self,event):
2606            id = event.GetId()
2607            self.data[id][0] = self.FindWindowById(id).GetValue()       
2608           
2609        def OnOk(self,event):
2610            parent = self.GetParent()
2611            parent.Raise()
2612            self.EndModal(wx.ID_OK)             
2613           
2614        def OnCancel(self,event):
2615            parent = self.GetParent()
2616            parent.Raise()
2617            self.EndModal(wx.ID_CANCEL)             
2618           
2619        def GetData(self):
2620            return self.data
2621       
2622    class SumDialog(wx.Dialog):
2623        'Allows user to supply scale factor(s) when summing data'
2624        def __init__(self,parent,title,text,dataType,data):
2625            wx.Dialog.__init__(self,parent,-1,title, 
2626                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2627            self.data = data
2628            panel = wx.Panel(self)
2629            mainSizer = wx.BoxSizer(wx.VERTICAL)
2630            topLabl = wx.StaticText(panel,-1,text)
2631            mainSizer.Add((10,10),1)
2632            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2633            mainSizer.Add((10,10),1)
2634            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2635            for id,item in enumerate(self.data[:-1]):
2636                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2637                name.SetEditable(False)
2638                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2639                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2640                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2641                dataGridSizer.Add(scale,0,wx.LEFT,10)
2642                dataGridSizer.Add(name,0,wx.RIGHT,10)
2643            if dataType:
2644                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2645                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2646                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2647                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2648                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2649                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2650            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2651            OkBtn = wx.Button(panel,-1,"Ok")
2652            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2653            cancelBtn = wx.Button(panel,-1,"Cancel")
2654            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2655            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2656            btnSizer.Add((20,20),1)
2657            btnSizer.Add(OkBtn)
2658            btnSizer.Add((20,20),1)
2659            btnSizer.Add(cancelBtn)
2660            btnSizer.Add((20,20),1)
2661           
2662            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2663            panel.SetSizer(mainSizer)
2664            panel.Fit()
2665            self.Fit()
2666
2667        def OnScaleChange(self,event):
2668            id = event.GetId()
2669            value = self.FindWindowById(id).GetValue()
2670            try:
2671                self.data[id][0] = float(value)
2672                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2673            except ValueError:
2674                if value and '-' not in value[0]:
2675                    print 'bad input - numbers only'
2676                    self.FindWindowById(id).SetValue('0.000')
2677           
2678        def OnNameChange(self,event):
2679            self.data[-1] = self.name.GetValue() 
2680           
2681        def OnOk(self,event):
2682            parent = self.GetParent()
2683            parent.Raise()
2684            self.EndModal(wx.ID_OK)             
2685           
2686        def OnCancel(self,event):
2687            parent = self.GetParent()
2688            parent.Raise()
2689            self.EndModal(wx.ID_CANCEL)             
2690           
2691        def GetData(self):
2692            return self.data
2693                       
2694    def OnPwdrSum(self,event):
2695        'Sum together powder data(?)'
2696        TextList = []
2697        DataList = []
2698        SumList = []
2699        Names = []
2700        Inst = None
2701        SumItemList = []
2702        Comments = ['Sum equals: \n']
2703        if self.PatternTree.GetCount():
2704            item, cookie = self.PatternTree.GetFirstChild(self.root)
2705            while item:
2706                name = self.PatternTree.GetItemText(item)
2707                Names.append(name)
2708                if 'PWDR' in name:
2709                    TextList.append([0.0,name])
2710                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2711                    if not Inst:
2712                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2713                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2714            if len(TextList) < 2:
2715                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2716                return
2717            TextList.append('default_sum_name')               
2718            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2719            try:
2720                if dlg.ShowModal() == wx.ID_OK:
2721                    lenX = 0
2722                    Xminmax = [0,0]
2723                    Xsum = []
2724                    Ysum = []
2725                    Vsum = []
2726                    result = dlg.GetData()
2727                    for i,item in enumerate(result[:-1]):
2728                        scale,name = item
2729                        data = DataList[i]
2730                        if scale:
2731                            Comments.append("%10.3f %s" % (scale,' * '+name))
2732                            x,y,w,yc,yb,yd = data   #numpy arrays!
2733                            v = 1./w
2734                            if lenX:
2735                                if lenX != len(x):
2736                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2737                                        '\nExpected:'+str(lenX)+ \
2738                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2739                                    return
2740                            else:
2741                                lenX = len(x)
2742                            if Xminmax[1]:
2743                                if Xminmax != [x[0],x[-1]]:
2744                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2745                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2746                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2747                                    return
2748                                else:
2749                                    for j,yi in enumerate(y):
2750                                         Ysum[j] += scale*yi
2751                                         Vsum[j] += abs(scale)*v[j]
2752                            else:
2753                                Xminmax = [x[0],x[-1]]
2754                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2755                                for j,yi in enumerate(y):
2756                                    Xsum.append(x[j])
2757                                    Ysum.append(scale*yi)
2758                                    Vsum.append(abs(scale*v[j]))
2759                    Wsum = 1./np.array(Vsum)
2760                    outname = 'PWDR '+result[-1]
2761                    Id = 0
2762                    if outname in Names:
2763                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2764                        try:
2765                            if dlg2.ShowModal() == wx.ID_OK:
2766                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2767                                self.PatternTree.Delete(Id)
2768                        finally:
2769                            dlg2.Destroy()
2770                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2771                    if Id:
2772                        Sample = G2pdG.SetDefaultSample()
2773                        valuesdict = {
2774                            'wtFactor':1.0,
2775                            'Dummy':False,
2776                            'ranId':ran.randint(0,sys.maxint),
2777                            'Offset':[0.0,0.0],'delOffset':0.02,'refOffset':-1.0,'refDelt':0.01,
2778                            'qPlot':False,'dPlot':False,'sqrtPlot':False
2779                            }
2780                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2781                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2782                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2783                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2784                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2785                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2786                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2787                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2788                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2789                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2790                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2791                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2792                        self.PatternTree.SelectItem(Id)
2793                        self.PatternTree.Expand(Id)
2794            finally:
2795                dlg.Destroy()
2796
2797    def OnImageSum(self,event):
2798        'Sum together image data(?)'
2799        TextList = []
2800        DataList = []
2801        SumList = []
2802        Names = []
2803        Inst = []
2804        SumItemList = []
2805        Comments = ['Sum equals: \n']
2806        if self.PatternTree.GetCount():
2807            item, cookie = self.PatternTree.GetFirstChild(self.root)
2808            while item:
2809                name = self.PatternTree.GetItemText(item)
2810                Names.append(name)
2811                if 'IMG' in name:
2812                    TextList.append([0.0,name])
2813                    DataList.append(self.PatternTree.GetImageLoc(item))        #Size,Image,Tag
2814                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2815                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2816            if len(TextList) < 2:
2817                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2818                return
2819            TextList.append('default_sum_name')               
2820            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2821            try:
2822                if dlg.ShowModal() == wx.ID_OK:
2823                    imSize = 0
2824                    result = dlg.GetData()
2825                    First = True
2826                    Found = False
2827                    for i,item in enumerate(result[:-1]):
2828                        scale,name = item
2829                        if scale:
2830                            Found = True                               
2831                            Comments.append("%10.3f %s" % (scale,' * '+name))
2832                            Npix,imagefile,imagetag = DataList[i]
2833                            image = G2IO.GetImageData(self,imagefile,imageOnly=True,ImageTag=imagetag)
2834                            if First:
2835                                newImage = np.zeros_like(image)
2836                                First = False
2837                            if imSize:
2838                                if imSize != Npix:
2839                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2840                                        '\nExpected:'+str(imSize)+ \
2841                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2842                                    return
2843                                newImage = newImage+scale*image
2844                            else:
2845                                imSize = Npix
2846                                newImage = newImage+scale*image
2847                            del(image)
2848                    if not Found:
2849                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2850                        return
2851                       
2852                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2853                    outname = 'IMG '+result[-1]
2854                    Id = 0
2855                    if outname in Names:
2856                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2857                        try:
2858                            if dlg2.ShowModal() == wx.ID_OK:
2859                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2860                        finally:
2861                            dlg2.Destroy()
2862                    else:
2863                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2864                    if Id:
2865                        pth = G2G.GetExportPath(self)
2866                        dlg = wx.FileDialog(self, 'Choose sum image filename', pth, '', 
2867                            'G2img files (*.G2img)|*.G2img', 
2868                            wx.SAVE|wx.FD_OVERWRITE_PROMPT)
2869                        if dlg.ShowModal() == wx.ID_OK:
2870                            newimagefile = dlg.GetPath()
2871                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2872                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2873                            Imax = np.amax(newImage)
2874                            Imin = np.amin(newImage)
2875                            newImage = []
2876                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2877                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2878                        del(newImage)
2879                        if self.imageDefault:
2880                            Data = copy.copy(self.imageDefault)
2881                        Data['showLines'] = True
2882                        Data['ring'] = []
2883                        Data['rings'] = []
2884                        Data['cutoff'] = 10
2885                        Data['pixLimit'] = 20
2886                        Data['ellipses'] = []
2887                        Data['calibrant'] = ''
2888                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2889                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2890                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2891                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2892                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2893                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2894                        self.PatternTree.SelectItem(Id)
2895                        self.PatternTree.Expand(Id)
2896                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2897                        self.Image = self.PickId
2898            finally:
2899                dlg.Destroy()
2900                     
2901    def OnAddPhase(self,event):
2902        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2903        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2904            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2905        else:
2906            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2907        PhaseName = ''
2908        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2909            style=wx.OK)
2910        if dlg.ShowModal() == wx.ID_OK:
2911            PhaseName = dlg.GetValue()
2912        dlg.Destroy()
2913        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2914        E,SGData = G2spc.SpcGroup('P 1')
2915        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2916       
2917    def OnDeletePhase(self,event):
2918        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2919        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2920        if self.dataFrame:
2921            self.dataFrame.Clear() 
2922        TextList = []
2923        DelList = []
2924        DelItemList = []
2925        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2926            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2927        else:
2928            return
2929        if sub:
2930            item, cookie = self.PatternTree.GetFirstChild(sub)
2931            while item:
2932                TextList.append(self.PatternTree.GetItemText(item))
2933                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2934            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2935            try:
2936                if dlg.ShowModal() == wx.ID_OK:
2937                    result = dlg.GetSelections()
2938                    for i in result: DelList.append([i,TextList[i]])
2939                    item, cookie = self.PatternTree.GetFirstChild(sub)
2940                    i = 0
2941                    while item:
2942                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2943                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2944                        i += 1
2945                    for item in DelItemList:
2946                        name = self.PatternTree.GetItemText(item)
2947                        self.PatternTree.Delete(item)
2948                        self.G2plotNB.Delete(name)
2949                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2950                    while item:
2951                        name = self.PatternTree.GetItemText(item)
2952                        if 'PWDR' in name:
2953                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2954                            refList = self.PatternTree.GetItemPyData(Id)
2955                            if len(refList):
2956                                for i,item in DelList:
2957                                    if item in refList:
2958                                        del(refList[item])
2959                            self.PatternTree.SetItemPyData(Id,refList)
2960                        elif 'HKLF' in name:
2961                            data = self.PatternTree.GetItemPyData(item)
2962                            data[0] = {}
2963                            self.PatternTree.SetItemPyData(item,data)
2964                           
2965                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2966            finally:
2967                dlg.Destroy()
2968               
2969    def OnRenameData(self,event):
2970        'Renames an existing phase. Called by Data/Rename Phase menu'
2971        name = self.PatternTree.GetItemText(self.PickId)     
2972        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2973            if 'Bank' in name:
2974                names = name.split('Bank')
2975                names[1] = ' Bank'+names[1]
2976            elif 'Azm' in name:
2977                names = name.split('Azm')
2978                names[1] = ' Azm'+names[1]
2979            else:
2980                names = [name,'']
2981            dataType = names[0][:names[0].index(' ')+1]                 #includes the ' '
2982            dlg = wx.TextEntryDialog(self,'Data name: '+name,'Change data name',
2983                defaultValue=names[0][names[0].index(' ')+1:])
2984            try:
2985                if dlg.ShowModal() == wx.ID_OK:
2986                    name = dataType+dlg.GetValue()+names[1]
2987                    self.PatternTree.SetItemText(self.PickId,name)
2988            finally:
2989                dlg.Destroy()
2990       
2991    def GetFileList(self,fileType,skip=None):        #potentially useful?
2992        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2993        fileList = []
2994        Source = ''
2995        id, cookie = self.PatternTree.GetFirstChild(self.root)
2996        while id:
2997            name = self.PatternTree.GetItemText(id)
2998            if fileType in name:
2999                if id == skip:
3000                    Source = name
3001                else:
3002                    fileList.append([False,name,id])
3003            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3004        if skip:
3005            return fileList,Source
3006        else:
3007            return fileList
3008           
3009    def OnDataDelete(self, event):
3010        '''Delete one or more histograms from data tree. Called by the
3011        Data/DeleteData menu
3012        '''
3013        TextList = []
3014        DelList = []
3015        DelItemList = []
3016        nItems = {'PWDR':0,'SASD':0,'IMG':0,'HKLF':0,'PDF':0}
3017        ifPWDR = False
3018        ifSASD = False
3019        ifIMG = False
3020        ifHKLF = False
3021        ifPDF = False
3022        if self.PatternTree.GetCount():
3023            item, cookie = self.PatternTree.GetFirstChild(self.root)
3024            while item:
3025                name = self.PatternTree.GetItemText(item)
3026                if name not in ['Notebook','Controls','Covariance','Constraints',
3027                    'Restraints','Phases','Rigid bodies','Sequential results']:
3028                    if 'PWDR' in name: ifPWDR = True; nItems['PWDR'] += 1
3029                    if 'SASD' in name: ifSASD = True; nItems['SASD'] += 1
3030                    if 'IMG' in name: ifIMG = True; nItems['IMG'] += 1
3031                    if 'HKLF' in name: ifHKLF = True; nItems['HKLF'] += 1
3032                    if 'PDF' in name: ifPDF = True; nItems['PDF'] += 1
3033                    TextList.append(name)
3034                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3035            dlg = G2G.G2MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
3036            try:
3037                if dlg.ShowModal() == wx.ID_OK:
3038                    result = dlg.GetSelections()
3039                    for i in result: DelList.append(TextList[i])
3040                    item, cookie = self.PatternTree.GetFirstChild(self.root)
3041                    while item:
3042                        itemName = self.PatternTree.GetItemText(item)
3043                        if itemName in DelList:
3044                            if 'PWDR' in itemName: nItems['PWDR'] -= 1
3045                            elif 'SASD' in itemName: nItems['SASD'] -= 1
3046                            elif 'IMG' in itemName: nItems['IMG'] -= 1
3047                            elif 'HKLF' in itemName: nItems['HKLF'] -= 1
3048                            elif 'PDF' in itemName: nItems['PDF'] -= 1
3049                            DelItemList.append(item)
3050                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3051                    for item in DelItemList:
3052                        self.PatternTree.Delete(item)
3053                    self.PickId = 0
3054                    self.PickIdText = None
3055                    self.PatternId = 0
3056                    if nItems['PWDR']:
3057                        wx.CallAfter(G2plt.PlotPatterns,self,True)
3058                    else:
3059                        self.G2plotNB.Delete('Powder Patterns')
3060                    if not nItems['IMG']:
3061                        self.G2plotNB.Delete('2D Powder Image')
3062                    if not nItems['HKLF']:
3063                        self.G2plotNB.Delete('Structure Factors')
3064                        if '3D Structure Factors' in self.G2plotNB.plotList:
3065                            self.G2plotNB.Delete('3D Structure Factors')
3066            finally:
3067                dlg.Destroy()
3068
3069    def OnFileOpen(self, event, filename=None):
3070        '''Gets a GSAS-II .gpx project file in response to the
3071        File/Open Project menu button
3072        '''
3073        result = wx.ID_OK
3074        self.EnablePlot = False
3075        if self.PatternTree.GetChildrenCount(self.root,False):
3076            if self.dataFrame:
3077                self.dataFrame.Clear() 
3078            dlg = wx.MessageDialog(
3079                self,
3080                'Do you want to overwrite the current project? '+
3081                'Any unsaved changes in current project will be lost. Press OK to continue.',
3082                'Overwrite?',  wx.OK | wx.CANCEL)
3083            try:
3084                result = dlg.ShowModal()
3085                if result == wx.ID_OK:
3086                    self.PatternTree.DeleteChildren(self.root)
3087                    self.GSASprojectfile = ''
3088                    self.HKL = []
3089                    if self.G2plotNB.plotList:
3090                        self.G2plotNB.clear()
3091            finally:
3092                dlg.Destroy()
3093        if result != wx.ID_OK: return
3094
3095        if not filename:
3096            if self.dataDisplay: self.dataDisplay.Destroy()
3097            if self.LastGPXdir:
3098                pth = self.LastGPXdir
3099            else:
3100                pth = '.'
3101            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', pth, 
3102                wildcard='GSAS-II project file (*.gpx)|*.gpx',style=wx.OPEN)
3103            try:
3104                if dlg.ShowModal() != wx.ID_OK: return
3105                self.GSASprojectfile = dlg.GetPath()
3106                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3107                self.dirname = dlg.GetDirectory()
3108            finally:
3109                dlg.Destroy()
3110        else:
3111            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
3112            self.dirname = os.path.split(filename)[0]
3113
3114        try:
3115            self.StartProject()         #open the file if possible
3116        except:
3117            print '\nError opening file ',filename
3118            import traceback
3119            print traceback.format_exc()
3120       
3121    def StartProject(self):
3122        '''Opens a GSAS-II project file & selects the 1st available data set to
3123        display (PWDR, HKLF or SASD)
3124        '''
3125       
3126        Id = 0
3127        G2IO.ProjFileOpen(self)
3128        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3129        self.PatternTree.Expand(self.root)
3130        self.HKL = []
3131        item, cookie = self.PatternTree.GetFirstChild(self.root)
3132        while item and not Id:
3133            name = self.PatternTree.GetItemText(item)
3134            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
3135                Id = item
3136            elif name == 'Controls':
3137                data = self.PatternTree.GetItemPyData(item)
3138                if data:
3139                    for item in self.Refine: item.Enable(True)
3140                    self.EnableSeqRefineMenu()
3141            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3142        if Id:
3143            self.EnablePlot = True
3144            self.PatternTree.SelectItem(Id)
3145        self.CheckNotebook()
3146        if self.dirname: os.chdir(self.dirname)           # to get Mac/Linux to change directory!
3147        pth = os.path.split(os.path.abspath(self.GSASprojectfile))[0]
3148        if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
3149        self.LastGPXdir = pth
3150
3151    def OnFileClose(self, event):
3152        '''Clears the data tree in response to the
3153        File/New Project menu button. User is given option to save
3154        the project.
3155        '''
3156        if self.dataFrame:
3157            self.dataFrame.Clear()
3158            self.dataFrame.SetLabel('GSAS-II data display') 
3159        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
3160        try:
3161            result = dlg.ShowModal()
3162            if result == wx.ID_OK:
3163                self.OnFileSaveMenu(event)
3164            if result != wx.ID_CANCEL:
3165                self.GSASprojectfile = ''
3166                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
3167                self.PatternTree.DeleteChildren(self.root)
3168                if self.HKL: self.HKL = []
3169                if self.G2plotNB.plotList:
3170                    self.G2plotNB.clear()
3171        finally:
3172            dlg.Destroy()
3173
3174    def OnFileSave(self, event):
3175        '''Save the current project in response to the
3176        File/Save Project menu button
3177        '''
3178       
3179        if self.GSASprojectfile: 
3180            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
3181            self.CheckNotebook()
3182            G2IO.ProjFileSave(self)
3183        else:
3184            self.OnFileSaveas(event)
3185
3186    def OnFileSaveas(self, event):
3187        '''Save the current project in response to the
3188        File/Save as menu button
3189        '''
3190        if GSASIIpath.GetConfigValue('Starting_directory'):
3191            pth = GSASIIpath.GetConfigValue('Starting_directory')
3192            pth = os.path.expanduser(pth) 
3193        elif self.LastGPXdir:
3194            pth = self.LastGPXdir
3195        else:
3196            pth = '.'
3197        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', pth, '', 
3198            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3199        try:
3200            if dlg.ShowModal() == wx.ID_OK:
3201                self.GSASprojectfile = dlg.GetPath()
3202                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
3203                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
3204                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
3205                self.CheckNotebook()
3206                G2IO.ProjFileSave(self)
3207                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
3208        finally:
3209            dlg.Destroy()
3210
3211    def ExitMain(self, event):
3212        '''Called if the main window is closed'''
3213        if self.G2plotNB:
3214            self.G2plotNB.Destroy()
3215        if self.dataFrame:
3216            self.dataFrame.Clear() 
3217            self.dataFrame.Destroy()
3218        if self.undofile:
3219            os.remove(self.undofile)
3220        sys.exit()
3221       
3222    def OnFileExit(self, event):
3223        '''Called in response to the File/Quit menu button'''
3224        if self.G2plotNB:
3225            self.G2plotNB.Destroy()
3226        if self.dataFrame:
3227            self.dataFrame.Clear() 
3228            self.dataFrame.Destroy()
3229        self.Close()
3230       
3231    def OnExportPeakList(self,event):
3232        nptand = lambda x: np.tan(x*math.pi/180.)
3233        pth = G2G.GetExportPath(self)
3234        dlg = wx.FileDialog(self, 'Choose output peak list file name', pth, '', 
3235            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3236        try:
3237            if dlg.ShowModal() == wx.ID_OK:
3238                self.peaklistfile = dlg.GetPath()
3239                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3240                file = open(self.peaklistfile,'w')               
3241                item, cookie = self.PatternTree.GetFirstChild(self.root)
3242                while item:
3243                    name = self.PatternTree.GetItemText(item)
3244                    if 'PWDR' in name:
3245                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3246                        wave = 0.0
3247                        while item2:
3248                            name2 = self.PatternTree.GetItemText(item2)
3249                            if name2 == 'Instrument Parameters':
3250                                Inst = self.PatternTree.GetItemPyData(item2)[0]
3251                                Type = Inst['Type'][0]
3252                                if 'T' not in Type:
3253                                    wave = G2mth.getWave(Inst)
3254                            elif name2 == 'Peak List':
3255                                pkdata = self.PatternTree.GetItemPyData(item2)
3256                                peaks = pkdata['peaks']
3257                                sigDict = pkdata['sigDict']
3258                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3259                        file.write("#%s \n" % (name+' Peak List'))
3260                        if wave:
3261                            file.write('#wavelength = %10.6f\n'%(wave))
3262                        if 'T' in Type:
3263                            file.write('#%9s %10s %10s %12s %10s %10s %10s %10s %10s\n'%('pos','dsp','esd','int','alp','bet','sig','gam','FWHM'))                                   
3264                        else:
3265                            file.write('#%9s %10s %10s %12s %10s %10s %10s\n'%('pos','dsp','esd','int','sig','gam','FWHM'))
3266                        for ip,peak in enumerate(peaks):
3267                            dsp = G2lat.Pos2dsp(Inst,peak[0])
3268                            if 'T' in Type:  #TOF - more cols
3269                                esds = {'pos':0.,'int':0.,'alp':0.,'bet':0.,'sig':0.,'gam':0.}
3270                                for name in esds.keys():
3271                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3272                                sig = np.sqrt(peak[8])
3273                                gam = peak[10]
3274                                esddsp = G2lat.Pos2dsp(Inst,esds['pos'])
3275                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-TOF from Gam(peak)
3276                                file.write("%10.2f %10.5f %10.5f %12.2f %10.3f %10.3f %10.3f %10.3f %10.3f\n" % \
3277                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4])),peak[6],peak[8],peak[10],FWHM))
3278                            else:               #CW
3279                                #get esds from sigDict for each peak & put in output - esds for sig & gam from UVWXY?
3280                                esds = {'pos':0.,'int':0.,'sig':0.,'gam':0.}
3281                                for name in esds.keys():
3282                                    esds[name] = sigDict.get('%s%d'%(name,ip),0.)
3283                                sig = np.sqrt(peak[4]) #var -> sig
3284                                gam = peak[6]
3285                                esddsp = 0.5*esds['pos']*dsp/nptand(peak[0]/2.)
3286                                FWHM = G2pwd.getgamFW(gam,sig)      #to get delta-2-theta in deg. from Gam(peak)
3287                                file.write("%10.4f %10.5f %10.5f %12.2f %10.5f %10.5f %10.5f \n" % \
3288                                    (peak[0],dsp,esddsp,peak[2],np.sqrt(max(0.0001,peak[4]))/100.,peak[6]/100.,FWHM/100.)) #convert to deg
3289                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3290                file.close()
3291        finally:
3292            dlg.Destroy()
3293       
3294    def OnExportHKL(self,event):
3295        pth = G2G.GetExportPath(self)
3296        dlg = wx.FileDialog(self, 'Choose output reflection list file name', pth, '', 
3297            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
3298        try:
3299            if dlg.ShowModal() == wx.ID_OK:
3300                self.peaklistfile = dlg.GetPath()
3301                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
3302                file = open(self.peaklistfile,'w')               
3303                item, cookie = self.PatternTree.GetFirstChild(self.root)
3304                while item:
3305                    name = self.PatternTree.GetItemText(item)
3306                    if 'PWDR' in name:
3307                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
3308                        while item2:
3309                            name2 = self.PatternTree.GetItemText(item2)
3310                            if name2 == 'Reflection Lists':
3311                                data = self.PatternTree.GetItemPyData(item2)
3312                                phases = data.keys()
3313                                for phase in phases:
3314                                    peaks = data[phase]
3315                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
3316                                    if 'T' in peaks.get('Type','PXC'):
3317                                        file.write('%s \n'%('   h   k   l   m    d-space     TOF         wid        F**2'))
3318                                    else:               
3319                                        file.write('%s \n'%('   h   k   l   m    d-space   2-theta       wid        F**2'))
3320                                    for peak in peaks['RefList']:
3321                                        if 'T' in peaks.get('Type','PXC'):
3322                                            sig = np.sqrt(peak[6])
3323                                            gam = peak[7]
3324                                            FWHM = G2pwd.getgamFW(gam,sig)
3325                                            file.write(" %3d %3d %3d %3d %10.5f %10.2f %10.5f %10.3f \n" % \
3326                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
3327                                        else:
3328                                            sig = np.sqrt(peak[6])
3329                                            gam = peak[7]
3330                                            FWHM = G2pwd.getgamFW(gam,sig)
3331                                            file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
3332                                                (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM/100.,peak[8]))
3333                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
3334                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
3335                file.close()
3336        finally:
3337            dlg.Destroy()
3338       
3339    def OnExportPDF(self,event):
3340        #need S(Q) and G(R) to be saved here - probably best from selection?
3341        names = ['All']
3342        exports = []
3343        item, cookie = self.PatternTree.GetFirstChild(self.root)
3344        while item:
3345            name = self.PatternTree.GetItemText(item)
3346            if 'PDF' in name:
3347                names.append(name)
3348            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3349        if names:
3350            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
3351            if dlg.ShowModal() == wx.ID_OK:
3352                sel = dlg.GetSelections()
3353                if sel[0] == 0:
3354                    exports = names[1:]
3355                else:
3356                    for x in sel:
3357                        exports.append(names[x])
3358            dlg.Destroy()
3359        if exports:
3360            G2IO.PDFSave(self,exports)
3361       
3362    def OnMakePDFs(self,event):
3363        '''Calculates PDFs
3364        '''
3365        sind = lambda x: math.sin(x*math.pi/180.)
3366        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
3367        TextList = ['All PWDR']
3368        PDFlist = []
3369        Names = []
3370        if self.PatternTree.GetCount():
3371            id, cookie = self.PatternTree.GetFirstChild(self.root)
3372            while id:
3373                name = self.PatternTree.GetItemText(id)
3374                Names.append(name)
3375                if 'PWDR' in name:
3376                    TextList.append(name)
3377                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
3378            if len(TextList) == 1:
3379                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
3380                return
3381            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
3382            try:
3383                if dlg.ShowModal() == wx.ID_OK:
3384                    result = dlg.GetSelections()
3385                    for i in result: PDFlist.append(TextList[i])
3386                    if 0 in result:
3387                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3388                    for item in PDFlist:
3389                        PWDRname = item[4:]
3390                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3391                        Data = {
3392                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3393                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3394                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3395                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3396                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3397                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3398                            'Lorch':True,}
3399                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3400                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3401                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3402                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3403                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3404                for item in self.ExportPDF: item.Enable(True)
3405            finally:
3406                dlg.Destroy()
3407               
3408    def GetPWDRdatafromTree(self,PWDRname):
3409        ''' Returns powder data from GSASII tree
3410
3411        :param str PWDRname: a powder histogram name as obtained from
3412          :meth:`GSASIIstruct.GetHistogramNames`
3413
3414        :returns: PWDRdata = powder data dictionary with
3415          Powder data arrays, Limits, Instrument Parameters,
3416          Sample Parameters           
3417        '''
3418        PWDRdata = {}
3419        try:
3420            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3421        except ValueError:
3422            PWDRdata['wtFactor'] = 1.0
3423        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3424        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3425        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3426        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3427        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3428        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3429        if 'ranId' not in PWDRdata:  # patch, add a random Id
3430            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3431        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3432            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3433        return PWDRdata
3434
3435    def GetHKLFdatafromTree(self,HKLFname):
3436        ''' Returns single crystal data from GSASII tree
3437
3438        :param str HKLFname: a single crystal histogram name as obtained
3439          from
3440          :meth:`GSASIIstruct.GetHistogramNames`
3441
3442        :returns: HKLFdata = single crystal data list of reflections
3443
3444        '''
3445        HKLFdata = {}
3446        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3447#        try:
3448#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3449#        except ValueError:
3450#            HKLFdata['wtFactor'] = 1.0
3451        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3452        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3453        return HKLFdata
3454       
3455    def GetPhaseData(self):
3456        '''Returns a dict with defined phases.
3457        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3458        get same info from GPX file.
3459        '''
3460        phaseData = {}
3461        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3462            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3463        else:
3464            print 'no phases found in GetPhaseData'
3465            sub = None
3466        if sub:
3467            item, cookie = self.PatternTree.GetFirstChild(sub)
3468            while item:
3469                phaseName = self.PatternTree.GetItemText(item)
3470                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3471                if 'ranId' not in phaseData[phaseName]:
3472                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3473                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3474        return phaseData
3475
3476    def GetPhaseInfofromTree(self):
3477        '''Get the phase names and their rId values,
3478        also the histograms used in each phase.
3479
3480        :returns: (phaseRIdList, usedHistograms) where
3481
3482          * phaseRIdList is a list of random Id values for each phase
3483          * usedHistograms is a dict where the keys are the phase names
3484            and the values for each key are a list of the histogram names
3485            used in each phase.
3486        '''
3487        phaseRIdList = []
3488        usedHistograms = {}
3489        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3490        if sub:
3491            item, cookie = self.PatternTree.GetFirstChild(sub)
3492            while item:
3493                phaseName = self.PatternTree.GetItemText(item)
3494                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3495                if ranId: phaseRIdList.append(ranId)
3496                data = self.PatternTree.GetItemPyData(item)
3497                UseList = data['Histograms']
3498                usedHistograms[phaseName] = UseList.keys()
3499                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3500        return phaseRIdList,usedHistograms
3501
3502    def GetPhaseNames(self):
3503        '''Returns a list of defined phases.
3504        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3505        get same info from GPX file.
3506        '''
3507        phaseNames = []
3508        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3509            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3510        else:
3511            print 'no phases found in GetPhaseNames'
3512            sub = None
3513        if sub:
3514            item, cookie = self.PatternTree.GetFirstChild(sub)
3515            while item:
3516                phase = self.PatternTree.GetItemText(item)
3517                phaseNames.append(phase)
3518                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3519        return phaseNames
3520   
3521    def GetHistogramNames(self,hType):
3522        """ Returns a list of histogram names found in the GSASII data tree
3523        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3524        get same info from GPX file.
3525       
3526        :param str hType: list of histogram types
3527        :return: list of histogram names
3528       
3529        """
3530        HistogramNames = []
3531        if self.PatternTree.GetCount():
3532            item, cookie = self.PatternTree.GetFirstChild(self.root)
3533            while item:
3534                name = self.PatternTree.GetItemText(item)
3535                if name[:4] in hType:
3536                    HistogramNames.append(name)       
3537                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3538
3539        return HistogramNames
3540                   
3541    def GetUsedHistogramsAndPhasesfromTree(self):
3542        ''' Returns all histograms that are found in any phase
3543        and any phase that uses a histogram.
3544        This also assigns numbers to used phases and histograms by the
3545        order they appear in the file.
3546        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3547        get same info from GPX file.
3548
3549        :returns: (Histograms,Phases)
3550
3551            * Histograms = dictionary of histograms as {name:data,...}
3552            * Phases = dictionary of phases that use histograms
3553        '''
3554        Histograms = {}
3555        Phases = {}
3556        phaseNames = self.GetPhaseNames()
3557        phaseData = self.GetPhaseData()
3558        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3559
3560        for phase in phaseData:
3561            Phase = phaseData[phase]
3562            pId = phaseNames.index(phase)
3563            Phase['pId'] = pId
3564            if Phase['Histograms']:
3565                if phase not in Phases:
3566                    Phases[phase] = Phase
3567                for hist in Phase['Histograms']:
3568                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3569                        Phase['Histograms'][hist]['Use'] = True         
3570                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3571                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3572                        if item:
3573                            if 'PWDR' in hist[:4]: 
3574                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3575                            elif 'HKLF' in hist[:4]:
3576                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3577                            hId = histoList.index(hist)
3578                            Histograms[hist]['hId'] = hId
3579                        else: # would happen if a referenced histogram were renamed or deleted
3580                            print('For phase "'+str(phase)+
3581                                  '" unresolved reference to histogram "'+str(hist)+'"')
3582        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3583        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3584        return Histograms,Phases
3585       
3586    def MakeLSParmDict(self):
3587        '''Load all parameters used for computation from the tree into a
3588        dict of paired values [value, refine flag]. Note that this is
3589        different than the parmDict used in the refinement, which only has
3590        values.
3591
3592        Note that similar things are done in
3593        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3594        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3595        a GPX file).
3596
3597        :returns: (parmDict,varyList) where:
3598
3599         * parmDict is a dict with values and refinement flags
3600           for each parameter and
3601         * varyList is a list of variables (refined parameters).
3602        '''
3603        parmDict = {}
3604        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3605        for phase in Phases:
3606            if 'pId' not in Phases[phase]:
3607                self.ErrorDialog('View parameter error','You must run least squares at least once')
3608                raise Exception,'No pId for phase '+str(phase)
3609        rigidbodyDict = self.PatternTree.GetItemPyData(   
3610            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3611        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3612        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3613        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,maxSSwave = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3614        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3615        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3616        varyList = rbVary+phaseVary+hapVary+histVary
3617        parmDict.update(rbDict)
3618        parmDict.update(phaseDict)
3619        parmDict.update(hapDict)
3620        parmDict.update(histDict)
3621        for parm in parmDict:
3622            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3623                'Omega','Chi','Phi','nDebye','nPeaks']:
3624                parmDict[parm] = [parmDict[parm],'-']
3625            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3626                parmDict[parm] = [parmDict[parm],'-']
3627            elif parm in varyList:
3628                parmDict[parm] = [parmDict[parm],'T']
3629            else:
3630                parmDict[parm] = [parmDict[parm],'F']
3631        # for i in parmDict: print i,'\t',parmDict[i]
3632        # fl = open('parmDict.dat','wb')
3633        # import cPickle
3634        # cPickle.dump(parmDict,fl,1)
3635        # fl.close()
3636        return parmDict,varyList
3637
3638    def ShowLSParms(self,event):
3639        '''Displays a window showing all parameters in the refinement.
3640        Called from the Calculate/View LS Parms menu.
3641        '''
3642        parmDict,varyList = self.MakeLSParmDict()
3643        parmValDict = {}
3644        for i in parmDict:
3645            parmValDict[i] = parmDict[i][0]
3646           
3647        reqVaryList = tuple(varyList) # save requested variables
3648        try:
3649            # process constraints
3650            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3651            Constraints = self.PatternTree.GetItemPyData(sub)
3652            constList = []
3653            for item in Constraints:
3654                if item.startswith('_'): continue
3655                constList += Constraints[item]
3656            G2mv.InitVars()
3657            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3658            groups,parmlist = G2mv.GroupConstraints(constrDict)
3659            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3660            G2mv.Map2Dict(parmValDict,varyList)
3661        except:
3662            pass
3663        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3664        dlg.ShowModal()
3665        dlg.Destroy()
3666       
3667    def OnRefine(self,event):
3668        '''Perform a refinement.
3669        Called from the Calculate/Refine menu.
3670        '''       
3671        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3672        if Id:
3673            dlg = wx.MessageDialog(
3674                self,
3675                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3676                'Remove sequential results?',wx.OK|wx.CANCEL)
3677            if dlg.ShowModal() == wx.ID_OK:
3678                self.PatternTree.Delete(Id)
3679                dlg.Destroy()
3680            else:
3681                dlg.Destroy()
3682                return
3683        self.OnFileSave(event)
3684        # check that constraints are OK here
3685        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3686        if errmsg:
3687            self.ErrorDialog('Refinement error',errmsg)
3688            return
3689        if warnmsg:
3690            print('Conflict between refinment flag settings and constraints:\n'+
3691                warnmsg+'\nRefinement not possible')
3692            self.ErrorDialog('Refinement Flag Error',
3693                'Conflict between refinement flag settings and constraints:\n'+
3694                warnmsg+'\nRefinement not possible')
3695            return
3696        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3697            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3698            parent=self)
3699        Size = dlg.GetSize()
3700        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3701            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3702        dlg.CenterOnParent()
3703        Rw = 100.00
3704        oldId =  self.PatternTree.GetSelection()        #retain current selection
3705        oldPath = self.GetTreeItemsList(oldId)
3706        parentName = ''
3707        oldName = self.PatternTree.GetItemText(oldId)
3708        parentId = self.PatternTree.GetItemParent(oldId)
3709        if parentId:
3710            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3711            if 'Phases' in parentName:
3712                tabId = self.dataDisplay.GetSelection()
3713        try:
3714            OK,Msg = G2stMn.Refine(self.GSASprojectfile,dlg)
3715        finally:
3716            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3717            dlg.Destroy()
3718            wx.Yield()
3719        if OK:
3720            Rw = Msg
3721            dlg2 = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3722            try:
3723                if dlg2.ShowModal() == wx.ID_OK:
3724                    Id = 0
3725                    self.G2plotNB.setReplotFlags() # mark all plots as old
3726                    self.PatternTree.DeleteChildren(self.root)
3727                    self.HKL = []
3728                    G2IO.ProjFileOpen(self,False)
3729                    Id =  self.root
3730                    txt = None
3731                    for txt in oldPath:
3732                        Id = G2gd.GetPatternTreeItemId(self, Id, txt)
3733                    self.PickIdText = None  #force reload of page
3734                    self.PickId = Id
3735                    self.PatternTree.SelectItem(Id)
3736                    G2gd.MovePatternTreeToGrid(self,Id)
3737                    self.G2plotNB.replotAll() # refresh any plots not yet updated
3738            finally:
3739                dlg2.Destroy()
3740        else:
3741            self.ErrorDialog('Refinement error',Msg)
3742
3743    def OnSeqRefine(self,event):
3744        '''Perform a sequential refinement.
3745        Called from the Calculate/Sequential refine menu.
3746        '''       
3747        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3748        if not Id:
3749            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3750            self.PatternTree.SetItemPyData(Id,{})           
3751        Controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
3752        Controls['ShowCell'] = True
3753        self.OnFileSave(event)
3754        # check that constraints are OK here
3755        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
3756        if errmsg:
3757            self.ErrorDialog('Refinement error',errmsg)
3758            return
3759        if warnmsg:
3760            print('Conflict between refinment flag settings and constraints:\n'+
3761                  warnmsg+'\nRefinement not possible')
3762            self.ErrorDialog('Refinement Flag Error',
3763                             'Conflict between refinment flag settings and constraints:\n'+
3764                             warnmsg+'\nRefinement not possible')
3765            return
3766        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3767            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT,
3768            parent=self)           
3769        Size = dlg.GetSize()
3770        if 50 < Size[0] < 500: # sanity check on size, since this fails w/Win & wx3.0
3771            dlg.SetSize((int(Size[0]*1.2),Size[1])) # increase size a bit along x
3772        dlg.CenterOnParent()
3773        try:
3774            OK,Msg = G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3775        finally:
3776            dlg.Update(101.) # forces the Auto_Hide; needed after move w/Win & wx3.0
3777            dlg.Destroy()
3778            wx.Yield()
3779        if OK:
3780            dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3781            try:
3782                if dlg.ShowModal() == wx.ID_OK:
3783                    Id = 0
3784                    self.G2plotNB.setReplotFlags() # mark all plots as old
3785                    self.PickIdText = None  #force reload of PickId contents
3786                    self.PatternTree.DeleteChildren(self.root)
3787                    if len(self.HKL): self.HKL = []
3788                    if self.G2plotNB.plotList:
3789                        self.G2plotNB.clear()
3790                    G2IO.ProjFileOpen(self,False)
3791                    Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3792                    self.PatternTree.SelectItem(Id)
3793                    self.G2plotNB.replotAll() # refresh any plots not yet updated
3794            finally:
3795                dlg.Destroy()
3796        else:
3797            self.ErrorDialog('Sequential refinement error',Msg)
3798       
3799    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3800        'Display an error message'
3801        result = None
3802        if parent is None:
3803            dlg = wx.MessageDialog(self, message, title,  wtype)
3804        else:
3805            dlg = wx.MessageDialog(parent, message, title,  wtype)
3806            dlg.CenterOnParent() # not working on Mac
3807        try:
3808            result = dlg.ShowModal()
3809        finally:
3810            dlg.Destroy()
3811        return result
3812   
3813    def OnSaveMultipleImg(self,event):
3814        '''Select and save multiple image parameter and mask files
3815        '''
3816        G2IO.SaveMultipleImg(self)
3817       
3818class GSASIImain(wx.App):
3819    '''Defines a wxApp for GSAS-II
3820
3821    Creates a wx frame (self.main) which contains the display of the
3822    data tree.
3823    '''
3824    def OnInit(self):
3825        '''Called automatically when the app is created.'''
3826        import platform
3827        if '2.7' not in sys.version[:5]:
3828            dlg = wx.MessageDialog(None, 
3829                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version.split()[0],
3830                'Python version error',  wx.OK)
3831            try:
3832                result = dlg.ShowModal()
3833            finally:
3834                dlg.Destroy()
3835            sys.exit()
3836        self.main = GSASII(None)
3837        self.main.Show()
3838        self.SetTopWindow(self.main)
3839        # save the current package versions
3840        self.main.PackageVersions = []
3841        self.main.PackageVersions.append(['Python',sys.version.split()[0]])
3842        for p in (wx,mpl,np,sp,ogl):
3843            self.main.PackageVersions.append([p.__name__,p.__version__])
3844        try:
3845            self.main.PackageVersions.append([Image.__name__,Image.VERSION])
3846        except:
3847            try:
3848                from PIL import PILLOW_VERSION
3849                self.main.PackageVersions.append([Image.__name__,PILLOW_VERSION])
3850            except:
3851                pass
3852        self.main.PackageVersions.append([' Platform',sys.platform+' '+platform.architecture()[0]+' '+platform.machine()])
3853       
3854        return True
3855    # def MacOpenFile(self, filename):
3856    #     '''Called on Mac every time a file is dropped on the app when it is running,
3857    #     treat this like a File/Open project menu action.
3858    #     Should be ignored on other platforms
3859    #     '''
3860    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3861    #     print 'MacOpen',filename
3862    #     if os.path.splitext(filename)[1] == '.py': return
3863    #     # end PATCH
3864    #     self.main.OnFileOpen(None,filename)
3865    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3866       
3867def main():
3868    '''Start up the GSAS-II application'''
3869    #application = GSASIImain() # don't redirect output, someday we
3870    # may want to do this if we can
3871    application = GSASIImain(0)
3872    if GSASIIpath.GetConfigValue('wxInspector'):
3873        import wx.lib.inspection as wxeye
3874        wxeye.InspectionTool().Show()
3875
3876    #application.main.OnRefine(None)
3877    application.MainLoop()
3878   
3879if __name__ == '__main__':
3880    # print versions
3881    print "Python module versions loaded:"
3882    print "python:     ",sys.version.split()[0]
3883    print "wxpython:   ",wx.__version__
3884    print "matplotlib: ",mpl.__version__
3885    print "numpy:      ",np.__version__
3886    print "scipy:      ",sp.__version__
3887    print "OpenGL:     ",ogl.__version__
3888    try:
3889        from PIL import Image
3890        try:
3891            from PIL import PILLOW_VERSION
3892            version = PILLOW_VERSION
3893        except:
3894            version = Image.VERSION
3895        print "pillow:     ",version
3896    except ImportError:
3897        try:
3898            import Image
3899            print "Image (PIL):",Image.VERSION
3900        except ImportError:
3901            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3902    try:
3903        import mkl
3904        print "Max threads ",mkl.get_max_threads()
3905    except:
3906        pass
3907    import platform
3908    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3909    #print "wxPython description",wx.PlatformInfo
3910    print "This is GSAS-II version:     ",__version__,' revision #'+str(GSASIIpath.GetVersionNumber())
3911    GSASIIpath.InvokeDebugOpts()
3912    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.