source: branch/logging/GSASII.py @ 1509

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

logging refactored, and much cleaner\!

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