source: trunk/GSASII.py @ 2234

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

implement code to select appropriate tree time when a plot tab is selected

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