source: branch/logging/GSASII.py @ 1510

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

logging pretty well ready to roll

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 163.4 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2014-10-01 21:14:25 +0000 (Wed, 01 Oct 2014) $
6# $Author: toby $
7# $Revision: 1510 $
8# $URL: branch/logging/GSASII.py $
9# $Id: GSASII.py 1510 2014-10-01 21:14:25Z 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: 1510 $")
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
78import GSASIIlog as log
79
80#wx inspector - use as needed
81wxInspector = False
82
83__version__ = '0.2.0'
84G2gd.__version__ = __version__
85
86# PATCH: for Mavericks (OS X 10.9.x), wx produces an annoying warning about LucidaGrandeUI.
87# In case stderr has been suppressed there, redirect python error output to stdout. Nobody
88# else should care much about this.
89sys.stderr = sys.stdout
90
91# useful degree trig functions
92sind = lambda x: math.sin(x*math.pi/180.)
93cosd = lambda x: math.cos(x*math.pi/180.)
94tand = lambda x: math.tan(x*math.pi/180.)
95asind = lambda x: 180.*math.asin(x)/math.pi
96acosd = lambda x: 180.*math.acos(x)/math.pi
97atan2d = lambda x,y: 180.*math.atan2(y,x)/math.pi
98
99def create(parent):
100    return GSASII(parent)
101
102def SetDefaultDData(dType,histoName,NShkl=0,NDij=0):
103    if dType in ['SXC','SNC']:
104        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
105            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
106            'Extinction':['Lorentzian','None', {'Tbar':0.1,'Cos2TM':0.955,
107            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}]}
108    elif dType == 'SNT':
109        return {'Histogram':histoName,'Show':False,'Scale':[1.0,True],
110            'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]},
111            'Extinction':['Lorentzian','None', {
112            'Eg':[1.e-10,False],'Es':[1.e-10,False],'Ep':[1.e-10,False]}]}
113    elif 'P' in dType:
114        return {'Histogram':histoName,'Show':False,'Scale':[1.0,False],
115            'Pref.Ori.':['MD',1.0,False,[0,0,1],0,{}],
116            'Size':['isotropic',[1.,1.,1.],[False,False,False],[0,0,1],
117                [1.,1.,1.,0.,0.,0.],6*[False,]],
118            'Mustrain':['isotropic',[1000.0,1000.0,1.0],[False,False,False],[0,0,1],
119                NShkl*[0.01,],NShkl*[False,]],
120            'HStrain':[NDij*[0.0,],NDij*[False,]],                         
121            'Extinction':[0.0,False],'Babinet':{'BabA':[0.0,False],'BabU':[0.0,False]}}
122
123class GSASII(wx.Frame):
124    '''Define the main GSAS-II frame and its associated menu items
125    '''
126    def MenuBinding(self,event):
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        #print 'main frame bind',handler
133        if eventtype == wx.EVT_MENU and 'id' in kwargs:
134            menulabels = log.SaveMenuCommand(kwargs['id'],self,handler)
135            if menulabels:
136                #print 'intercepting bind for',handler,menulabels,kwargs['id']
137                wx.Frame.Bind(self,eventtype,self.MenuBinding,*args,**kwargs)
138                return
139        wx.Frame.Bind(self,eventtype,handler,*args,**kwargs)     
140       
141   
142    def _Add_FileMenuItems(self, parent):
143        item = parent.Append(
144            help='Open a GSAS-II project file (*.gpx)', id=wx.ID_ANY,
145            kind=wx.ITEM_NORMAL,text='&Open project...')
146        self.Bind(wx.EVT_MENU, self.OnFileOpen, id=item.GetId())
147        item = parent.Append(
148            help='Save project under current name', id=wx.ID_ANY,
149            kind=wx.ITEM_NORMAL,text='&Save project')
150        self.Bind(wx.EVT_MENU, self.OnFileSave, id=item.GetId())
151        item = parent.Append(
152            help='Save current project to new file', id=wx.ID_ANY,
153            kind=wx.ITEM_NORMAL,text='Save project as...')
154        self.Bind(wx.EVT_MENU, self.OnFileSaveas, id=item.GetId())
155        item = parent.Append(
156            help='Create empty new project, saving current is optional', id=wx.ID_ANY,
157            kind=wx.ITEM_NORMAL,text='&New project')
158        self.Bind(wx.EVT_MENU, self.OnFileClose, id=item.GetId())
159        item = parent.Append(
160            help='Exit from GSAS-II', id=wx.ID_ANY,
161            kind=wx.ITEM_NORMAL,text='&Exit')
162        self.Bind(wx.EVT_MENU, self.OnFileExit, id=item.GetId())
163       
164    def _Add_DataMenuItems(self,parent):
165        item = parent.Append(
166            help='',id=wx.ID_ANY,
167            kind=wx.ITEM_NORMAL,
168            text='Read image data...')
169        self.Bind(wx.EVT_MENU, self.OnImageRead, id=item.GetId())
170        item = parent.Append(
171            help='',id=wx.ID_ANY,
172            kind=wx.ITEM_NORMAL,
173            text='Read Powder Pattern Peaks...')
174        self.Bind(wx.EVT_MENU, self.OnReadPowderPeaks, id=item.GetId())
175        item = parent.Append(
176            help='',id=wx.ID_ANY,
177            kind=wx.ITEM_NORMAL,
178            text='Sum powder data')
179        self.Bind(wx.EVT_MENU, self.OnPwdrSum, id=item.GetId())
180        item = parent.Append(
181            help='',id=wx.ID_ANY,
182            kind=wx.ITEM_NORMAL,
183            text='Sum image data')
184        self.Bind(wx.EVT_MENU, self.OnImageSum, id=item.GetId())
185        item = parent.Append(
186            help='',id=wx.ID_ANY,
187            kind=wx.ITEM_NORMAL,
188            text='Add phase')
189        self.Bind(wx.EVT_MENU, self.OnAddPhase, id=item.GetId())
190        item = parent.Append(
191            help='',id=wx.ID_ANY,
192            kind=wx.ITEM_NORMAL,
193            text='Delete phase')
194        self.Bind(wx.EVT_MENU, self.OnDeletePhase, id=item.GetId())
195        item = parent.Append(
196            help='',id=wx.ID_ANY,
197            kind=wx.ITEM_NORMAL,
198            text='Rename data') 
199        self.Bind(wx.EVT_MENU, self.OnRenameData, id=item.GetId())
200        item = parent.Append(
201            help='',id=wx.ID_ANY,
202            kind=wx.ITEM_NORMAL,
203            text='Delete data')
204        self.Bind(wx.EVT_MENU, self.OnDataDelete, id=item.GetId())
205               
206    def _Add_CalculateMenuItems(self,parent):
207        item = parent.Append(help='Make new PDFs from selected powder patterns', 
208            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='Make new PDFs')
209        self.MakePDF.append(item)
210#        item.Enable(False)
211        self.Bind(wx.EVT_MENU, self.OnMakePDFs, id=item.GetId())
212       
213        item = parent.Append(help='View least squares parameters', 
214            id=wx.ID_ANY, kind=wx.ITEM_NORMAL,text='&View LS parms')
215        self.Bind(wx.EVT_MENU, self.ShowLSParms, id=item.GetId())
216       
217        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
218            text='&Refine')
219        if len(self.Refine): # extend state for new menus to match main (on mac)
220            state = self.Refine[0].IsEnabled()
221        else:
222            state = False
223        item.Enable(state)
224        self.Refine.append(item)
225        self.Bind(wx.EVT_MENU, self.OnRefine, id=item.GetId())
226       
227        item = parent.Append(help='', id=wx.ID_ANY, kind=wx.ITEM_NORMAL,
228            text='Sequential refine')
229        if len(self.SeqRefine): # extend state for new menus to match main (on mac)
230            state = self.SeqRefine[0].IsEnabled()
231        else:
232            state = False
233        item.Enable(state)
234        self.SeqRefine.append(item) # save menu obj for use in self.EnableSeqRefineMenu
235        self.Bind(wx.EVT_MENU, self.OnSeqRefine, id=item.GetId())
236       
237    def _init_Imports(self):
238        '''import all the G2phase*.py & G2sfact*.py & G2pwd*.py files that
239        are found in the path
240        '''
241
242        self.ImportPhaseReaderlist = []
243        self._init_Import_routines('phase',self.ImportPhaseReaderlist,'Phase')
244        self.ImportSfactReaderlist = []
245        self._init_Import_routines('sfact',self.ImportSfactReaderlist,'Struct_Factor')
246        self.ImportPowderReaderlist = []
247        self._init_Import_routines('pwd',self.ImportPowderReaderlist,'Powder_Data')
248        self.ImportSmallAngleReaderlist = []
249        self._init_Import_routines('sad',self.ImportSmallAngleReaderlist,'SmallAngle_Data')
250        self.ImportMenuId = {}
251
252    def _init_Import_routines(self,prefix,readerlist,errprefix):
253        '''import all the import readers matching the prefix
254        '''
255        #path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file
256        #pathlist = sys.path[:]
257        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
258        #path2GSAS2 = os.path.join(
259        #    os.path.dirname(os.path.realpath(__file__)), # location of this file
260        #    'imports')
261        pathlist = sys.path[:]
262        #if path2GSAS2 not in pathlist: pathlist.append(path2GSAS2)
263
264        filelist = []
265        for path in pathlist:
266            for filename in glob.iglob(os.path.join(
267                path,
268                "G2"+prefix+"*.py")):
269                filelist.append(filename)   
270                #print 'debug: found',filename
271        filelist = sorted(list(set(filelist))) # remove duplicates
272        for filename in filelist:
273            path,rootname = os.path.split(filename)
274            pkg = os.path.splitext(rootname)[0]
275            try:
276                fp = None
277                fp, fppath,desc = imp.find_module(pkg,[path,])
278                pkg = imp.load_module(pkg,fp,fppath,desc)
279                for clss in inspect.getmembers(pkg): # find classes defined in package
280                    if clss[0].startswith('_'): continue
281                    if inspect.isclass(clss[1]):
282                        # check if we have the required methods
283                        for m in 'Reader','ExtensionValidator','ContentsValidator':
284                            if not hasattr(clss[1],m): break
285                            if not callable(getattr(clss[1],m)): break
286                        else:
287                            reader = clss[1]() # create an import instance
288                            readerlist.append(reader)
289            except AttributeError:
290                print 'Import_'+errprefix+': Attribute Error '+str(filename)
291                pass
292            except ImportError:
293                print 'Import_'+errprefix+': Error importing file '+str(filename)
294                pass
295            if fp: fp.close()
296
297    def EnableSeqRefineMenu(self):
298        '''Enable or disable the sequential refinement menu items based on the
299        contents of the Controls 'Seq Data' item (if present)
300        '''
301        controls = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,self.root, 'Controls'))
302        if controls.get('Seq Data'):
303            for i in self.SeqRefine: i.Enable(True)
304        else:
305            for i in self.SeqRefine: i.Enable(False)
306
307    def OnImportGeneric(self,reader,readerlist,label,multiple=False,usedRanIdList=[]):
308        '''Used to import Phases, powder dataset or single
309        crystal datasets (structure factor tables) using reader objects
310        subclassed from :class:`GSASIIIO.ImportPhase`, :class:`GSASIIIO.ImportStructFactor`
311        or :class:`GSASIIIO.ImportPowderData`. If a reader is specified, only
312        that will be attempted, but if no reader is specified, every one
313        that is potentially compatible (by file extension) will
314        be tried on the selected file(s).
315
316        :param readerobject reader: This will be a reference to
317          a particular object to be used to read a file or None,
318          if every appropriate reader should be used.
319
320        :param list readerlist: a list of reader objects appropriate for
321          the current read attempt. At present, this will be either
322          self.ImportPhaseReaderlist, self.ImportSfactReaderlist or
323          self.ImportPowderReaderlist (defined in _init_Imports from
324          the files found in the path), but in theory this list could
325          be tailored. Used only when reader is None.
326
327        :param str label: string to place on the open file dialog:
328          Open `label` input file
329
330        :param bool multiple: True if multiple files can be selected
331          in the file dialog. False is default. At present True is used
332          only for reading of powder data.
333
334        :param list usedRanIdList: an optional list of random Ids that
335          have been used and should not be reused
336
337        :returns: a list of reader objects (rd_list) that were able
338          to read the specified file(s). This list may be empty.
339        '''
340        self.lastimport = ''
341        self.zipfile = None
342        singlereader = True
343        if reader is None:
344            singlereader = False
345            multiple = False
346            #print "use all formats"
347            choices = "any file (*.*)|*.*"
348            choices += "|zip archive (.zip)|*.zip"
349            extdict = {}
350            # compile a list of allowed extensions
351            for rd in readerlist:
352                fmt = rd.formatName
353                for extn in rd.extensionlist:
354                    if not extdict.get(extn): extdict[extn] = []
355                    extdict[extn] += [fmt,]
356            for extn in sorted(extdict.keys(),cmp=lambda x,y: cmp(x.lower(), y.lower())):
357                fmt = ''
358                for f in extdict[extn]:
359                    if fmt != "": fmt += ', '
360                    fmt += f
361                choices += "|" + fmt + " file (*" + extn + ")|*" + extn
362        else:
363            readerlist = [reader,]
364            # compile a list of allowed extensions
365            choices = reader.formatName + " file ("
366            w = ""
367            for extn in reader.extensionlist:
368                if w != "": w += ";"
369                w += "*" + extn
370            choices += w + ")|" + w
371            choices += "|zip archive (.zip)|*.zip"
372            if not reader.strictExtension:
373                choices += "|any file (*.*)|*.*"
374        # get the file(s)
375        if multiple:
376            mode = style=wx.OPEN | wx.CHANGE_DIR | wx.MULTIPLE
377        else:
378            mode = style=wx.OPEN | wx.CHANGE_DIR
379        dlg = wx.FileDialog(self, message="Choose "+label+" input file",
380            defaultFile="",wildcard=choices, style=mode)
381        if self.Tutorials:
382            dlg.SetDirectory(os.path.join(GSASIIpath.path2GSAS2,'Exercises'))
383            self.Tutorials = False
384        try:
385            if dlg.ShowModal() == wx.ID_OK:
386                if multiple:
387                    filelist = dlg.GetPaths()
388                    if len(filelist) == 0: return []
389                else:
390                    filename = dlg.GetPath()
391                    filelist = [filename,]
392                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
393            else: # cancel was pressed
394                return []
395        finally:
396            dlg.Destroy()
397        rd_list = []
398        filelist1 = []
399        for filename in filelist:
400            # is this a zip file?
401            if os.path.splitext(filename)[1].lower() == '.zip':
402                extractedfiles = G2IO.ExtractFileFromZip(
403                    filename,parent=self,
404                    multipleselect=True)
405                if extractedfiles is None: continue # error or Cancel
406                if extractedfiles != filename:
407                    self.zipfile = filename # save zip name
408                    filelist1 += extractedfiles
409                    continue
410            filelist1.append(filename)
411        filelist = filelist1
412        for filename in filelist:
413            # is this a zip file?
414            if os.path.splitext(filename)[1].lower() == '.zip':
415                extractedfile = G2IO.ExtractFileFromZip(filename,parent=self)
416                if extractedfile is None: continue # error or Cancel
417                if extractedfile != filename:
418                    filename,self.zipfile = extractedfile,filename # now use the file that was created
419            # set what formats are compatible with this file
420            primaryReaders = []
421            secondaryReaders = []
422            for rd in readerlist:
423                flag = rd.ExtensionValidator(filename)
424                if flag is None: 
425                    secondaryReaders.append(rd)
426                elif flag:
427                    primaryReaders.append(rd)
428            if len(secondaryReaders) + len(primaryReaders) == 0:
429                self.ErrorDialog('No Format','No matching format for file '+filename)
430                return []
431
432            fp = None
433            msg = ''
434            try:
435                fp = open(filename,'Ur')
436                if len(filelist) == 1:
437                    # confirm we have the right file
438                    rdmsg = 'File '+str(filename)+' begins:\n\n'
439                    for i in range(3):
440                        rdmsg += fp.readline()
441                    rdmsg += '\n\nDo you want to read this file?'
442                    if not all([ord(c) < 128 and ord(c) != 0 for c in rdmsg]): # show only if ASCII
443                        rdmsg = 'File '+str(
444                            filename)+' is a binary file. Do you want to read this file?'
445                    result = wx.ID_NO
446                    # it would be better to use something that
447                    # would resize better, but this will do for now
448                    dlg = wx.MessageDialog(
449                        self, rdmsg,
450                        'Is this the file you want?', 
451                        wx.YES_NO | wx.ICON_QUESTION,
452                        )
453                    dlg.SetSize((700,300)) # does not resize on Mac
454                    try:
455                        result = dlg.ShowModal()
456                    finally:
457                        dlg.Destroy()
458                    if result == wx.ID_NO: return []
459                           
460                self.lastimport = filename # this is probably not what I want to do -- it saves only the
461                # last name in a series. See rd.readfilename for a better name.
462               
463                # try the file first with Readers that specify the
464                # file's extension and later with ones that merely allow it
465                errorReport = ''
466                for rd in primaryReaders+secondaryReaders:
467                    rd.ReInitialize() # purge anything from a previous read
468                    fp.seek(0)  # rewind
469                    rd.errors = "" # clear out any old errors
470                    if not rd.ContentsValidator(fp): # rejected on cursory check
471                        errorReport += "\n  "+rd.formatName + ' validator error'
472                        if rd.errors: 
473                            errorReport += ': '+rd.errors
474                        continue 
475                    repeat = True
476                    rdbuffer = {} # create temporary storage for file reader
477                    block = 0
478                    while repeat: # loop if the reader asks for another pass on the file
479                        block += 1
480                        repeat = False
481                        fp.seek(0)  # rewind
482                        rd.objname = os.path.basename(filename)
483                        flag = False
484                        try:
485                            flag = rd.Reader(filename,fp,self,
486                                             buffer=rdbuffer,
487                                             blocknum=block,
488                                             usedRanIdList=usedRanIdList,
489                                             )
490                        except rd.ImportException as detail:
491                            rd.errors += "\n  Read exception: "+str(detail)
492                        except Exception as detail:
493                            rd.errors += "\n  Unhandled read exception: "+str(detail)
494                            rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
495                        if flag: # this read succeeded
496                            rd.readfilename = filename
497                            rd_list.append(copy.deepcopy(rd)) # save the result before it is written over
498                            if rd.repeat:
499                                repeat = True
500                            continue
501                        errorReport += '\n'+rd.formatName + ' read error'
502                        if rd.errors:
503                            errorReport += ': '+rd.errors
504                    if rd_list: # read succeeded, was there a warning or any errors?
505                        if rd.warnings:
506                            self.ErrorDialog('Read Warning','The '+ rd.formatName+
507                                             ' reader reported a warning message:\n\n'+
508                                             rd.warnings)
509                        break # success in reading, try no further
510                else:
511                    if singlereader:
512                        self.ErrorDialog('Read Error','The '+ rd.formatName+
513                                         ' reader was not able to read file '+filename+msg+
514                                         '\n\nError message(s):\n'+errorReport
515                                         )
516                    else:
517                        self.ErrorDialog('Read Error','No reader was able to read file '+filename+msg+
518                                         '\n\nError messages:\n'+errorReport
519                                         )
520            except:
521                import traceback
522                print traceback.format_exc()
523                self.ErrorDialog('Open Error','Unexpected error trying to open or read file '+filename)
524            if fp: fp.close()
525        return rd_list
526
527    def _Add_ImportMenu_Phase(self,parent):
528        '''configure the Import Phase menus accord to the readers found in _init_Imports
529        '''
530        submenu = wx.Menu()
531        item = parent.AppendMenu(wx.ID_ANY, 'Phase',
532            submenu, help='Import phase data')
533        for reader in self.ImportPhaseReaderlist:
534            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
535                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
536            self.ImportMenuId[item.GetId()] = reader
537            self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
538        item = submenu.Append(wx.ID_ANY,
539                              help='Import phase data, use file to try to determine format',
540                              kind=wx.ITEM_NORMAL,
541                              text='guess format from file')
542        self.Bind(wx.EVT_MENU, self.OnImportPhase, id=item.GetId())
543
544    def OnImportPhase(self,event):
545        '''Called in response to an Import/Phase/... menu item
546        to read phase information.
547        dict self.ImportMenuId is used to look up the specific
548        reader item associated with the menu item, which will be
549        None for the last menu item, which is the "guess" option
550        where all appropriate formats will be tried.
551        '''
552        # look up which format was requested
553        reqrdr = self.ImportMenuId.get(event.GetId())
554       
555        # make a list of phase names, ranId's and the histograms used in those phases
556        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
557        phaseNameList = usedHistograms.keys() # phase names in use
558        usedHKLFhists = [] # used single-crystal histograms
559        for p in usedHistograms:
560            for h in usedHistograms[p]:
561                if h.startswith('HKLF ') and h not in usedHKLFhists:
562                    usedHKLFhists.append(h)
563        rdlist = self.OnImportGeneric(reqrdr,
564                                  self.ImportPhaseReaderlist,
565                                  'phase',usedRanIdList=phaseRIdList)
566        if len(rdlist) == 0: return
567        # for now rdlist is only expected to have one element
568        # but below will allow multiple phases to be imported
569        # if ever the import routines ever implement multiple phase reads.
570        self.CheckNotebook()
571        newPhaseList = []
572        for rd in rdlist:
573            dlg = wx.TextEntryDialog( # allow editing of phase name
574                self, 'Enter the name for the new phase',
575                'Edit phase name', rd.Phase['General']['Name'],
576                style=wx.OK)
577            dlg.CenterOnParent()
578            if dlg.ShowModal() == wx.ID_OK:
579                rd.Phase['General']['Name'] = dlg.GetValue()
580            dlg.Destroy()
581            # make new phase names unique
582            rd.Phase['General']['Name'] = G2obj.MakeUniqueLabel(rd.Phase['General']['Name'],phaseNameList)
583            PhaseName = rd.Phase['General']['Name']
584            newPhaseList.append(PhaseName)
585            print 'Read phase '+str(PhaseName)+' from file '+str(self.lastimport)
586            if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
587                sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
588            else:
589                sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
590            psub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
591            self.PatternTree.SetItemPyData(psub,rd.Phase)
592            self.PatternTree.Expand(self.root) # make sure phases are seen
593            self.PatternTree.Expand(sub) 
594            self.PatternTree.Expand(psub)
595            self.PatternTree.SelectItem(psub) # show the page to complete the initialization (yuk!)
596            wx.Yield() # make sure call of GSASII.OnPatternTreeSelChanged happens before we go on
597
598            if rd.Constraints:
599                sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') # was created in CheckNotebook if needed
600                Constraints = self.PatternTree.GetItemPyData(sub)               
601                # TODO: make sure that NEWVAR names are unique here?
602                for i in rd.Constraints:
603                    if type(i) is dict:
604                        #for j in i: print j,' --> ',i[j]
605                        if '_Explain' not in Constraints: Constraints['_Explain'] = {}
606                        Constraints['_Explain'].update(i)
607                        continue
608                    Constraints['Phase'].append(i)
609        if not newPhaseList: return # somehow, no new phases
610        # get a list of existing histograms
611        PWDRlist = []
612        HKLFlist = []
613        if self.PatternTree.GetCount():
614            item, cookie = self.PatternTree.GetFirstChild(self.root)
615            while item:
616                name = self.PatternTree.GetItemText(item)
617                if name.startswith('PWDR ') and name not in PWDRlist:
618                    PWDRlist.append(name)
619                if name.startswith('HKLF ') and name not in HKLFlist:
620                    HKLFlist.append(name)
621                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
622        TextList = PWDRlist + HKLFlist
623        if not len(TextList): return # no data loaded yet
624        header = 'Select histogram(s) to add to new phase(s):'
625        for phaseName in newPhaseList:
626            header += '\n  '+str(phaseName)
627
628        notOK = True
629        while notOK:
630            result = G2gd.ItemSelector(TextList,self,header,header='Add histogram(s)',multiple=True)
631            if not result: return
632            # check that selected single crystal histograms are not already in use!
633            used = [TextList[i] for i in result if TextList[i] in usedHKLFhists]
634            #for i in result:
635            #    if TextList[i] in usedHKLFhists: used.append(TextList[i])
636            if used:
637                msg = 'The following single crystal histogram(s) are already in use'
638                for i in used:
639                    msg += '\n  '+str(i)
640                msg += '\nAre you sure you want to add them to this phase? '
641                msg += 'Associating a single crystal dataset to >1 histogram is usually an error, '
642                msg += 'so No is suggested here.'
643                if self.ErrorDialog('Likely error',msg,self,wtype=wx.YES_NO) == wx.ID_YES: notOK = False
644            else:
645                notOK = False
646        # connect new phases to histograms
647        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
648        if not sub:
649            raise Exception('ERROR -- why are there no phases here?')
650        wx.BeginBusyCursor()
651        item, cookie = self.PatternTree.GetFirstChild(sub)
652        while item: # loop over (new) phases
653            phaseName = self.PatternTree.GetItemText(item)
654            data = self.PatternTree.GetItemPyData(item)
655            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
656            if phaseName not in newPhaseList: continue
657            generalData = data['General']
658            SGData = generalData['SGData']
659            UseList = data['Histograms']
660            NShkl = len(G2spc.MustrainNames(SGData))
661            NDij = len(G2spc.HStrainNames(SGData))
662            for i in result:
663                histoName = TextList[i]
664                if histoName in HKLFlist:
665                    #redo UpdateHKLFdata(histoName) here:
666                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
667                    refDict,reflData = self.PatternTree.GetItemPyData(Id)
668                    G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
669                    for iref,ref in enumerate(reflData['RefList']):
670                        H = list(ref[:3])
671                        ref[4] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
672                        iabsnt,ref[3],Uniq,phi = G2spc.GenHKLf(H,SGData)
673                    UseList[histoName] = SetDefaultDData(refDict['Type'],histoName)
674                elif histoName in PWDRlist:
675                    Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
676                    refList = self.PatternTree.GetItemPyData(
677                        G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
678                    refList[generalData['Name']] = []
679                    UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
680                else:
681                    raise Exception('Unexpected histogram '+str(histoName))
682        wx.EndBusyCursor()
683        return # success
684       
685    def _Add_ImportMenu_Sfact(self,parent):
686        '''configure the Import Structure Factor menus accord to the readers found in _init_Imports
687        '''
688        submenu = wx.Menu()
689        item = parent.AppendMenu(wx.ID_ANY, 'Structure Factor',
690            submenu, help='Import Structure Factor data')
691        for reader in self.ImportSfactReaderlist:
692            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,               
693                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
694            self.ImportMenuId[item.GetId()] = reader
695            self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
696        item = submenu.Append(wx.ID_ANY,
697            help='Import Structure Factor, use file to try to determine format',
698            kind=wx.ITEM_NORMAL,
699            text='guess format from file')
700        self.Bind(wx.EVT_MENU, self.OnImportSfact, id=item.GetId())
701
702    def OnImportSfact(self,event):
703        '''Called in response to an Import/Structure Factor/... menu item
704        to read single crystal datasets.
705        dict self.ImportMenuId is used to look up the specific
706        reader item associated with the menu item, which will be
707        None for the last menu item, which is the "guess" option
708        where all appropriate formats will be tried.
709        '''
710        # get a list of existing histograms
711        HKLFlist = []
712        if self.PatternTree.GetCount():
713            item, cookie = self.PatternTree.GetFirstChild(self.root)
714            while item:
715                name = self.PatternTree.GetItemText(item)
716                if name.startswith('HKLF ') and name not in HKLFlist:
717                    HKLFlist.append(name)
718                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
719        # look up which format was requested
720        reqrdr = self.ImportMenuId.get(event.GetId())
721        rdlist = self.OnImportGeneric(reqrdr,self.ImportSfactReaderlist,
722            'Structure Factor',multiple=True)
723        if len(rdlist) == 0: return
724        self.CheckNotebook()
725        newHistList = []
726        for rd in rdlist:
727            HistName = rd.objname
728            if len(rdlist) <= 2: 
729                dlg = wx.TextEntryDialog( # allow editing of Structure Factor name
730                    self, 'Enter the name for the new Structure Factor',
731                    'Edit Structure Factor name', HistName,
732                    style=wx.OK)
733                dlg.CenterOnParent()
734                if dlg.ShowModal() == wx.ID_OK:
735                    HistName = dlg.GetValue()
736                dlg.Destroy()
737            HistName = 'HKLF '+HistName
738            # make new histogram names unique
739            if len(rd.Banks):
740                for Bank in rd.Banks:
741                    valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
742                    HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
743                    print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
744                    Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
745                    if not Bank['RefDict'].get('FF'):
746                        Bank['RefDict']['FF'] = [{} for i in range(len(Bank['RefDict']['RefList']))]
747                    self.PatternTree.SetItemPyData(Id,[valuesdict,Bank['RefDict']])
748                    Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
749                    self.PatternTree.SetItemPyData(Sub,copy.copy(rd.Parameters))
750                    self.PatternTree.SetItemPyData(
751                        self.PatternTree.AppendItem(Id,text='Reflection List'),[])  #dummy entry for GUI use
752            else:
753                valuesdict = {'wtFactor':1.0,'Dummy':False,'ranId':ran.randint(0,sys.maxint),}
754                HistName = G2obj.MakeUniqueLabel(HistName,HKLFlist)
755                print 'Read structure factor table '+str(HistName)+' from file '+str(self.lastimport)
756                if not rd.RefDict.get('FF'):
757                    rd.RefDict['FF'] = [{} for i in range(len(rd.RefDict['RefList']))]
758                Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
759                self.PatternTree.SetItemPyData(Id,[valuesdict,rd.RefDict])
760                Sub = self.PatternTree.AppendItem(Id,text='Instrument Parameters')
761                self.PatternTree.SetItemPyData(Sub,rd.Parameters)
762                self.PatternTree.SetItemPyData(
763                    self.PatternTree.AppendItem(Id,text='Reflection List'),[])  #dummy entry for GUI use
764               
765            self.PatternTree.SelectItem(Id)
766            self.PatternTree.Expand(Id)
767            self.Sngl = True
768            newHistList.append(HistName)
769
770        if not newHistList: return # somehow, no new histograms
771        # make a list of phase names
772        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
773        phaseNameList = usedHistograms.keys() # phase names in use
774        if not phaseNameList: return # no phases yet, nothing to do
775        header = 'Select phase(s) to add the new\nsingle crystal dataset(s) to:'
776        for Name in newHistList:
777            header += '\n  '+str(Name)
778        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
779        if not result: return
780        # connect new phases to histograms
781        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
782        if not sub:
783            raise Exception('ERROR -- why are there no phases here?')
784        wx.BeginBusyCursor()
785        item, cookie = self.PatternTree.GetFirstChild(sub)
786        iph = -1
787        while item: # loop over (new) phases
788            iph += 1
789            phaseName = self.PatternTree.GetItemText(item)
790            data = self.PatternTree.GetItemPyData(item)
791            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
792            if iph not in result: continue
793            generalData = data['General']
794            SGData = generalData['SGData']
795            UseList = data['Histograms']
796            for histoName in newHistList:
797                #redo UpdateHKLFdata(histoName) here:
798                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
799                refDict,reflData = self.PatternTree.GetItemPyData(Id)
800                UseList[histoName] = SetDefaultDData(refDict['Type'],histoName)
801                G,g = G2lat.cell2Gmat(generalData['Cell'][1:7])
802                for iref,ref in enumerate(reflData['RefList']):
803                    H = list(ref[:3])
804                    ref[4] = np.sqrt(1./G2lat.calc_rDsq2(H,G))
805                    iabsnt,ref[3],Uniq,phi = G2spc.GenHKLf(H,SGData)
806        wx.EndBusyCursor()
807       
808        return # success
809
810    def _Add_ImportMenu_powder(self,parent):
811        '''configure the Powder Data menus accord to the readers found in _init_Imports
812        '''
813        submenu = wx.Menu()
814        item = parent.AppendMenu(wx.ID_ANY, 'Powder Data',
815            submenu, help='Import Powder data')
816        for reader in self.ImportPowderReaderlist:
817            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
818                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
819            self.ImportMenuId[item.GetId()] = reader
820            self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
821        item = submenu.Append(wx.ID_ANY,
822            help='Import powder data, use file to try to determine format',
823            kind=wx.ITEM_NORMAL,text='guess format from file')
824        self.Bind(wx.EVT_MENU, self.OnImportPowder, id=item.GetId())
825        submenu.AppendSeparator()
826        item = submenu.Append(wx.ID_ANY,
827            help='Create a powder data set entry that will be simulated',
828            kind=wx.ITEM_NORMAL,text='Simulate a dataset')
829        self.Bind(wx.EVT_MENU, self.OnDummyPowder, id=item.GetId())
830           
831    def ReadPowderInstprm(self,instfile):       #fix the write routine for [inst1,inst2] style
832        '''Read a GSAS-II (new) instrument parameter file
833
834        :param str instfile: name of instrument parameter file
835
836        '''
837        if os.path.splitext(instfile)[1].lower() != '.instprm': # invalid file
838            return None           
839        if not os.path.exists(instfile): # no such file
840            return None
841        File = open(instfile,'r')
842        S = File.readline()
843        if not S.startswith('#GSAS-II'): # not a valid file
844            File.close()
845            return None
846        newItems = []
847        newVals = []
848        while S:
849            if S[0] == '#':
850                S = File.readline()
851                continue
852            [item,val] = S[:-1].split(':')
853            newItems.append(item)
854            try:
855                newVals.append(float(val))
856            except ValueError:
857                newVals.append(val)                       
858            S = File.readline()               
859        File.close()
860        # add a second MT dict here. TOF parms? (BHT)
861        return G2IO.makeInstDict(newItems,newVals,len(newVals)*[False,]),{}
862       
863    def ReadPowderIparm(self,instfile,bank,databanks,rd):
864        '''Read a GSAS (old) instrument parameter file
865
866        :param str instfile: name of instrument parameter file
867
868        :param int bank: the bank number read in the raw data file
869
870        :param int databanks: the number of banks in the raw data file.
871          If the number of banks in the data and instrument parameter files
872          agree, then the sets of banks are assumed to match up and bank
873          is used to select the instrument parameter file. If not, the user
874          is asked to make a selection.
875
876        :param obj rd: the raw data (histogram) data object. This
877          sets rd.instbank.
878
879        '''
880        if not os.path.exists(instfile): # no such file
881            return {}
882        fp = 0
883        try:
884            fp = open(instfile,'Ur')
885            Iparm = {}
886            for S in fp:
887                Iparm[S[:12]] = S[12:-1]
888        except IOError:
889            print('Error reading file:'+str(instfile))
890        if fp:       
891            fp.close()
892
893        ibanks = int(Iparm.get('INS   BANK  ','1').strip())
894        hType = Iparm['INS   HTYPE '].strip()
895        if ibanks == 1: # there is only one bank here, return it
896            rd.instbank = 1
897            return Iparm
898        if 'PNT' in hType:
899            rd.instbank = bank
900        elif ibanks != databanks:
901            # number of banks in data and prm file not not agree, need a
902            # choice from a human here
903            choices = []
904            for i in range(1,1+ibanks):
905                choices.append('Bank '+str(i))
906            bank = rd.BlockSelector(
907                choices, self,
908                title='Select an instrument parameter bank for '+
909                os.path.split(rd.powderentry[0])[1]+' BANK '+str(bank)+
910                '\nOr use Cancel to select from the default parameter sets',
911                header='Block Selector')
912        if bank is None: return {}
913        # pull out requested bank # bank from the data, and change the bank to 1
914        IparmS = {}
915        for key in Iparm:
916            if key[4:6] == "  ":
917                IparmS[key] = Iparm[key]
918            elif int(key[4:6].strip()) == bank:
919                IparmS[key[:4]+' 1'+key[6:]] = Iparm[key]
920        rd.instbank = bank
921        return IparmS
922                       
923    def GetPowderIparm(self,rd, prevIparm, lastIparmfile, lastdatafile):
924        '''Open and read an instrument parameter file for a data file
925        Returns the list of parameters used in the data tree
926
927        :param obj rd: the raw data (histogram) data object.
928
929        :param str prevIparm: not used
930
931        :param str lastIparmfile: Name of last instrument parameter
932          file that was read, or a empty string.
933
934        :param str lastdatafile: Name of last data file that was read.
935
936        :returns: a list of two dicts, the first containing instrument parameters
937          and the second used for future TOF datasets (timemaps?)
938
939        '''
940        def SetPowderInstParms(Iparm, rd):
941            '''extracts values from instrument parameters in rd.instdict
942            or in array Iparm.
943            Create and return the contents of the instrument parameter tree entry.
944            '''
945            DataType = Iparm['INS   HTYPE '].strip()[:3]  # take 1st 3 chars
946            # override inst values with values read from data file
947            if rd.instdict.get('type'):
948                DataType = rd.instdict.get('type')
949            data = [DataType,]
950            instname = Iparm.get('INS  1INAME ')
951            if instname:
952                rd.Sample['InstrName'] = instname.strip()
953            if 'C' in DataType:
954                wave1 = None
955                wave2 = 0.0
956                if rd.instdict.get('wave'):
957                    wl = rd.instdict.get('wave')
958                    wave1 = wl[0]
959                    if len(wl) > 1: wave2 = wl[1]
960                s = Iparm['INS  1 ICONS']
961                if not wave1:
962                    wave1 = G2IO.sfloat(s[:10])
963                    wave2 = G2IO.sfloat(s[10:20])
964                v = (wave1,wave2,
965                     G2IO.sfloat(s[20:30]),G2IO.sfloat(s[55:65]),G2IO.sfloat(s[40:50])) #get lam1, lam2, zero, pola & ratio
966                if not v[1]:
967                    names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
968                    v = (v[0],v[2],v[4])
969                    codes = [0,0,0,0]
970                else:
971                    names = ['Type','Lam1','Lam2','Zero','I(L2)/I(L1)','Polariz.','U','V','W','X','Y','SH/L','Azimuth']
972                    codes = [0,0,0,0,0,0]
973                data.extend(v)
974                if 'INS  1PRCF  ' in Iparm:
975                    v1 = Iparm['INS  1PRCF  '].split()                                                 
976                    v = Iparm['INS  1PRCF 1'].split()
977                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
978                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
979                    v = Iparm['INS  1PRCF 2'].split()
980                    if v1[0] == 3:
981                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
982                    else:
983                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file                   
984                else:
985                    v1 = Iparm['INS  1PRCF1 '].split()                                                 
986                    v = Iparm['INS  1PRCF11'].split()
987                    data.extend([float(v[0]),float(v[1]),float(v[2])])                  #get GU, GV & GW - always here
988                    azm = float(Iparm.get('INS  1DETAZM','0.0'))
989                    v = Iparm['INS  1PRCF12'].split()
990                    if v1[0] == 3:
991                        data.extend([float(v[0]),float(v[1]),float(v[2])+float(v[3],azm)])  #get LX, LY, S+H/L & azimuth
992                    else:
993                        data.extend([0.0,0.0,0.002,azm])                                      #OK defaults if fxn #3 not 1st in iprm file
994                codes.extend([0,0,0,0,0,0,0])
995                return [G2IO.makeInstDict(names,data,codes),{}]
996            elif 'T' in DataType:
997                names = ['Type','2-theta','difC','difA','difB','Zero','alpha','beta-0','beta-1',
998                    'beta-q','sig-0','sig-1','sig-q','X','Y','Azimuth']
999                codes = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
1000                azm = 0.
1001                if 'INS  1DETAZM' in Iparm:
1002                    azm = float(Iparm['INS  1DETAZM'])
1003                s = Iparm['INS  1BNKPAR'].split()
1004                data.extend([G2IO.sfloat(s[1]),])               #2-theta for bank
1005                s = Iparm['INS  1 ICONS'].split()
1006                data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),0.0,G2IO.sfloat(s[2])])    #difC,difA,difB,Zero
1007                if 'INS  1PRCF  ' in Iparm:
1008                    s = Iparm['INS  1PRCF  '].split()
1009                    pfType = int(s[0])
1010                    s = Iparm['INS  1PRCF 1'].split()
1011                    if abs(pfType) == 1:
1012                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])])
1013                        s = Iparm['INS  1PRCF 2'].split()
1014                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])
1015                    elif abs(pfType) in [3,4,5]:
1016                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])
1017                        if abs(pfType) == 4:
1018                            data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,azm])
1019                        else:
1020                            s = Iparm['INS  1PRCF 2'].split()
1021                            data.extend([0.0,0.0,G2IO.sfloat(s[0]),0.0,0.0,0.0,azm])                       
1022                else:
1023                    s = Iparm['INS  1PRCF1 '].split()
1024                    pfType = int(s[0])
1025                    s = Iparm['INS  1PRCF11'].split()
1026                    if abs(pfType) == 1:
1027                        data.extend([G2IO.sfloat(s[1]),G2IO.sfloat(s[2]),G2IO.sfloat(s[3])])
1028                        s = Iparm['INS  1PRCF12'].split()
1029                        data.extend([0.0,0.0,G2IO.sfloat(s[1]),0.0,0.0,0.0,azm])
1030                    elif abs(pfType) in [3,4,5]:
1031                        data.extend([G2IO.sfloat(s[0]),G2IO.sfloat(s[1]),G2IO.sfloat(s[2])])
1032                        if abs(pfType) == 4:
1033                            data.extend([0.0,0.0,G2IO.sfloat(s[3]),0.0,0.0,0.0,azm])
1034                        else:
1035                            s = Iparm['INS  1PRCF12'].split()
1036                            data.extend([0.0,0.0,G2IO.sfloat(s[0]),0.0,0.0,0.0,azm])                       
1037                Inst1 = G2IO.makeInstDict(names,data,codes)
1038                Inst2 = {}
1039                if pfType < 0:
1040                    Ipab = 'INS  1PAB'+str(-pfType)
1041                    Npab = int(Iparm[Ipab+'  '].strip())
1042                    Inst2['Pdabc'] = []
1043                    for i in range(Npab):
1044                        k = Ipab+str(i+1).rjust(2)
1045                        s = Iparm[k].split()
1046                        Inst2['Pdabc'].append([float(t) for t in s])
1047                    Inst2['Pdabc'] = np.array(Inst2['Pdabc'])
1048                    Inst2['Pdabc'].T[3] += Inst2['Pdabc'].T[0]*Inst1['difC'][0] #turn 3rd col into TOF
1049                if 'INS  1I ITYP' in Iparm:
1050                    s = Iparm['INS  1I ITYP'].split()
1051                    Ityp = int(s[0])
1052                    Tminmax = [float(s[1])*1000.,float(s[2])*1000.]
1053                    Itypes = ['Exponential','Maxwell/Exponential','','Maxwell/Chebyschev','']
1054                    if Ityp in [1,2,4]:
1055                        Inst2['Itype'] = Itypes[Ityp-1]
1056                        Inst2['Tminmax'] = Tminmax
1057                        Icoeff = []
1058                        Iesd = []
1059                        Icovar = []                   
1060                        for i in range(3):
1061                            s = Iparm['INS  1ICOFF'+str(i+1)].split()
1062                            Icoeff += [float(S) for S in s]
1063                            s = Iparm['INS  1IECOF'+str(i+1)].split()
1064                            Iesd += [float(S) for S in s]
1065                        NT = 10
1066                        for i in range(8):
1067                            s = Iparm['INS  1IECOR'+str(i+1)]
1068                            if i == 7:
1069                                NT = 8
1070                            Icovar += [float(s[6*j:6*j+6]) for j in range(NT)]
1071                        Inst2['Icoeff'] = Icoeff
1072                        Inst2['Iesd'] = Iesd
1073                        Inst2['Icovar'] = Icovar
1074                return [Inst1,Inst2]
1075
1076        # stuff we might need from the reader
1077        filename = rd.powderentry[0]
1078        bank = rd.powderentry[2]
1079        numbanks = rd.numbanks
1080        # is there an instrument parameter file defined for the current data set?
1081        # or if this is a read on a set of set of files, use the last one again
1082        #if rd.instparm or (lastdatafile == filename and lastIparmfile):
1083        if rd.instparm or lastIparmfile:
1084            if rd.instparm:
1085                instfile = os.path.join(os.path.split(filename)[0],
1086                                    rd.instparm)
1087            else:
1088                # for multiple reads of one data file, reuse the inst parm file
1089                instfile = lastIparmfile
1090            if os.path.exists(instfile):
1091                #print 'debug: try read',instfile
1092                instParmList = self.ReadPowderInstprm(instfile)
1093                if instParmList is not None:
1094                    rd.instfile = instfile
1095                    rd.instmsg = 'GSAS-II file '+instfile
1096                    return instParmList
1097                Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1098                if Iparm:
1099                    #print 'debug: success'
1100                    rd.instfile = instfile
1101                    rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1102                    return SetPowderInstParms(Iparm,rd)
1103            else:
1104                self.ErrorDialog('Open Error','Error opening instrument parameter file '
1105                    +str(instfile)+' requested by file '+ filename)
1106        # is there an instrument parameter file matching the current file
1107        # with extension .inst or .prm? If so read it
1108        basename = os.path.splitext(filename)[0]
1109        for ext in '.instprm','.prm','.inst','.ins':
1110            instfile = basename + ext
1111            instParmList = self.ReadPowderInstprm(instfile)
1112            if instParmList is not None:
1113                rd.instfile = instfile
1114                rd.instmsg = 'GSAS-II file '+instfile
1115                return instParmList
1116            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1117            if Iparm:
1118                #print 'debug: success'
1119                rd.instfile = instfile
1120                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1121                return SetPowderInstParms(Iparm,rd)
1122            else:
1123                #print 'debug: open/read failed',instfile
1124                pass # fail silently
1125
1126        # did we read the data file from a zip? If so, look there for a
1127        # instrument parameter file
1128        if self.zipfile:
1129            for ext in '.instprm','.prm','.inst','.ins':
1130                instfile = G2IO.ExtractFileFromZip(
1131                    self.zipfile,
1132                    selection=os.path.split(basename + ext)[1],
1133                    parent=self)
1134                if instfile is not None and instfile != self.zipfile:
1135                    print 'debug:',instfile,'created from ',self.zipfile
1136                    instParmList = self.ReadPowderInstprm(instfile)
1137                    if instParmList is not None:
1138                        rd.instfile = instfile
1139                        rd.instmsg = 'GSAS-II file '+instfile
1140                        return instParmList
1141                    Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1142                    if Iparm:
1143                        rd.instfile = instfile
1144                        rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1145                        return SetPowderInstParms(Iparm,rd)
1146                    else:
1147                        #print 'debug: open/read for',instfile,'from',self.zipfile,'failed'
1148                        pass # fail silently
1149
1150        while True: # loop until we get a file that works or we get a cancel
1151            instfile = ''
1152            dlg = wx.FileDialog(
1153                self,
1154                'Choose inst. param file for "'
1155                +rd.idstring
1156                +'" (or Cancel for default)',
1157                '.', '',
1158                'GSAS iparm file (*.prm,*.inst,*.ins)|*.prm;*.inst;*.ins|'
1159                'GSAS-II iparm file (*.instprm)|*.instprm|'
1160                'All files (*.*)|*.*', 
1161                wx.OPEN|wx.CHANGE_DIR)
1162            if os.path.exists(lastIparmfile):
1163                dlg.SetFilename(lastIparmfile)
1164            if dlg.ShowModal() == wx.ID_OK:
1165                instfile = dlg.GetPath()
1166            dlg.Destroy()
1167            if not instfile: break
1168            instParmList = self.ReadPowderInstprm(instfile)
1169            if instParmList is not None:
1170                rd.instfile = instfile
1171                rd.instmsg = 'GSAS-II file '+instfile
1172                return instParmList
1173            Iparm = self.ReadPowderIparm(instfile,bank,numbanks,rd)
1174            if Iparm:
1175                #print 'debug: success with',instfile
1176                rd.instfile = instfile
1177                rd.instmsg = instfile + ' bank ' + str(rd.instbank)
1178                return SetPowderInstParms(Iparm,rd)
1179            else:
1180                self.ErrorDialog('Read Error',
1181                                 'Error opening/reading file '+str(instfile))
1182       
1183        # still no success: offer user choice of defaults
1184        while True: # loop until we get a choice
1185            choices = []
1186            head = 'Select from default instrument parameters for '+rd.idstring
1187
1188            for l in rd.defaultIparm_lbl:
1189                choices.append('Defaults for '+l)
1190            res = rd.BlockSelector(
1191                choices,
1192                ParentFrame=self,
1193                title=head,
1194                header='Select default inst parms',
1195                useCancel=False)
1196            if res is None: continue
1197            rd.instfile = ''
1198            rd.instmsg = 'default: '+rd.defaultIparm_lbl[res]
1199            return SetPowderInstParms(rd.defaultIparms[res],rd)
1200
1201    def OnImportPowder(self,event):
1202        '''Called in response to an Import/Powder Data/... menu item
1203        to read a powder diffraction data set.
1204        dict self.ImportMenuId is used to look up the specific
1205        reader item associated with the menu item, which will be
1206        None for the last menu item, which is the "guess" option
1207        where all appropriate formats will be tried.
1208
1209        Also reads an instrument parameter file for each dataset.
1210        '''
1211        # get a list of existing histograms
1212        PWDRlist = []
1213        if self.PatternTree.GetCount():
1214            item, cookie = self.PatternTree.GetFirstChild(self.root)
1215            while item:
1216                name = self.PatternTree.GetItemText(item)
1217                if name.startswith('PWDR ') and name not in PWDRlist:
1218                    PWDRlist.append(name)
1219                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1220        # look up which format was requested
1221        reqrdr = self.ImportMenuId.get(event.GetId()) 
1222        rdlist = self.OnImportGeneric(
1223            reqrdr,self.ImportPowderReaderlist,'Powder Data',multiple=True)
1224        if len(rdlist) == 0: return
1225        self.CheckNotebook()
1226        Iparm = None
1227        lastIparmfile = ''
1228        lastdatafile = ''
1229        newHistList = []
1230        self.EnablePlot = False
1231        for rd in rdlist:
1232            # get instrument parameters for each dataset
1233            Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1234            if rd.repeat_instparm: 
1235                lastIparmfile = rd.instfile
1236            lastdatafile = rd.powderentry[0]
1237            HistName = rd.idstring
1238            HistName = 'PWDR '+HistName
1239            # make new histogram names unique
1240            HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)
1241            print 'Read powder data '+str(HistName)+ \
1242                ' from file '+str(rd.readfilename) + \
1243                ' with parameters from '+str(rd.instmsg)
1244            # data are read, now store them in the tree
1245            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1246            if 'T' in Iparm1['Type'][0]:
1247                if not rd.clockWd and rd.GSAS:
1248                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1249                cw = np.diff(rd.powderdata[0])
1250                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1251                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1252                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1253                if 'Itype' in Iparm2:
1254                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1255                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1256                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1257                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1258                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1259                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1260                    var += WYI*rd.powderdata[1]**2
1261                    var /= YI**2
1262                    rd.powderdata[2] = 1./var
1263                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1264                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1265                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1266            Tmin = min(rd.powderdata[0])
1267            Tmax = max(rd.powderdata[0])
1268            valuesdict = {
1269                'wtFactor':1.0,
1270                'Dummy':False,
1271                'ranId':ran.randint(0,sys.maxint),
1272                }
1273            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1274            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1275            self.PatternTree.SetItemPyData(
1276                self.PatternTree.AppendItem(Id,text='Comments'),
1277                rd.comments)
1278            self.PatternTree.SetItemPyData(
1279                self.PatternTree.AppendItem(Id,text='Limits'),
1280                [(Tmin,Tmax),[Tmin,Tmax]])
1281            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1282            self.PatternTree.SetItemPyData(
1283                self.PatternTree.AppendItem(Id,text='Background'),
1284                [['chebyschev',True,3,1.0,0.0,0.0],
1285                 {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1286            self.PatternTree.SetItemPyData(
1287                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1288                [Iparm1,Iparm2])
1289            self.PatternTree.SetItemPyData(
1290                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1291                rd.Sample)
1292            self.PatternTree.SetItemPyData(
1293                self.PatternTree.AppendItem(Id,text='Peak List')
1294                ,{'peaks':[],'sigDict':{}})
1295            self.PatternTree.SetItemPyData(
1296                self.PatternTree.AppendItem(Id,text='Index Peak List'),
1297                [[],[]])
1298            self.PatternTree.SetItemPyData(
1299                self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1300                [])
1301            self.PatternTree.SetItemPyData(
1302                self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1303                {})
1304            newHistList.append(HistName)
1305        else:
1306            self.EnablePlot = True
1307            self.PatternTree.Expand(Id)
1308            self.PatternTree.SelectItem(Id)
1309           
1310        if not newHistList: return # somehow, no new histograms
1311        # make a list of phase names
1312        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1313        phaseNameList = usedHistograms.keys() # phase names in use
1314        if not phaseNameList: return # no phases yet, nothing to do
1315        header = 'Select phase(s) to add the new\npowder dataset(s) to:'
1316        for Name in newHistList:
1317            header += '\n  '+str(Name)
1318
1319        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1320        if not result: return
1321        # connect new phases to histograms
1322        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1323        if not sub:
1324            raise Exception('ERROR -- why are there no phases here?')
1325        item, cookie = self.PatternTree.GetFirstChild(sub)
1326        iph = -1
1327        while item: # loop over (new) phases
1328            iph += 1
1329            phaseName = self.PatternTree.GetItemText(item)
1330            data = self.PatternTree.GetItemPyData(item)
1331            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1332            if iph not in result: continue
1333            generalData = data['General']
1334            SGData = generalData['SGData']
1335            UseList = data['Histograms']
1336            NShkl = len(G2spc.MustrainNames(SGData))
1337            NDij = len(G2spc.HStrainNames(SGData))
1338            for histoName in newHistList:
1339                UseList[histoName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1340                Id = G2gd.GetPatternTreeItemId(self,self.root,histoName)
1341                refList = self.PatternTree.GetItemPyData(
1342                    G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1343                refList[generalData['Name']] = []
1344        return # success
1345
1346    def OnDummyPowder(self,event):
1347        '''Called in response to Import/Powder Data/Simulate menu item
1348        to create a Dummy powder diffraction data set.
1349
1350        Reads an instrument parameter file and then gets input from the user
1351        '''
1352        # get a list of existing histograms
1353        PWDRlist = []
1354        if self.PatternTree.GetCount():
1355            item, cookie = self.PatternTree.GetFirstChild(self.root)
1356            while item:
1357                name = self.PatternTree.GetItemText(item)
1358                if name.startswith('PWDR ') and name not in PWDRlist:
1359                    PWDRlist.append(name)
1360                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1361        # Initialize a base class reader
1362        rd = G2IO.ImportPowderData(
1363            extensionlist=tuple(),
1364            strictExtension=False,
1365            formatName = 'Simulate dataset',
1366            longFormatName = 'Compute a simulated pattern')
1367        rd.powderentry[0] = '' # no filename
1368        # #self.powderentry[1] = pos # bank offset (N/A here)
1369        rd.powderentry[2] = 1 # only one bank
1370        rd.comments.append('This is a dummy dataset for powder pattern simulation')
1371        self.CheckNotebook()
1372        Iparm = None
1373        lastIparmfile = ''
1374        lastdatafile = ''
1375        self.zipfile = None
1376        # get instrument parameters for it
1377        Iparm1,Iparm2 = self.GetPowderIparm(rd, Iparm, lastIparmfile, lastdatafile)
1378        if 'T' in Iparm1['Type'][0]:
1379            print('TOF simulation not supported yet')
1380            return False
1381        else:
1382            # need to get name, 2theta start, end, step
1383            rd.idstring = ' CW'
1384            if 'X' in Iparm1['Type'][0]:
1385                rd.idstring = 'CW x-ray simulation'
1386            else:
1387                rd.idstring = 'CW neutron simulation'
1388            # base initial range on wavelength
1389            wave = Iparm1.get('Lam')
1390            if wave:
1391                wave = wave[0]
1392            else:
1393                wave = Iparm1.get('Lam1')
1394                if wave:
1395                    wave = wave[0]
1396        N = 0
1397        while (N < 3): # insist on a dataset with a few points
1398            names = ('dataset name', 'start angle', 'end angle', 'step size')
1399            if not wave or wave < 1.0:
1400                inp = [rd.idstring, 10.,40.,0.005] # see names for what's what
1401            else:
1402                inp = [rd.idstring, 10.,80.,0.01] # see names for what's what
1403            dlg = G2gd.ScrolledMultiEditor(
1404                self,[inp] * len(inp),range(len(inp)),names,
1405                header='Enter simulation name and range',
1406                minvals=(None,0.001,0.001,0.0001),
1407                maxvals=(None,180.,180.,.1),
1408                sizevals=((225,-1),)
1409                )
1410            dlg.CenterOnParent()
1411            if dlg.ShowModal() == wx.ID_OK:
1412                if inp[1] > inp[2]:
1413                    end,start,step = inp[1:]
1414                else:               
1415                    start,end,step = inp[1:]
1416                step = abs(step)
1417            else:
1418                return False
1419            N = int((end-start)/step)+1
1420            x = np.linspace(start,end,N,True)
1421            N = len(x)
1422        rd.powderdata = [
1423            np.array(x), # x-axis values
1424            np.zeros_like(x), # powder pattern intensities
1425            np.ones_like(x), # 1/sig(intensity)^2 values (weights)
1426            np.zeros_like(x), # calc. intensities (zero)
1427            np.zeros_like(x), # calc. background (zero)
1428            np.zeros_like(x), # obs-calc profiles
1429            ]
1430        Tmin = rd.powderdata[0][0]
1431        Tmax = rd.powderdata[0][-1]
1432        # data are read, now store them in the tree
1433        HistName = inp[0]
1434        HistName = 'PWDR '+HistName
1435        HistName = G2obj.MakeUniqueLabel(HistName,PWDRlist)  # make new histogram names unique
1436        Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1437        valuesdict = {
1438            'wtFactor':1.0,
1439            'Dummy':True,
1440            'ranId':ran.randint(0,sys.maxint),
1441            }
1442        self.PatternTree.SetItemPyData(Id,[valuesdict,rd.powderdata])
1443        self.PatternTree.SetItemPyData(
1444            self.PatternTree.AppendItem(Id,text='Comments'),
1445            rd.comments)
1446        self.PatternTree.SetItemPyData(
1447            self.PatternTree.AppendItem(Id,text='Limits'),
1448            [(Tmin,Tmax),[Tmin,Tmax]])
1449        self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1450        self.PatternTree.SetItemPyData(
1451            self.PatternTree.AppendItem(Id,text='Background'),
1452            [['chebyschev',True,3,1.0,0.0,0.0],
1453             {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
1454        self.PatternTree.SetItemPyData(
1455            self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1456            [Iparm1,Iparm2])
1457        self.PatternTree.SetItemPyData(
1458            self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1459            rd.Sample)
1460        self.PatternTree.SetItemPyData(
1461            self.PatternTree.AppendItem(Id,text='Peak List')
1462            ,{'peaks':[],'sigDict':{}})
1463        self.PatternTree.SetItemPyData(
1464            self.PatternTree.AppendItem(Id,text='Index Peak List'),
1465            [[],[]])
1466        self.PatternTree.SetItemPyData(
1467            self.PatternTree.AppendItem(Id,text='Unit Cells List'),
1468            [])
1469        self.PatternTree.SetItemPyData(
1470            self.PatternTree.AppendItem(Id,text='Reflection Lists'),
1471            {})
1472        self.PatternTree.Expand(Id)
1473        self.PatternTree.SelectItem(Id)
1474        print('Added simulation powder data '+str(HistName)+ 
1475              ' with parameters from '+str(rd.instmsg))
1476
1477        # make a list of phase names
1478        phaseRIdList,usedHistograms = self.GetPhaseInfofromTree()
1479        phaseNameList = usedHistograms.keys() # phase names in use
1480        if not phaseNameList: return # no phases yet, nothing to do
1481        header = 'Select phase(s) to add the new\npowder simulation (dummy) dataset to:'
1482        result = G2gd.ItemSelector(phaseNameList,self,header,header='Add to phase(s)',multiple=True)
1483        if not result: return
1484        # connect new phases to histograms
1485        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1486        if not sub:
1487            raise Exception('ERROR -- why are there no phases here?')
1488        item, cookie = self.PatternTree.GetFirstChild(sub)
1489        iph = -1
1490        while item: # loop over (new) phases
1491            iph += 1
1492            phaseName = self.PatternTree.GetItemText(item)
1493            data = self.PatternTree.GetItemPyData(item)
1494            item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1495            if iph not in result: continue
1496            generalData = data['General']
1497            SGData = generalData['SGData']
1498            UseList = data['Histograms']
1499            NShkl = len(G2spc.MustrainNames(SGData))
1500            NDij = len(G2spc.HStrainNames(SGData))
1501            UseList[HistName] = SetDefaultDData('PWDR',histoName,NShkl=NShkl,NDij=NDij)
1502            Id = G2gd.GetPatternTreeItemId(self,self.root,HistName)
1503            refList = self.PatternTree.GetItemPyData(
1504                G2gd.GetPatternTreeItemId(self,Id,'Reflection Lists'))
1505            refList[generalData['Name']] = []
1506        return # success
1507
1508    def _Add_ImportMenu_smallangle(self,parent):
1509        '''configure the Small Angle Data menus accord to the readers found in _init_Imports
1510        '''
1511        submenu = wx.Menu()
1512        item = parent.AppendMenu(wx.ID_ANY, 'Small Angle Data',
1513            submenu, help='Import small angle data')
1514        for reader in self.ImportSmallAngleReaderlist:
1515            item = submenu.Append(wx.ID_ANY,help=reader.longFormatName,
1516                kind=wx.ITEM_NORMAL,text='from '+reader.formatName+' file')
1517            self.ImportMenuId[item.GetId()] = reader
1518            self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1519        # item = submenu.Append(wx.ID_ANY,
1520        #     help='Import small angle data, use file to try to determine format',
1521        #     kind=wx.ITEM_NORMAL,text='guess format from file')
1522        # self.Bind(wx.EVT_MENU, self.OnImportSmallAngle, id=item.GetId())
1523
1524    def OnImportSmallAngle(self,event):
1525        '''Called in response to an Import/Small Angle Data/... menu item
1526        to read a small angle diffraction data set.
1527        dict self.ImportMenuId is used to look up the specific
1528        reader item associated with the menu item, which will be
1529        None for the last menu item, which is the "guess" option
1530        where all appropriate formats will be tried.
1531
1532        '''
1533       
1534        def GetSASDIparm(reader):
1535            parm = reader.instdict
1536            Iparm = {'Type':[parm['type'],parm['type'],0],'Lam':[parm['wave'],
1537                parm['wave'],0],'Azimuth':[0.,0.,0]}           
1538            return Iparm,{}
1539           
1540        # get a list of existing histograms
1541        SASDlist = []
1542        if self.PatternTree.GetCount():
1543            item, cookie = self.PatternTree.GetFirstChild(self.root)
1544            while item:
1545                name = self.PatternTree.GetItemText(item)
1546                if name.startswith('SASD ') and name not in SASDlist:
1547                    SASDlist.append(name)
1548                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
1549        # look up which format was requested
1550        reqrdr = self.ImportMenuId.get(event.GetId()) 
1551        rdlist = self.OnImportGeneric(
1552            reqrdr,self.ImportSmallAngleReaderlist,'Small Angle Data',multiple=True)
1553        if len(rdlist) == 0: return
1554        self.CheckNotebook()
1555        Iparm = None
1556        lastdatafile = ''
1557        newHistList = []
1558        self.EnablePlot = False
1559        for rd in rdlist:
1560            lastdatafile = rd.smallangleentry[0]
1561            HistName = rd.idstring
1562            HistName = 'SASD '+HistName
1563            # make new histogram names unique
1564            HistName = G2obj.MakeUniqueLabel(HistName,SASDlist)
1565            print 'Read small angle data '+str(HistName)+ \
1566                ' from file '+str(self.lastimport)
1567            # data are read, now store them in the tree
1568            Id = self.PatternTree.AppendItem(parent=self.root,text=HistName)
1569            Iparm1,Iparm2 = GetSASDIparm(rd)
1570#            if 'T' in Iparm1['Type'][0]:
1571#                if not rd.clockWd and rd.GSAS:
1572#                    rd.powderdata[0] *= 100.        #put back the CW centideg correction
1573#                cw = np.diff(rd.powderdata[0])
1574#                rd.powderdata[0] = rd.powderdata[0][:-1]+cw/2.
1575#                rd.powderdata[1] = rd.powderdata[1][:-1]/cw
1576#                rd.powderdata[2] = rd.powderdata[2][:-1]*cw**2  #1/var=w at this point
1577#                if 'Itype' in Iparm2:
1578#                    Ibeg = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][0])
1579#                    Ifin = np.searchsorted(rd.powderdata[0],Iparm2['Tminmax'][1])
1580#                    rd.powderdata[0] = rd.powderdata[0][Ibeg:Ifin]
1581#                    YI,WYI = G2pwd.calcIncident(Iparm2,rd.powderdata[0])
1582#                    rd.powderdata[1] = rd.powderdata[1][Ibeg:Ifin]/YI
1583#                    var = 1./rd.powderdata[2][Ibeg:Ifin]
1584#                    var += WYI*rd.powderdata[1]**2
1585#                    var /= YI**2
1586#                    rd.powderdata[2] = 1./var
1587#                rd.powderdata[3] = np.zeros_like(rd.powderdata[0])                                       
1588#                rd.powderdata[4] = np.zeros_like(rd.powderdata[0])                                       
1589#                rd.powderdata[5] = np.zeros_like(rd.powderdata[0])                                       
1590            Tmin = min(rd.smallangledata[0])
1591            Tmax = max(rd.smallangledata[0])
1592            valuesdict = {
1593                'wtFactor':1.0,
1594                'Dummy':False,
1595                'ranId':ran.randint(0,sys.maxint),
1596                }
1597            rd.Sample['ranId'] = valuesdict['ranId'] # this should be removed someday
1598            self.PatternTree.SetItemPyData(Id,[valuesdict,rd.smallangledata])
1599            self.PatternTree.SetItemPyData(
1600                self.PatternTree.AppendItem(Id,text='Comments'),
1601                rd.comments)
1602            self.PatternTree.SetItemPyData(
1603                self.PatternTree.AppendItem(Id,text='Limits'),
1604                [(Tmin,Tmax),[Tmin,Tmax]])
1605            self.PatternId = G2gd.GetPatternTreeItemId(self,Id,'Limits')
1606            self.PatternTree.SetItemPyData(
1607                self.PatternTree.AppendItem(Id,text='Instrument Parameters'),
1608                [Iparm1,Iparm2])
1609            self.PatternTree.SetItemPyData(
1610                self.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
1611            self.PatternTree.SetItemPyData(
1612                self.PatternTree.AppendItem(Id,text='Sample Parameters'),
1613                rd.Sample)
1614            self.PatternTree.SetItemPyData(
1615                self.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
1616            newHistList.append(HistName)
1617        else:
1618            self.EnablePlot = True
1619            self.PatternTree.Expand(Id)
1620            self.PatternTree.SelectItem(Id)
1621           
1622        if not newHistList: return # somehow, no new histograms
1623        return # success
1624
1625       
1626    def OnMacroRecordStatus(self,event,setvalue=None):
1627        nextvalue = log.ShowLogStatus() != True
1628        if setvalue is not None:
1629            nextvalue = setvalue
1630        if nextvalue:
1631            log.LogOn()
1632            set2 = True
1633        else:
1634            log.LogOff()
1635            set2 = False
1636        for menuitem in self.MacroStatusList:
1637            menuitem.Check(set2)
1638
1639    def _init_Macro(self):
1640        menu = self.MacroMenu
1641        item = menu.Append(
1642                help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
1643                kind=wx.ITEM_CHECK,text='Record actions')
1644        self.MacroStatusList.append(item)
1645        item.Check(log.ShowLogStatus())
1646        self.Bind(wx.EVT_MENU, self.OnMacroRecordStatus, item)
1647       
1648        item = menu.Append(
1649            help='Show logged commands', id=wx.ID_ANY,
1650            kind=wx.ITEM_NORMAL,text='Show log')
1651        def OnShowLog(event):
1652            print 70*'='
1653            print 'List of logged actions'
1654            for i,line in enumerate(log.G2logList):
1655                if line: print i,line
1656            print 70*'='
1657        self.Bind(wx.EVT_MENU, OnShowLog, item)
1658
1659        item = menu.Append(
1660            help='Clear logged commands', id=wx.ID_ANY,
1661            kind=wx.ITEM_NORMAL,text='Clear log')
1662        def OnClearLog(event): log.G2logList=[None]
1663        self.Bind(wx.EVT_MENU, OnClearLog, item)
1664       
1665        item = menu.Append(
1666            help='Save logged commands to file', id=wx.ID_ANY,
1667            kind=wx.ITEM_NORMAL,text='Save log')
1668        def OnSaveLog(event):
1669            import cPickle
1670            defnam = os.path.splitext(
1671                os.path.split(self.GSASprojectfile)[1]
1672                )[0]+'.gcmd'
1673            dlg = wx.FileDialog(self,
1674                'Choose an file to save past actions', '.', defnam, 
1675                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1676                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1677            dlg.CenterOnParent()
1678            try:
1679                if dlg.ShowModal() == wx.ID_OK:
1680                    filename = dlg.GetPath()
1681                    # make sure extension is correct
1682                    filename = os.path.splitext(filename)[0]+'.gcmd'
1683                else:
1684                    filename = None
1685            finally:
1686                dlg.Destroy()
1687            if filename:
1688                fp = open(filename,'wb')
1689                fp.write(str(len(log.G2logList)-1)+'\n')
1690                for item in log.G2logList:
1691                    if item: cPickle.dump(item,fp)
1692                fp.close()
1693        self.Bind(wx.EVT_MENU, OnSaveLog, item)
1694
1695        item = menu.Append(
1696            help='Load logged commands from file', id=wx.ID_ANY,
1697            kind=wx.ITEM_NORMAL,text='Load log')
1698        def OnLoadLog(event):
1699            # this appends. Perhaps we should ask to clear?
1700            import cPickle
1701            defnam = os.path.splitext(
1702                os.path.split(self.GSASprojectfile)[1]
1703                )[0]+'.gcmd'
1704            dlg = wx.FileDialog(self,
1705                'Choose an file to read saved actions', '.', defnam, 
1706                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1707                wx.OPEN|wx.CHANGE_DIR)
1708            dlg.CenterOnParent()
1709            try:
1710                if dlg.ShowModal() == wx.ID_OK:
1711                    filename = dlg.GetPath()
1712                    # make sure extension is correct
1713                    filename = os.path.splitext(filename)[0]+'.gcmd'
1714                else:
1715                    filename = None
1716            finally:
1717                dlg.Destroy()
1718            if filename and os.path.exists(filename):
1719                fp = open(filename,'rb')
1720                lines = fp.readline()
1721                for i in range(int(lines)):
1722                    log.G2logList.append(cPickle.load(fp))
1723                fp.close()
1724        self.Bind(wx.EVT_MENU, OnLoadLog, item)
1725
1726        item = menu.Append(
1727            help='Replay saved commands', id=wx.ID_ANY,
1728            kind=wx.ITEM_NORMAL,text='Replay log')
1729        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
1730                               
1731    def _init_Exports(self,menu):
1732        '''Find exporter routines and add them into menus
1733        '''
1734        # set up the top-level menus
1735        projectmenu = wx.Menu()
1736        item = menu.AppendMenu(
1737            wx.ID_ANY, 'Entire project as',
1738            projectmenu, help='Export entire project')
1739
1740        phasemenu = wx.Menu()
1741        item = menu.AppendMenu(
1742            wx.ID_ANY, 'Phase as',
1743            phasemenu, help='Export phase or sometimes phases')
1744
1745        powdermenu = wx.Menu()
1746        item = menu.AppendMenu(
1747            wx.ID_ANY, 'Powder data as',
1748            powdermenu, help='Export powder diffraction histogram(s)')
1749
1750        singlemenu = wx.Menu()
1751        item = menu.AppendMenu(
1752            wx.ID_ANY, 'Single crystal data as',
1753            singlemenu, help='Export single crystal histogram(s)')
1754
1755        imagemenu = wx.Menu()
1756        item = menu.AppendMenu(
1757            wx.ID_ANY, 'Image data as',
1758            imagemenu, help='Export powder image(s) data')
1759
1760        mapmenu = wx.Menu()
1761        item = menu.AppendMenu(
1762            wx.ID_ANY, 'Maps as',
1763            mapmenu, help='Export density map(s)')
1764
1765        # pdfmenu = wx.Menu()
1766        # item = menu.AppendMenu(
1767        #     wx.ID_ANY, 'PDFs as',
1768        #     pdfmenu, help='Export pair distribution function(s)')
1769
1770        # find all the exporter files
1771        pathlist = sys.path
1772        filelist = []
1773        for path in pathlist:
1774            for filename in glob.iglob(os.path.join(path,"G2export*.py")):
1775                filelist.append(filename)   
1776        filelist = sorted(list(set(filelist))) # remove duplicates
1777        exporterlist = []
1778        # go through the routines and import them, saving objects that
1779        # have export routines (method Exporter)
1780        for filename in filelist:
1781            path,rootname = os.path.split(filename)
1782            pkg = os.path.splitext(rootname)[0]
1783            try:
1784                fp = None
1785                fp, fppath,desc = imp.find_module(pkg,[path,])
1786                pkg = imp.load_module(pkg,fp,fppath,desc)
1787                for clss in inspect.getmembers(pkg): # find classes defined in package
1788                    if clss[0].startswith('_'): continue
1789                    if inspect.isclass(clss[1]):
1790                        # check if we have the required methods
1791                        for m in 'Exporter','loadParmDict':
1792                            if not hasattr(clss[1],m): break
1793                            if not callable(getattr(clss[1],m)): break
1794                        else:
1795                            exporter = clss[1](self) # create an export instance
1796                            exporterlist.append(exporter)
1797            except AttributeError:
1798                print 'Import_'+errprefix+': Attribute Error'+str(filename)
1799                pass
1800            except ImportError:
1801                print 'Import_'+errprefix+': Error importing file'+str(filename)
1802                pass
1803            if fp: fp.close()
1804        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
1805        for obj in exporterlist:
1806            #print 'exporter',obj
1807            for typ in obj.exporttype:
1808                if typ == "project":
1809                    submenu = projectmenu
1810                elif typ == "phase":
1811                    submenu = phasemenu
1812                elif typ == "powder":
1813                    submenu = powdermenu
1814                elif typ == "single":
1815                    submenu = singlemenu
1816                elif typ == "image":
1817                    submenu = imagemenu
1818                elif typ == "map":
1819                    submenu = mapmenu
1820                # elif typ == "pdf":
1821                #     submenu = pdfmenu
1822                else:
1823                    print("Error, unknown type in "+str(obj))
1824                    break
1825                item = submenu.Append(
1826                    wx.ID_ANY,
1827                    help=obj.longFormatName,
1828                    kind=wx.ITEM_NORMAL,
1829                    text=obj.formatName)
1830                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
1831                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
1832               
1833        #code to debug an Exporter. hard-coded the routine below, to allow a reload before use
1834        # def DebugExport(event):
1835        #      print 'start reload'
1836        #      reload(G2IO)
1837        #      import G2export_pwdr as dev
1838        #      reload(dev)
1839        #      dev.ExportPowderFXYE(self).Exporter(event)
1840        # item = menu.Append(
1841        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1842        #     help="debug exporter",text="test Export FXYE")
1843        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
1844        # # #self.ExportLookup[item.GetId()] = 'image'
1845        # self.ExportLookup[item.GetId()] = 'powder'
1846
1847    def _Add_ExportMenuItems(self,parent):
1848        # item = parent.Append(
1849        #     help='Select PWDR item to enable',id=wx.ID_ANY,
1850        #     kind=wx.ITEM_NORMAL,
1851        #     text='Export Powder Patterns...')
1852        # self.ExportPattern.append(item)
1853        # item.Enable(False)
1854        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1855
1856        item = parent.Append(
1857            help='',id=wx.ID_ANY,
1858            kind=wx.ITEM_NORMAL,
1859            text='Export All Peak Lists...')
1860        self.ExportPeakList.append(item)
1861        item.Enable(True)
1862        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1863
1864        item = parent.Append(
1865            help='',id=wx.ID_ANY,
1866            kind=wx.ITEM_NORMAL,
1867            text='Export HKLs...')
1868        self.ExportHKL.append(item)
1869        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1870
1871        item = parent.Append(
1872            help='Select PDF item to enable',
1873            id=wx.ID_ANY,
1874            kind=wx.ITEM_NORMAL,
1875            text='Export PDF...')
1876        self.ExportPDF.append(item)
1877        item.Enable(False)
1878        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1879
1880    def FillMainMenu(self,menubar):
1881        '''Define contents of the main GSAS-II menu for the (main) data tree window
1882        For the mac this is also call for the data item windows as well.
1883        '''
1884        File = wx.Menu(title='')
1885        menubar.Append(menu=File, title='&File')
1886        self._Add_FileMenuItems(File)
1887        Data = wx.Menu(title='')
1888        menubar.Append(menu=Data, title='Data')
1889        self._Add_DataMenuItems(Data)
1890        Calculate = wx.Menu(title='')       
1891        menubar.Append(menu=Calculate, title='&Calculate')
1892        self._Add_CalculateMenuItems(Calculate)
1893        Import = wx.Menu(title='')       
1894        menubar.Append(menu=Import, title='Import')
1895        self._Add_ImportMenu_Phase(Import)
1896        self._Add_ImportMenu_powder(Import)
1897        self._Add_ImportMenu_Sfact(Import)
1898        self._Add_ImportMenu_smallangle(Import)
1899        #======================================================================
1900        # Code to help develop/debug an importer, much is hard-coded below
1901        # but module is reloaded before each use, allowing faster testing
1902        # def DebugImport(event):
1903        #     print 'start reload'
1904        #     import G2phase_ISO as dev
1905        #     reload(dev)
1906        #     rd = dev.ISODISTORTPhaseReader()
1907        #     self.ImportMenuId[event.GetId()] = rd
1908        #     self.OnImportPhase(event)
1909            # or ----------------------------------------------------------------------
1910            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
1911            # special debug code
1912            # or ----------------------------------------------------------------------
1913            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
1914            # fp = open(filename,'Ur')
1915            # if not rd.ContentsValidator(fp):
1916            #     print 'not validated'
1917            #     # make a list of used phase ranId's
1918            # phaseRIdList = []
1919            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1920            # if sub:
1921            #     item, cookie = self.PatternTree.GetFirstChild(sub)
1922            #     while item:
1923            #         phaseName = self.PatternTree.GetItemText(item)
1924            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
1925            #         if ranId: phaseRIdList.append(ranId)
1926            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1927            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
1928            #     print 'read OK'
1929        # item = Import.Append(
1930        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1931        #     help="debug importer",text="test importer")
1932        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
1933        #======================================================================
1934        self.ExportMenu = wx.Menu(title='')
1935        menubar.Append(menu=self.ExportMenu, title='Export')
1936        self._init_Exports(self.ExportMenu)
1937        self._Add_ExportMenuItems(self.ExportMenu)
1938        self.MacroMenu = wx.Menu(title='')
1939        menubar.Append(menu=self.MacroMenu, title='Macro')
1940        self._init_Macro()
1941        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1942            morehelpitems=[('&Tutorials','Tutorials')])
1943        menubar.Append(menu=HelpMenu,title='&Help')
1944
1945    def _init_ctrls(self, parent):
1946        wx.Frame.__init__(self, name='GSASII', parent=parent,
1947            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1948        clientSize = wx.ClientDisplayRect()
1949        Size = self.GetSize()
1950        xPos = clientSize[2]-Size[0]
1951        self.SetPosition(wx.Point(xPos,clientSize[1]))
1952        self._init_Imports()
1953        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1954        self.MakePDF = []
1955        self.Refine = []
1956        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
1957        #self.ExportPattern = []
1958        self.ExportPeakList = []
1959        self.ExportHKL = []
1960        self.ExportPDF = []
1961        self.ExportPhase = []
1962        self.ExportCIF = []
1963        #
1964        self.GSASIIMenu = wx.MenuBar()
1965        # create a list of all dataframe menus (appended in PrefillDataMenu)
1966        self.dataMenuBars = [self.GSASIIMenu]
1967        self.MacroStatusList = []
1968        self.FillMainMenu(self.GSASIIMenu)
1969        self.SetMenuBar(self.GSASIIMenu)
1970        self.Bind(wx.EVT_SIZE, self.OnSize)
1971        self.CreateStatusBar()
1972        self.mainPanel = wx.Panel(self,-1)
1973       
1974        wxID_PATTERNTREE = wx.NewId()
1975        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE, # replaced for logging
1976        self.PatternTree = G2gd.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.