source: trunk/GSASII.py @ 2304

Last change on this file since 2304 was 2304, checked in by vondreele, 7 years ago

add Cancel to image integration ProgressDialog? - will do a Pause in AutoIntegrate?

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