source: trunk/GSASII.py @ 2147

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

handle TOF profile type=2 iparm files (e.g. for GEM)
implement multibank instprm files - add Save all profiles to Instrument parameters MenuBinding?
Do the Load profile to handle these - modifications to OnLoad?
add common transformation matrices to MergeDialog?
add bank no. to ReadPowderInstprm? call for matching it - in progress

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