source: trunk/GSASII.py @ 2344

Last change on this file since 2344 was 2344, checked in by toby, 5 years ago

add option to correct data on import

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