source: branch/logging/GSASII.py @ 1505

Last change on this file since 1505 was 1505, checked in by toby, 7 years ago

track with relative phase & histogram numbers

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