source: branch/logging/GSASII.py @ 1497

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

logging more complete

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 163.2 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#GSASII
4########### SVN repository information ###################
5# $Date: 2014-09-16 19:28:31 +0000 (Tue, 16 Sep 2014) $
6# $Author: toby $
7# $Revision: 1497 $
8# $URL: branch/logging/GSASII.py $
9# $Id: GSASII.py 1497 2014-09-16 19:28:31Z 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: 1497 $")
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 _init_Macro(self):
1628        menu = self.MacroMenu
1629        self.MacroRecordStatus = menu.Append(
1630            help='Start or stop recording of menu actions, etc.', id=wx.ID_ANY,
1631            kind=wx.ITEM_NORMAL,text='Record actions')
1632        self.MacroRecordStatus.SetText('Stop recording') # debug, start w/logging enabled
1633        import log
1634        log.LogOn()           
1635        def OnMacroRecordStatus(event):
1636            import log
1637            if 'actions' in self.MacroRecordStatus.GetText():
1638                self.MacroRecordStatus.SetText('Stop recording')
1639                log.LogOn()           
1640            else:
1641                self.MacroRecordStatus.SetText('Record actions')
1642                log.LogOff()
1643        self.Bind(wx.EVT_MENU, OnMacroRecordStatus, self.MacroRecordStatus)
1644       
1645        item = menu.Append(
1646            help='Show logged commands', id=wx.ID_ANY,
1647            kind=wx.ITEM_NORMAL,text='Show log')
1648        def OnShowLog(event):
1649            import log
1650            print 70*'='
1651            print 'List of logged actions'
1652            for line in log.G2logList:
1653                if line: print line
1654            print 70*'='
1655        self.Bind(wx.EVT_MENU, OnShowLog, item)
1656
1657        item = menu.Append(
1658            help='Save logged commands to file', id=wx.ID_ANY,
1659            kind=wx.ITEM_NORMAL,text='Save log')
1660        def OnSaveLog(event):
1661            import log
1662            import cPickle
1663            defnam = os.path.splitext(
1664                os.path.split(self.GSASprojectfile)[1]
1665                )[0]+'.gcmd'
1666            dlg = wx.FileDialog(self,
1667                'Choose an file to save past actions', '.', defnam, 
1668                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1669                wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
1670            dlg.CenterOnParent()
1671            try:
1672                if dlg.ShowModal() == wx.ID_OK:
1673                    filename = dlg.GetPath()
1674                    # make sure extension is correct
1675                    filename = os.path.splitext(filename)[0]+'.gcmd'
1676                else:
1677                    filename = None
1678            finally:
1679                dlg.Destroy()
1680            if filename:
1681                fp = open(filename,'wb')
1682                fp.write(str(len(log.G2logList))+'\n')
1683                for item in log.G2logList:
1684                    cPickle.dump(item,fp)
1685                fp.close()
1686        self.Bind(wx.EVT_MENU, OnSaveLog, item)
1687
1688        item = menu.Append(
1689            help='Load logged commands from file', id=wx.ID_ANY,
1690            kind=wx.ITEM_NORMAL,text='Load log')
1691        def OnLoadLog(event):
1692            import log
1693            import cPickle
1694            defnam = os.path.splitext(
1695                os.path.split(self.GSASprojectfile)[1]
1696                )[0]+'.gcmd'
1697            dlg = wx.FileDialog(self,
1698                'Choose an file to read saved actions', '.', defnam, 
1699                'GSAS-II cmd output (*.gcmd)|*.gcmd',
1700                wx.OPEN|wx.CHANGE_DIR)
1701            dlg.CenterOnParent()
1702            try:
1703                if dlg.ShowModal() == wx.ID_OK:
1704                    filename = dlg.GetPath()
1705                    # make sure extension is correct
1706                    filename = os.path.splitext(filename)[0]+'.gcmd'
1707                else:
1708                    filename = None
1709            finally:
1710                dlg.Destroy()
1711            if filename and os.path.exists(filename):
1712                fp = open(filename,'rb')
1713                lines = fp.readline()
1714                for i in range(int(lines)):
1715                    log.G2logList.append(cPickle.load(fp))
1716                fp.close()
1717        self.Bind(wx.EVT_MENU, OnLoadLog, item)
1718
1719        item = menu.Append(
1720            help='Replay saved commands', id=wx.ID_ANY,
1721            kind=wx.ITEM_NORMAL,text='Replay log')
1722        self.Bind(wx.EVT_MENU, log.ReplayLog, item)
1723                               
1724    def _init_Exports(self,menu):
1725        '''Find exporter routines and add them into menus
1726        '''
1727        # set up the top-level menus
1728        projectmenu = wx.Menu()
1729        item = menu.AppendMenu(
1730            wx.ID_ANY, 'Entire project as',
1731            projectmenu, help='Export entire project')
1732
1733        phasemenu = wx.Menu()
1734        item = menu.AppendMenu(
1735            wx.ID_ANY, 'Phase as',
1736            phasemenu, help='Export phase or sometimes phases')
1737
1738        powdermenu = wx.Menu()
1739        item = menu.AppendMenu(
1740            wx.ID_ANY, 'Powder data as',
1741            powdermenu, help='Export powder diffraction histogram(s)')
1742
1743        singlemenu = wx.Menu()
1744        item = menu.AppendMenu(
1745            wx.ID_ANY, 'Single crystal data as',
1746            singlemenu, help='Export single crystal histogram(s)')
1747
1748        imagemenu = wx.Menu()
1749        item = menu.AppendMenu(
1750            wx.ID_ANY, 'Image data as',
1751            imagemenu, help='Export powder image(s) data')
1752
1753        mapmenu = wx.Menu()
1754        item = menu.AppendMenu(
1755            wx.ID_ANY, 'Maps as',
1756            mapmenu, help='Export density map(s)')
1757
1758        # pdfmenu = wx.Menu()
1759        # item = menu.AppendMenu(
1760        #     wx.ID_ANY, 'PDFs as',
1761        #     pdfmenu, help='Export pair distribution function(s)')
1762
1763        # find all the exporter files
1764        pathlist = sys.path
1765        filelist = []
1766        for path in pathlist:
1767            for filename in glob.iglob(os.path.join(path,"G2export*.py")):
1768                filelist.append(filename)   
1769        filelist = sorted(list(set(filelist))) # remove duplicates
1770        exporterlist = []
1771        # go through the routines and import them, saving objects that
1772        # have export routines (method Exporter)
1773        for filename in filelist:
1774            path,rootname = os.path.split(filename)
1775            pkg = os.path.splitext(rootname)[0]
1776            try:
1777                fp = None
1778                fp, fppath,desc = imp.find_module(pkg,[path,])
1779                pkg = imp.load_module(pkg,fp,fppath,desc)
1780                for clss in inspect.getmembers(pkg): # find classes defined in package
1781                    if clss[0].startswith('_'): continue
1782                    if inspect.isclass(clss[1]):
1783                        # check if we have the required methods
1784                        for m in 'Exporter','loadParmDict':
1785                            if not hasattr(clss[1],m): break
1786                            if not callable(getattr(clss[1],m)): break
1787                        else:
1788                            exporter = clss[1](self) # create an export instance
1789                            exporterlist.append(exporter)
1790            except AttributeError:
1791                print 'Import_'+errprefix+': Attribute Error'+str(filename)
1792                pass
1793            except ImportError:
1794                print 'Import_'+errprefix+': Error importing file'+str(filename)
1795                pass
1796            if fp: fp.close()
1797        # Add submenu item(s) for each Exporter by its self-declared type (can be more than one)
1798        for obj in exporterlist:
1799            #print 'exporter',obj
1800            for typ in obj.exporttype:
1801                if typ == "project":
1802                    submenu = projectmenu
1803                elif typ == "phase":
1804                    submenu = phasemenu
1805                elif typ == "powder":
1806                    submenu = powdermenu
1807                elif typ == "single":
1808                    submenu = singlemenu
1809                elif typ == "image":
1810                    submenu = imagemenu
1811                elif typ == "map":
1812                    submenu = mapmenu
1813                # elif typ == "pdf":
1814                #     submenu = pdfmenu
1815                else:
1816                    print("Error, unknown type in "+str(obj))
1817                    break
1818                item = submenu.Append(
1819                    wx.ID_ANY,
1820                    help=obj.longFormatName,
1821                    kind=wx.ITEM_NORMAL,
1822                    text=obj.formatName)
1823                self.Bind(wx.EVT_MENU, obj.Exporter, id=item.GetId())
1824                self.ExportLookup[item.GetId()] = typ # lookup table for submenu item
1825               
1826        #code to debug an Exporter. hard-coded the routine below, to allow a reload before use
1827        # def DebugExport(event):
1828        #      print 'start reload'
1829        #      reload(G2IO)
1830        #      import G2export_pwdr as dev
1831        #      reload(dev)
1832        #      dev.ExportPowderFXYE(self).Exporter(event)
1833        # item = menu.Append(
1834        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1835        #     help="debug exporter",text="test Export FXYE")
1836        # self.Bind(wx.EVT_MENU, DebugExport, id=item.GetId())
1837        # # #self.ExportLookup[item.GetId()] = 'image'
1838        # self.ExportLookup[item.GetId()] = 'powder'
1839
1840    def _Add_ExportMenuItems(self,parent):
1841        # item = parent.Append(
1842        #     help='Select PWDR item to enable',id=wx.ID_ANY,
1843        #     kind=wx.ITEM_NORMAL,
1844        #     text='Export Powder Patterns...')
1845        # self.ExportPattern.append(item)
1846        # item.Enable(False)
1847        # self.Bind(wx.EVT_MENU, self.OnExportPatterns, id=item.GetId())
1848
1849        item = parent.Append(
1850            help='',id=wx.ID_ANY,
1851            kind=wx.ITEM_NORMAL,
1852            text='Export All Peak Lists...')
1853        self.ExportPeakList.append(item)
1854        item.Enable(True)
1855        self.Bind(wx.EVT_MENU, self.OnExportPeakList, id=item.GetId())
1856
1857        item = parent.Append(
1858            help='',id=wx.ID_ANY,
1859            kind=wx.ITEM_NORMAL,
1860            text='Export HKLs...')
1861        self.ExportHKL.append(item)
1862        self.Bind(wx.EVT_MENU, self.OnExportHKL, id=item.GetId())
1863
1864        item = parent.Append(
1865            help='Select PDF item to enable',
1866            id=wx.ID_ANY,
1867            kind=wx.ITEM_NORMAL,
1868            text='Export PDF...')
1869        self.ExportPDF.append(item)
1870        item.Enable(False)
1871        self.Bind(wx.EVT_MENU, self.OnExportPDF, id=item.GetId())
1872
1873    def FillMainMenu(self,menubar):
1874        '''Define contents of the main GSAS-II menu for the (main) data tree window
1875        in the mac, used also for the data item windows as well.
1876        '''
1877        File = wx.Menu(title='')
1878        menubar.Append(menu=File, title='&File')
1879        self._Add_FileMenuItems(File)
1880        Data = wx.Menu(title='')
1881        menubar.Append(menu=Data, title='Data')
1882        self._Add_DataMenuItems(Data)
1883        Calculate = wx.Menu(title='')       
1884        menubar.Append(menu=Calculate, title='&Calculate')
1885        self._Add_CalculateMenuItems(Calculate)
1886        Import = wx.Menu(title='')       
1887        menubar.Append(menu=Import, title='Import')
1888        self._Add_ImportMenu_Phase(Import)
1889        self._Add_ImportMenu_powder(Import)
1890        self._Add_ImportMenu_Sfact(Import)
1891        self._Add_ImportMenu_smallangle(Import)
1892        #======================================================================
1893        # Code to help develop/debug an importer, much is hard-coded below
1894        # but module is reloaded before each use, allowing faster testing
1895        # def DebugImport(event):
1896        #     print 'start reload'
1897        #     import G2phase_ISO as dev
1898        #     reload(dev)
1899        #     rd = dev.ISODISTORTPhaseReader()
1900        #     self.ImportMenuId[event.GetId()] = rd
1901        #     self.OnImportPhase(event)
1902            # or ----------------------------------------------------------------------
1903            #self.OnImportGeneric(rd,[],'test of ISODISTORTPhaseReader')
1904            # special debug code
1905            # or ----------------------------------------------------------------------
1906            # filename = '/Users/toby/projects/branton/subgroup_cif.txt'
1907            # fp = open(filename,'Ur')
1908            # if not rd.ContentsValidator(fp):
1909            #     print 'not validated'
1910            #     # make a list of used phase ranId's
1911            # phaseRIdList = []
1912            # sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
1913            # if sub:
1914            #     item, cookie = self.PatternTree.GetFirstChild(sub)
1915            #     while item:
1916            #         phaseName = self.PatternTree.GetItemText(item)
1917            #         ranId = self.PatternTree.GetItemPyData(item).get('ranId')
1918            #         if ranId: phaseRIdList.append(ranId)
1919            #         item, cookie = self.PatternTree.GetNextChild(sub, cookie)
1920            # if rd.Reader(filename,fp,usedRanIdList=phaseRIdList):
1921            #     print 'read OK'
1922        # item = Import.Append(
1923        #     wx.ID_ANY,kind=wx.ITEM_NORMAL,
1924        #     help="debug importer",text="test importer")
1925        # self.Bind(wx.EVT_MENU, DebugImport, id=item.GetId())
1926        #======================================================================
1927        self.ExportMenu = wx.Menu(title='')
1928        menubar.Append(menu=self.ExportMenu, title='Export')
1929        self._init_Exports(self.ExportMenu)
1930        self._Add_ExportMenuItems(self.ExportMenu)
1931        self.MacroMenu = wx.Menu(title='')
1932        menubar.Append(menu=self.MacroMenu, title='Macro')
1933        self._init_Macro()
1934        HelpMenu=G2gd.MyHelp(self,helpType='Data tree',
1935            morehelpitems=[('&Tutorials','Tutorials')])
1936        menubar.Append(menu=HelpMenu,title='&Help')
1937
1938    def _init_ctrls(self, parent):
1939        wx.Frame.__init__(self, name='GSASII', parent=parent,
1940            size=wx.Size(400, 250),style=wx.DEFAULT_FRAME_STYLE, title='GSAS-II data tree')
1941        clientSize = wx.ClientDisplayRect()
1942        Size = self.GetSize()
1943        xPos = clientSize[2]-Size[0]
1944        self.SetPosition(wx.Point(xPos,clientSize[1]))
1945        self._init_Imports()
1946        #initialize Menu item objects (these contain lists of menu items that are enabled or disabled)
1947        self.MakePDF = []
1948        self.Refine = []
1949        self.SeqRefine = [] # pointer(s) to Sequential Refinement menu objects
1950        #self.ExportPattern = []
1951        self.ExportPeakList = []
1952        self.ExportHKL = []
1953        self.ExportPDF = []
1954        self.ExportPhase = []
1955        self.ExportCIF = []
1956        #
1957        self.GSASIIMenu = wx.MenuBar()
1958        # create a list of all dataframe menus (appended in PrefillDataMenu)
1959        self.dataMenuBars = [self.GSASIIMenu]
1960        self.FillMainMenu(self.GSASIIMenu)
1961        self.SetMenuBar(self.GSASIIMenu)
1962        self.Bind(wx.EVT_SIZE, self.OnSize)
1963        self.CreateStatusBar()
1964        self.mainPanel = wx.Panel(self,-1)
1965       
1966        wxID_PATTERNTREE = wx.NewId()
1967        #self.PatternTree = wx.TreeCtrl(id=wxID_PATTERNTREE,
1968        import log
1969        self.PatternTree = log.G2TreeCtrl(id=wxID_PATTERNTREE,
1970            parent=self.mainPanel, pos=wx.Point(0, 0),style=wx.TR_DEFAULT_STYLE )
1971        self.PatternTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnPatternTreeSelChanged)
1972        self.PatternTree.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
1973            self.OnPatternTreeItemCollapsed, id=wxID_PATTERNTREE)
1974        self.PatternTree.Bind(wx.EVT_TREE_ITEM_EXPANDED,
1975            self.OnPatternTreeItemExpanded, id=wxID_PATTERNTREE)
1976        self.PatternTree.Bind(wx.EVT_TREE_DELETE_ITEM,
1977            self.OnPatternTreeItemDelete, id=wxID_PATTERNTREE)
1978        self.PatternTree.Bind(wx.EVT_TREE_KEY_DOWN,
1979            self.OnPatternTreeKeyDown, id=wxID_PATTERNTREE)
1980        #self.root = self.PatternTree.AddRoot('Loaded Data: ')
1981        self.root = self.PatternTree.root
1982       
1983        plotFrame = wx.Frame(None,-1,'GSASII Plots',size=wx.Size(700,600), \
1984            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
1985        self.G2plotNB = G2plt.G2PlotNoteBook(plotFrame)
1986        plotFrame.Show()
1987       
1988        self.dataDisplay = None
1989       
1990    def __init__(self, parent):
1991        self.ExportLookup = {}
1992        self._init_ctrls(parent)
1993        self.Image = wx.Image(
1994            os.path.join(GSASIIpath.path2GSAS2,'gsas2.ico'),
1995            wx.BITMAP_TYPE_ICO)
1996        if "wxMSW" in wx.PlatformInfo:
1997            img = self.Image.Scale(16, 16).ConvertToBitmap()
1998        elif "wxGTK" in wx.PlatformInfo:
1999            img = self.Image.Scale(22, 22).ConvertToBitmap()
2000        else:
2001            img = self.Image.ConvertToBitmap()
2002        self.SetIcon(wx.IconFromBitmap(img))
2003        self.Bind(wx.EVT_CLOSE, self.ExitMain)
2004        # various defaults
2005        self.oldFocus = None
2006        self.GSASprojectfile = ''
2007        self.dirname = os.path.expanduser('~')       #start in the users home directory by default; may be meaningless
2008        self.exportDir = None  # the last directory used for exports, if any.
2009        self.undofile = ''
2010        self.TreeItemDelete = False
2011        self.Offset = [0.0,0.0]
2012        self.delOffset = .02
2013        self.refOffset = -100.0
2014        self.refDelt = .01
2015        self.Weight = False
2016        self.IparmName = ''  # to be removed when SelectPowderData & GetInstrumentFile is
2017        self.IfPlot = False
2018        self.PatternId = 0
2019        self.PickId = 0
2020        self.PeakTable = []
2021        self.LimitsTable = []
2022        self.HKL = []
2023        self.Lines = []
2024        self.itemPicked = None
2025        self.dataFrame = None
2026        self.Interpolate = 'nearest'
2027        self.ContourColor = 'Paired'
2028        self.VcovColor = 'RdYlGn'
2029        self.RamaColor = 'Blues'
2030        self.Projection = 'equal area'
2031        self.logPlot = False
2032        self.qPlot = False
2033        self.sqPlot = False
2034        self.SqrtPlot = False
2035        self.ErrorBars = False
2036        self.Contour = False
2037        self.Legend = False
2038        self.SinglePlot = True
2039        self.SubBack = False
2040        self.seqReverse = False
2041        self.plotView = 0
2042        self.Image = 0
2043        self.oldImagefile = ''
2044        self.ImageZ = []
2045        self.Integrate = 0
2046        self.imageDefault = {}
2047        self.Sngl = False
2048        self.ifGetRing = False
2049        self.MaskKey = ''           #trigger for making image masks
2050        self.StrainKey = ''         #ditto for new strain d-zeros
2051        self.EnablePlot = True
2052        self.Tutorials = False      #used for changing default directory
2053        arg = sys.argv
2054        if len(arg) > 1:
2055            self.GSASprojectfile = os.path.splitext(arg[1])[0]+'.gpx'
2056            self.dirname = os.path.dirname(arg[1])
2057            if self.dirname: os.chdir(self.dirname)
2058            try:
2059                G2IO.ProjFileOpen(self)
2060                self.PatternTree.Expand(self.root)
2061                for item in self.Refine: item.Enable(True)
2062                self.EnableSeqRefineMenu()
2063
2064            except:
2065                print 'Error opening file',arg[1]
2066
2067    def OnSize(self,event):
2068        'Called when the main window is resized. Not sure why'
2069        w,h = self.GetClientSizeTuple()
2070        self.mainPanel.SetSize(wx.Size(w,h))
2071        self.PatternTree.SetSize(wx.Size(w,h))
2072                       
2073    def OnPatternTreeSelChanged(self, event):
2074        '''Called when a data tree item is selected'''
2075        if self.TreeItemDelete:
2076            self.TreeItemDelete = False
2077        else:
2078            pltNum = self.G2plotNB.nb.GetSelection()
2079            if pltNum >= 0:                         #to avoid the startup with no plot!
2080                pltPage = self.G2plotNB.nb.GetPage(pltNum)
2081                pltPlot = pltPage.figure
2082            item = event.GetItem()
2083            G2gd.MovePatternTreeToGrid(self,item)
2084            if self.oldFocus:
2085                self.oldFocus.SetFocus()
2086       
2087    def OnPatternTreeItemCollapsed(self, event):
2088        'Called when a tree item is collapsed'
2089        event.Skip()
2090
2091    def OnPatternTreeItemExpanded(self, event):
2092        'Called when a tree item is expanded'
2093        event.Skip()
2094       
2095    def OnPatternTreeItemDelete(self, event):
2096        'Called when a tree item is deleted -- not sure what this does'
2097        self.TreeItemDelete = True
2098
2099    def OnPatternTreeItemActivated(self, event):
2100        'Called when a tree item is activated'
2101        event.Skip()
2102       
2103    def OnPatternTreeKeyDown(self,event):
2104        'Not sure what this does'
2105        key = event.GetKeyCode()
2106        item = self.PickId
2107        if type(item) is int: return # is this the toplevel in tree?
2108        if key == wx.WXK_UP:
2109            self.oldFocus = wx.Window.FindFocus()
2110            self.PatternTree.GetPrevSibling(item)
2111        elif key == wx.WXK_DOWN:
2112            self.oldFocus = wx.Window.FindFocus()
2113            self.PatternTree.GetNextSibling(item)
2114               
2115    def OnReadPowderPeaks(self,event):
2116        'Bound to menu Data/Read Powder Peaks -- still needed?'
2117        Cuka = 1.54052
2118        self.CheckNotebook()
2119        dlg = wx.FileDialog(self, 'Choose file with peak list', '.', '', 
2120            'peak files (*.txt)|*.txt|All files (*.*)|*.*',wx.OPEN|wx.CHANGE_DIR)
2121        try:
2122            if dlg.ShowModal() == wx.ID_OK:
2123                self.HKL = []
2124                self.powderfile = dlg.GetPath()
2125                comments,peaks = G2IO.GetPowderPeaks(self.powderfile)
2126                Id = self.PatternTree.AppendItem(parent=self.root,text='PKS '+os.path.basename(self.powderfile))
2127                data = ['PKS',Cuka,0.0]
2128                names = ['Type','Lam','Zero'] 
2129                codes = [0,0,0]
2130                inst = [G2IO.makeInstDict(names,data,codes),{}]
2131                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
2132                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),comments)
2133                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[peaks,[]])
2134                self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2135                self.PatternTree.Expand(Id)
2136                self.PatternTree.SelectItem(Id)
2137                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2138        finally:
2139            dlg.Destroy()
2140                       
2141    def OnImageRead(self,event):
2142        'Called to read in an image in any known format'
2143        self.CheckNotebook()
2144        dlg = wx.FileDialog(
2145            self, 'Choose image files', '.', '',
2146            'Any supported image file (*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png)|'
2147            '*.edf;*.tif;*.tiff;*.mar*;*.ge*;*.avg;*.sum;*.img;*.G2img;*.png;*.zip|'
2148            'European detector file (*.edf)|*.edf|'
2149            'Any detector tif (*.tif;*.tiff)|*.tif;*.tiff|'
2150            'MAR file (*.mar*)|*.mar*|'
2151            'GE Image (*.ge*;*.avg;*.sum)|*.ge*;*.avg;*.sum|'
2152            'ADSC Image (*.img)|*.img|'
2153            'GSAS-II Image (*.G2img)|*.G2img|'
2154            'Portable Network Graphics image (*.png)|*.png|'
2155            'Zip archive (*.zip)|*.zip|'
2156            'All files (*.*)|*.*',
2157            wx.OPEN | wx.MULTIPLE|wx.CHANGE_DIR)
2158        try:
2159            if dlg.ShowModal() == wx.ID_OK:
2160                imagefiles = dlg.GetPaths()
2161                imagefiles.sort()
2162                for imagefile in imagefiles:
2163                    # if a zip file, open and extract
2164                    if os.path.splitext(imagefile)[1].lower() == '.zip':
2165                        extractedfile = G2IO.ExtractFileFromZip(imagefile,parent=self)
2166                        if extractedfile is not None and extractedfile != imagefile:
2167                            imagefile = extractedfile
2168                    Comments,Data,Npix,Image = G2IO.GetImageData(self,imagefile)
2169                    if Comments:
2170                        Id = self.PatternTree.AppendItem(parent=self.root,text='IMG '+os.path.basename(imagefile))
2171                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2172                        Imax = np.amax(Image)
2173                        Imin = max(0.0,np.amin(Image))          #force positive
2174                        if self.imageDefault:
2175                            Data = copy.copy(self.imageDefault)
2176                            Data['showLines'] = True
2177                            Data['ring'] = []
2178                            Data['rings'] = []
2179                            Data['cutoff'] = 10
2180                            Data['pixLimit'] = 20
2181                            Data['edgemin'] = 100000000
2182                            Data['calibdmin'] = 0.5
2183                            Data['calibskip'] = 0
2184                            Data['ellipses'] = []
2185                            Data['calibrant'] = ''
2186                            Data['GonioAngles'] = [0.,0.,0.]
2187                            Data['DetDepthRef'] = False
2188                        else:
2189                            Data['type'] = 'PWDR'
2190                            Data['color'] = 'Paired'
2191                            Data['tilt'] = 0.0
2192                            Data['rotation'] = 0.0
2193                            Data['showLines'] = False
2194                            Data['ring'] = []
2195                            Data['rings'] = []
2196                            Data['cutoff'] = 10
2197                            Data['pixLimit'] = 20
2198                            Data['calibdmin'] = 0.5
2199                            Data['calibskip'] = 0
2200                            Data['edgemin'] = 100000000
2201                            Data['ellipses'] = []
2202                            Data['GonioAngles'] = [0.,0.,0.]
2203                            Data['DetDepth'] = 0.
2204                            Data['DetDepthRef'] = False
2205                            Data['calibrant'] = ''
2206                            Data['IOtth'] = [2.0,5.0]
2207                            Data['LRazimuth'] = [135,225]
2208                            Data['azmthOff'] = 0.0
2209                            Data['outChannels'] = 2500
2210                            Data['outAzimuths'] = 1
2211                            Data['centerAzm'] = False
2212                            Data['fullIntegrate'] = False
2213                            Data['setRings'] = False
2214                            Data['background image'] = ['',-1.0]                           
2215                            Data['dark image'] = ['',-1.0]
2216                        Data['setDefault'] = False
2217                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2218                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)
2219                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2220                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2221                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2222                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2223                        self.PatternTree.SetItemPyData(Id,[Npix,imagefile])
2224                        self.PickId = Id
2225                        self.Image = Id
2226                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!               
2227                self.PatternTree.SelectItem(G2gd.GetPatternTreeItemId(self,Id,'Image Controls'))             #show last one
2228        finally:
2229            path = dlg.GetDirectory()           # to get Mac/Linux to change directory!
2230            os.chdir(path)
2231            dlg.Destroy()
2232
2233    def CheckNotebook(self):
2234        '''Make sure the data tree has the minimally expected controls.
2235        (BHT) correct?
2236        '''
2237        if not G2gd.GetPatternTreeItemId(self,self.root,'Notebook'):
2238            sub = self.PatternTree.AppendItem(parent=self.root,text='Notebook')
2239            self.PatternTree.SetItemPyData(sub,[''])
2240        if not G2gd.GetPatternTreeItemId(self,self.root,'Controls'):
2241            sub = self.PatternTree.AppendItem(parent=self.root,text='Controls')
2242            self.PatternTree.SetItemPyData(sub,copy.copy(G2obj.DefaultControls))
2243        if not G2gd.GetPatternTreeItemId(self,self.root,'Covariance'):
2244            sub = self.PatternTree.AppendItem(parent=self.root,text='Covariance')
2245            self.PatternTree.SetItemPyData(sub,{})
2246        if not G2gd.GetPatternTreeItemId(self,self.root,'Constraints'):
2247            sub = self.PatternTree.AppendItem(parent=self.root,text='Constraints')
2248            self.PatternTree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
2249        if not G2gd.GetPatternTreeItemId(self,self.root,'Restraints'):
2250            sub = self.PatternTree.AppendItem(parent=self.root,text='Restraints')
2251            self.PatternTree.SetItemPyData(sub,{})
2252        if not G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'):
2253            sub = self.PatternTree.AppendItem(parent=self.root,text='Rigid bodies')
2254            self.PatternTree.SetItemPyData(sub,{'Vector':{'AtInfo':{}},
2255                'Residue':{'AtInfo':{}},'RBIds':{'Vector':[],'Residue':[]}})
2256               
2257    class CopyDialog(wx.Dialog):
2258        '''Creates a dialog for copying control settings between
2259        data tree items'''
2260        def __init__(self,parent,title,text,data):
2261            wx.Dialog.__init__(self,parent,-1,title, 
2262                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2263            self.data = data
2264            panel = wx.Panel(self)
2265            mainSizer = wx.BoxSizer(wx.VERTICAL)
2266            topLabl = wx.StaticText(panel,-1,text)
2267            mainSizer.Add((10,10),1)
2268            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2269            mainSizer.Add((10,10),1)
2270            ncols = len(data)/40+1
2271            dataGridSizer = wx.FlexGridSizer(cols=ncols,hgap=2,vgap=2)
2272            for id,item in enumerate(self.data):
2273                ckbox = wx.CheckBox(panel,id,item[1])
2274                ckbox.Bind(wx.EVT_CHECKBOX,self.OnCopyChange)                   
2275                dataGridSizer.Add(ckbox,0,wx.LEFT,10)
2276            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2277            OkBtn = wx.Button(panel,-1,"Ok")
2278            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2279            cancelBtn = wx.Button(panel,-1,"Cancel")
2280            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2281            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2282            btnSizer.Add((20,20),1)
2283            btnSizer.Add(OkBtn)
2284            btnSizer.Add((20,20),1)
2285            btnSizer.Add(cancelBtn)
2286            btnSizer.Add((20,20),1)
2287           
2288            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2289            panel.SetSizer(mainSizer)
2290            panel.Fit()
2291            self.Fit()
2292       
2293        def OnCopyChange(self,event):
2294            id = event.GetId()
2295            self.data[id][0] = self.FindWindowById(id).GetValue()       
2296           
2297        def OnOk(self,event):
2298            parent = self.GetParent()
2299            parent.Raise()
2300            self.EndModal(wx.ID_OK)             
2301           
2302        def OnCancel(self,event):
2303            parent = self.GetParent()
2304            parent.Raise()
2305            self.EndModal(wx.ID_CANCEL)             
2306           
2307        def GetData(self):
2308            return self.data
2309       
2310    class SumDialog(wx.Dialog):
2311        'Allows user to supply scale factor(s) when summing data'
2312        def __init__(self,parent,title,text,dataType,data):
2313            wx.Dialog.__init__(self,parent,-1,title, 
2314                pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
2315            self.data = data
2316            panel = wx.Panel(self)
2317            mainSizer = wx.BoxSizer(wx.VERTICAL)
2318            topLabl = wx.StaticText(panel,-1,text)
2319            mainSizer.Add((10,10),1)
2320            mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
2321            mainSizer.Add((10,10),1)
2322            dataGridSizer = wx.FlexGridSizer(cols=2,hgap=2,vgap=2)
2323            for id,item in enumerate(self.data[:-1]):
2324                name = wx.TextCtrl(panel,-1,item[1],size=wx.Size(200,20))
2325                name.SetEditable(False)
2326                scale = wx.TextCtrl(panel,id,'%.3f'%(item[0]),style=wx.TE_PROCESS_ENTER)
2327                scale.Bind(wx.EVT_TEXT_ENTER,self.OnScaleChange)
2328                scale.Bind(wx.EVT_KILL_FOCUS,self.OnScaleChange)
2329                dataGridSizer.Add(scale,0,wx.LEFT,10)
2330                dataGridSizer.Add(name,0,wx.RIGHT,10)
2331            if dataType:
2332                dataGridSizer.Add(wx.StaticText(panel,-1,'Sum result name: '+dataType),0, \
2333                    wx.LEFT|wx.TOP|wx.ALIGN_CENTER_VERTICAL,10)
2334                self.name = wx.TextCtrl(panel,-1,self.data[-1],size=wx.Size(200,20),style=wx.TE_PROCESS_ENTER)
2335                self.name.Bind(wx.EVT_TEXT_ENTER,self.OnNameChange)
2336                self.name.Bind(wx.EVT_KILL_FOCUS,self.OnNameChange)
2337                dataGridSizer.Add(self.name,0,wx.RIGHT|wx.TOP,10)
2338            mainSizer.Add(dataGridSizer,0,wx.EXPAND)
2339            OkBtn = wx.Button(panel,-1,"Ok")
2340            OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
2341            cancelBtn = wx.Button(panel,-1,"Cancel")
2342            cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
2343            btnSizer = wx.BoxSizer(wx.HORIZONTAL)
2344            btnSizer.Add((20,20),1)
2345            btnSizer.Add(OkBtn)
2346            btnSizer.Add((20,20),1)
2347            btnSizer.Add(cancelBtn)
2348            btnSizer.Add((20,20),1)
2349           
2350            mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
2351            panel.SetSizer(mainSizer)
2352            panel.Fit()
2353            self.Fit()
2354
2355        def OnScaleChange(self,event):
2356            id = event.GetId()
2357            value = self.FindWindowById(id).GetValue()
2358            try:
2359                self.data[id][0] = float(value)
2360                self.FindWindowById(id).SetValue('%.3f'%(self.data[id][0]))
2361            except ValueError:
2362                if value and '-' not in value[0]:
2363                    print 'bad input - numbers only'
2364                    self.FindWindowById(id).SetValue('0.000')
2365           
2366        def OnNameChange(self,event):
2367            self.data[-1] = self.name.GetValue() 
2368           
2369        def OnOk(self,event):
2370            parent = self.GetParent()
2371            parent.Raise()
2372            self.EndModal(wx.ID_OK)             
2373           
2374        def OnCancel(self,event):
2375            parent = self.GetParent()
2376            parent.Raise()
2377            self.EndModal(wx.ID_CANCEL)             
2378           
2379        def GetData(self):
2380            return self.data
2381                       
2382    def OnPwdrSum(self,event):
2383        'Sum together powder data(?)'
2384        TextList = []
2385        DataList = []
2386        SumList = []
2387        Names = []
2388        Inst = None
2389        SumItemList = []
2390        Comments = ['Sum equals: \n']
2391        if self.PatternTree.GetCount():
2392            item, cookie = self.PatternTree.GetFirstChild(self.root)
2393            while item:
2394                name = self.PatternTree.GetItemText(item)
2395                Names.append(name)
2396                if 'PWDR' in name:
2397                    TextList.append([0.0,name])
2398                    DataList.append(self.PatternTree.GetItemPyData(item)[1])    # (x,y,w,yc,yb,yd)
2399                    if not Inst:
2400                        Inst = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item, 'Instrument Parameters'))
2401                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2402            if len(TextList) < 2:
2403                self.ErrorDialog('Not enough data to sum','There must be more than one "PWDR" pattern')
2404                return
2405            TextList.append('default_sum_name')               
2406            dlg = self.SumDialog(self,'Sum data','Enter scale for each pattern in summation','PWDR',TextList)
2407            try:
2408                if dlg.ShowModal() == wx.ID_OK:
2409                    lenX = 0
2410                    Xminmax = [0,0]
2411                    Xsum = []
2412                    Ysum = []
2413                    Vsum = []
2414                    result = dlg.GetData()
2415                    for i,item in enumerate(result[:-1]):
2416                        scale,name = item
2417                        data = DataList[i]
2418                        if scale:
2419                            Comments.append("%10.3f %s" % (scale,' * '+name))
2420                            x,y,w,yc,yb,yd = data   #numpy arrays!
2421                            v = 1./w
2422                            if lenX:
2423                                if lenX != len(x):
2424                                    self.ErrorDialog('Data length error','Data to be summed must have same number of points'+ \
2425                                        '\nExpected:'+str(lenX)+ \
2426                                        '\nFound:   '+str(len(x))+'\nfor '+name)
2427                                    return
2428                            else:
2429                                lenX = len(x)
2430                            if Xminmax[1]:
2431                                if Xminmax != [x[0],x[-1]]:
2432                                    self.ErrorDialog('Data range error','Data to be summed must span same range'+ \
2433                                        '\nExpected:'+str(Xminmax[0])+' '+str(Xminmax[1])+ \
2434                                        '\nFound:   '+str(x[0])+' '+str(x[-1])+'\nfor '+name)
2435                                    return
2436                                else:
2437                                    for j,yi in enumerate(y):
2438                                         Ysum[j] += scale*yi
2439                                         Vsum[j] += abs(scale)*v[j]
2440                            else:
2441                                Xminmax = [x[0],x[-1]]
2442                                YCsum = YBsum = YDsum = [0.0 for i in range(lenX)]
2443                                for j,yi in enumerate(y):
2444                                    Xsum.append(x[j])
2445                                    Ysum.append(scale*yi)
2446                                    Vsum.append(abs(scale*v[j]))
2447                    Wsum = 1./np.array(Vsum)
2448                    outname = 'PWDR '+result[-1]
2449                    Id = 0
2450                    if outname in Names:
2451                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2452                        try:
2453                            if dlg2.ShowModal() == wx.ID_OK:
2454                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2455                                self.PatternTree.Delete(Id)
2456                        finally:
2457                            dlg2.Destroy()
2458                    Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2459                    if Id:
2460                        Sample = G2pdG.SetDefaultSample()
2461                        valuesdict = {
2462                            'wtFactor':1.0,
2463                            'Dummy':False,
2464                            'ranId':ran.randint(0,sys.maxint),
2465                            }
2466                        self.PatternTree.SetItemPyData(Id,[valuesdict,[np.array(Xsum),np.array(Ysum),np.array(Wsum),
2467                            np.array(YCsum),np.array(YBsum),np.array(YDsum)]])
2468                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
2469                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
2470                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',True,3,1.0,0.0,0.0],
2471                            {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
2472                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Instrument Parameters'),Inst)
2473                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
2474                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Peak List'),{'peaks':[],'sigDict':{}})
2475                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
2476                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Unit Cells List'),[])             
2477                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Reflection Lists'),{})             
2478                        self.PatternTree.SelectItem(Id)
2479                        self.PatternTree.Expand(Id)
2480            finally:
2481                dlg.Destroy()
2482
2483    def OnImageSum(self,event):
2484        'Sum together image data(?)'
2485        TextList = []
2486        DataList = []
2487        SumList = []
2488        Names = []
2489        Inst = []
2490        SumItemList = []
2491        Comments = ['Sum equals: \n']
2492        if self.PatternTree.GetCount():
2493            item, cookie = self.PatternTree.GetFirstChild(self.root)
2494            while item:
2495                name = self.PatternTree.GetItemText(item)
2496                Names.append(name)
2497                if 'IMG' in name:
2498                    TextList.append([0.0,name])
2499                    DataList.append(self.PatternTree.GetItemPyData(item))        #Size,Image
2500                    Data = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,item,'Image Controls'))
2501                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2502            if len(TextList) < 2:
2503                self.ErrorDialog('Not enough data to sum','There must be more than one "IMG" pattern')
2504                return
2505            TextList.append('default_sum_name')               
2506            dlg = self.SumDialog(self,'Sum data','Enter scale for each image in summation','IMG',TextList)
2507            try:
2508                if dlg.ShowModal() == wx.ID_OK:
2509                    imSize = 0
2510                    result = dlg.GetData()
2511                    First = True
2512                    Found = False
2513                    for i,item in enumerate(result[:-1]):
2514                        scale,name = item
2515                        data = DataList[i]
2516                        if scale:
2517                            Found = True                               
2518                            Comments.append("%10.3f %s" % (scale,' * '+name))
2519                            Npix,imagefile = data
2520                            image = G2IO.GetImageData(self,imagefile,imageOnly=True)
2521                            if First:
2522                                newImage = np.zeros_like(image)
2523                                First = False
2524                            if imSize:
2525                                if imSize != Npix:
2526                                    self.ErrorDialog('Image size error','Images to be summed must be same size'+ \
2527                                        '\nExpected:'+str(imSize)+ \
2528                                        '\nFound:   '+str(Npix)+'\nfor '+name)
2529                                    return
2530                                newImage = newImage+scale*image
2531                            else:
2532                                imSize = Npix
2533                                newImage = newImage+scale*image
2534                            del(image)
2535                    if not Found:
2536                        self.ErrorDialog('Image sum error','No nonzero image multipliers found')
2537                        return
2538                       
2539                    newImage = np.asfarray(newImage,dtype=np.float32)                       
2540                    outname = 'IMG '+result[-1]
2541                    Id = 0
2542                    if outname in Names:
2543                        dlg2 = wx.MessageDialog(self,'Overwrite data?','Duplicate data name',wx.OK|wx.CANCEL)
2544                        try:
2545                            if dlg2.ShowModal() == wx.ID_OK:
2546                                Id = G2gd.GetPatternTreeItemId(self,self.root,name)
2547                        finally:
2548                            dlg2.Destroy()
2549                    else:
2550                        Id = self.PatternTree.AppendItem(parent=self.root,text=outname)
2551                    if Id:
2552                        dlg = wx.FileDialog(self, 'Choose sum image filename', '.', '', 
2553                            'G2img files (*.G2img)|*.G2img', 
2554                            wx.SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2555                        if dlg.ShowModal() == wx.ID_OK:
2556                            newimagefile = dlg.GetPath()
2557                            newimagefile = G2IO.FileDlgFixExt(dlg,newimagefile)
2558                            G2IO.PutG2Image(newimagefile,Comments,Data,Npix,newImage)
2559                            Imax = np.amax(newImage)
2560                            Imin = np.amin(newImage)
2561                            newImage = []
2562                            self.PatternTree.SetItemPyData(Id,[imSize,newimagefile])
2563                            self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Comments'),Comments)
2564                        del(newImage)
2565                        if self.imageDefault:
2566                            Data = copy.copy(self.imageDefault)
2567                        Data['showLines'] = True
2568                        Data['ring'] = []
2569                        Data['rings'] = []
2570                        Data['cutoff'] = 10
2571                        Data['pixLimit'] = 20
2572                        Data['ellipses'] = []
2573                        Data['calibrant'] = ''
2574                        Data['range'] = [(Imin,Imax),[Imin,Imax]]
2575                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Image Controls'),Data)                                           
2576                        Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(Imin,Imax),[Imin,Imax]]}
2577                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Masks'),Masks)
2578                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='Stress/Strain'),
2579                            {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
2580                        self.PatternTree.SelectItem(Id)
2581                        self.PatternTree.Expand(Id)
2582                        self.PickId = G2gd.GetPatternTreeItemId(self,self.root,outname)
2583                        self.Image = self.PickId
2584            finally:
2585                dlg.Destroy()
2586                     
2587    def OnAddPhase(self,event):
2588        'Add a new, empty phase to the tree. Called by Data/Add Phase menu'
2589        if not G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2590            sub = self.PatternTree.AppendItem(parent=self.root,text='Phases')
2591        else:
2592            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2593        PhaseName = ''
2594        dlg = wx.TextEntryDialog(None,'Enter a name for this phase','Phase Name Entry','New phase',
2595            style=wx.OK)
2596        if dlg.ShowModal() == wx.ID_OK:
2597            PhaseName = dlg.GetValue()
2598        dlg.Destroy()
2599        sub = self.PatternTree.AppendItem(parent=sub,text=PhaseName)
2600        E,SGData = G2spc.SpcGroup('P 1')
2601        self.PatternTree.SetItemPyData(sub,G2IO.SetNewPhase(Name=PhaseName,SGData=SGData))
2602       
2603    def OnDeletePhase(self,event):
2604        'Delete a phase from the tree. Called by Data/Delete Phase menu'
2605        #Hmm, also need to delete this phase from Reflection Lists for each PWDR histogram
2606        if self.dataFrame:
2607            self.dataFrame.Clear() 
2608        TextList = []
2609        DelList = []
2610        DelItemList = []
2611        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
2612            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
2613        else:
2614            return
2615        if sub:
2616            item, cookie = self.PatternTree.GetFirstChild(sub)
2617            while item:
2618                TextList.append(self.PatternTree.GetItemText(item))
2619                item, cookie = self.PatternTree.GetNextChild(sub, cookie)               
2620            dlg = wx.MultiChoiceDialog(self, 'Which phase to delete?', 'Delete phase', TextList, wx.CHOICEDLG_STYLE)
2621            try:
2622                if dlg.ShowModal() == wx.ID_OK:
2623                    result = dlg.GetSelections()
2624                    for i in result: DelList.append([i,TextList[i]])
2625                    item, cookie = self.PatternTree.GetFirstChild(sub)
2626                    i = 0
2627                    while item:
2628                        if [i,self.PatternTree.GetItemText(item)] in DelList: DelItemList.append(item)
2629                        item, cookie = self.PatternTree.GetNextChild(sub, cookie)
2630                        i += 1
2631                    for item in DelItemList:
2632                        name = self.PatternTree.GetItemText(item)
2633                        self.PatternTree.Delete(item)
2634                        self.G2plotNB.Delete(name)
2635                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2636                    while item:
2637                        name = self.PatternTree.GetItemText(item)
2638                        if 'PWDR' in name:
2639                            Id = G2gd.GetPatternTreeItemId(self,item, 'Reflection Lists')
2640                            refList = self.PatternTree.GetItemPyData(Id)
2641                            for i,item in DelList:
2642                                del(refList[item])
2643                            self.PatternTree.SetItemPyData(Id,refList)
2644                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2645            finally:
2646                dlg.Destroy()
2647               
2648    def OnRenameData(self,event):
2649        'Renames an existing phase. Called by Data/Rename Phase menu'
2650        name = self.PatternTree.GetItemText(self.PickId)     
2651        if 'PWDR' in name or 'HKLF' in name or 'IMG' in name:
2652            dataType = name[:name.index(' ')+1]                 #includes the ' '
2653            dlg = wx.TextEntryDialog(self,'Data name: '+dataType,'Change data name',
2654                defaultValue=name[name.index(' ')+1:])
2655            try:
2656                if dlg.ShowModal() == wx.ID_OK:
2657                    self.PatternTree.SetItemText(self.PickId,dataType+dlg.GetValue())
2658            finally:
2659                dlg.Destroy()
2660       
2661    def GetFileList(self,fileType,skip=None):        #potentially useful?
2662        'Appears unused. Note routine of same name in GSASIIpwdGUI'
2663        fileList = []
2664        Source = ''
2665        id, cookie = self.PatternTree.GetFirstChild(self.root)
2666        while id:
2667            name = self.PatternTree.GetItemText(id)
2668            if fileType in name:
2669                if id == skip:
2670                    Source = name
2671                else:
2672                    fileList.append([False,name,id])
2673            id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2674        if skip:
2675            return fileList,Source
2676        else:
2677            return fileList
2678           
2679    def OnDataDelete(self, event):
2680        '''Delete one or more histograms from data tree. Called by the
2681        Data/DeleteData menu
2682        '''
2683        TextList = ['All Data']
2684        DelList = []
2685        DelItemList = []
2686        ifPWDR = False
2687        ifSASD = False
2688        ifIMG = False
2689        ifHKLF = False
2690        ifPDF = False
2691        if self.PatternTree.GetCount():
2692            item, cookie = self.PatternTree.GetFirstChild(self.root)
2693            while item:
2694                name = self.PatternTree.GetItemText(item)
2695                if name not in ['Notebook','Controls','Covariance','Constraints',
2696                    'Restraints','Phases','Rigid bodies']:
2697                    if 'PWDR' in name: ifPWDR = True
2698                    if 'SASD' in name: ifSASD = True
2699                    if 'IMG' in name: ifIMG = True
2700                    if 'HKLF' in name: ifHKLF = True
2701                    if 'PDF' in name: ifPDF = True
2702                    TextList.append(name)
2703                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2704            if ifPWDR: TextList.insert(1,'All PWDR')
2705            if ifSASD: TextList.insert(1,'All SASD')
2706            if ifIMG: TextList.insert(1,'All IMG')
2707            if ifHKLF: TextList.insert(1,'All HKLF')
2708            if ifPDF: TextList.insert(1,'All PDF')               
2709            dlg = wx.MultiChoiceDialog(self, 'Which data to delete?', 'Delete data', TextList, wx.CHOICEDLG_STYLE)
2710            try:
2711                if dlg.ShowModal() == wx.ID_OK:
2712                    result = dlg.GetSelections()
2713                    for i in result: DelList.append(TextList[i])
2714                    if 'All Data' in DelList:
2715                        DelList = [item for item in TextList if item[:3] != 'All']
2716                    elif 'All PWDR' in DelList:
2717                        DelList = [item for item in TextList if item[:4] == 'PWDR']
2718                    elif 'All SASD' in DelList:
2719                        DelList = [item for item in TextList if item[:4] == 'SASD']
2720                    elif 'All IMG' in DelList:
2721                        DelList = [item for item in TextList if item[:3] == 'IMG']
2722                    elif 'All HKLF' in DelList:
2723                        DelList = [item for item in TextList if item[:4] == 'HKLF']
2724                    elif 'All PDF' in DelList:
2725                        DelList = [item for item in TextList if item[:3] == 'PDF']
2726                    item, cookie = self.PatternTree.GetFirstChild(self.root)
2727                    while item:
2728                        if self.PatternTree.GetItemText(item) in DelList: DelItemList.append(item)
2729                        item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2730                    for item in DelItemList:
2731                        self.PatternTree.Delete(item)
2732                    self.PickId = 0
2733                    wx.CallAfter(G2plt.PlotPatterns,self,True)                        #so plot gets updated
2734            finally:
2735                dlg.Destroy()
2736
2737    def OnFileOpen(self, event, filename=None):
2738        '''Reads in a GSAS-II .gpx project file in response to the
2739        File/Open Project menu button
2740        '''
2741        result = wx.ID_OK
2742        Id = 0
2743        self.EnablePlot = False
2744        if self.PatternTree.GetChildrenCount(self.root,False):
2745            if self.dataFrame:
2746                self.dataFrame.Clear() 
2747            dlg = wx.MessageDialog(
2748                self,
2749                'Do you want to overwrite the current project? '
2750                'Any unsaved changes will be lost. Press OK to continue.',
2751                'Overwrite?',  wx.OK | wx.CANCEL)
2752            try:
2753                result = dlg.ShowModal()
2754                if result == wx.ID_OK:
2755                    self.PatternTree.DeleteChildren(self.root)
2756                    self.GSASprojectfile = ''
2757                    if self.HKL: self.HKL = []
2758                    if self.G2plotNB.plotList:
2759                        self.G2plotNB.clear()
2760            finally:
2761                dlg.Destroy()
2762        if result != wx.ID_OK: return
2763
2764        if not filename:
2765            if self.dataDisplay: self.dataDisplay.Destroy()
2766            dlg = wx.FileDialog(self, 'Choose GSAS-II project file', '.', '', 
2767                'GSAS-II project file (*.gpx)|*.gpx',wx.OPEN|wx.CHANGE_DIR)
2768            try:
2769                if dlg.ShowModal() != wx.ID_OK: return
2770                self.GSASprojectfile = dlg.GetPath()
2771                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2772                self.dirname = dlg.GetDirectory()
2773            finally:
2774                dlg.Destroy()
2775        else:
2776            self.GSASprojectfile = os.path.splitext(filename)[0]+'.gpx'
2777            self.dirname = os.path.split(filename)[0]
2778
2779        G2IO.ProjFileOpen(self)
2780        self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2781        self.PatternTree.Expand(self.root)
2782        self.HKL = []
2783        item, cookie = self.PatternTree.GetFirstChild(self.root)
2784        while item and not Id:
2785            name = self.PatternTree.GetItemText(item)
2786            if name[:4] in ['PWDR','HKLF','IMG ','PDF ','SASD',]:
2787                Id = item
2788            elif name == 'Controls':
2789                data = self.PatternTree.GetItemPyData(item)
2790                if data:
2791                    for item in self.Refine: item.Enable(True)
2792                    self.EnableSeqRefineMenu()
2793            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2794        if Id:
2795            self.EnablePlot = True
2796            self.PatternTree.SelectItem(Id)
2797        self.CheckNotebook()
2798        os.chdir(self.dirname)           # to get Mac/Linux to change directory!
2799
2800    def OnFileClose(self, event):
2801        '''Clears the data tree in response to the
2802        File/New Project menu button. User is given option to save
2803        the project.
2804        '''
2805        if self.dataFrame:
2806            self.dataFrame.Clear()
2807            self.dataFrame.SetLabel('GSAS-II data display') 
2808        dlg = wx.MessageDialog(self, 'Save current project?', ' ', wx.YES | wx.NO | wx.CANCEL)
2809        try:
2810            result = dlg.ShowModal()
2811            if result == wx.ID_OK:
2812                self.OnFileSaveMenu(event)
2813            if result != wx.ID_CANCEL:
2814                self.GSASprojectfile = ''
2815                self.PatternTree.SetItemText(self.root,'Loaded Data: ')
2816                self.PatternTree.DeleteChildren(self.root)
2817                if self.HKL: self.HKL = []
2818                if self.G2plotNB.plotList:
2819                    self.G2plotNB.clear()
2820        finally:
2821            dlg.Destroy()
2822
2823    def OnFileSave(self, event):
2824        '''Save the current project in response to the
2825        File/Save Project menu button
2826        '''
2827       
2828        if self.GSASprojectfile: 
2829            self.PatternTree.SetItemText(self.root,'Loaded Data: '+self.GSASprojectfile)
2830            G2IO.ProjFileSave(self)
2831        else:
2832            self.OnFileSaveas(event)
2833
2834    def OnFileSaveas(self, event):
2835        '''Save the current project in response to the
2836        File/Save as menu button
2837        '''
2838        dlg = wx.FileDialog(self, 'Choose GSAS-II project file name', '.', '', 
2839            'GSAS-II project file (*.gpx)|*.gpx',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2840        try:
2841            if dlg.ShowModal() == wx.ID_OK:
2842                self.GSASprojectfile = dlg.GetPath()
2843                self.GSASprojectfile = G2IO.FileDlgFixExt(dlg,self.GSASprojectfile)
2844                self.PatternTree.SetItemText(self.root,'Saving project as'+self.GSASprojectfile)
2845                self.SetTitle("GSAS-II data tree: "+os.path.split(self.GSASprojectfile)[1])
2846                G2IO.ProjFileSave(self)
2847                os.chdir(dlg.GetDirectory())           # to get Mac/Linux to change directory!
2848        finally:
2849            dlg.Destroy()
2850
2851    def ExitMain(self, event):
2852        '''Called if the main window is closed'''
2853        if self.undofile:
2854            os.remove(self.undofile)
2855        sys.exit()
2856       
2857    def OnFileExit(self, event):
2858        '''Called in response to the File/Quit menu button'''
2859        if self.dataFrame:
2860            self.dataFrame.Clear() 
2861            self.dataFrame.Destroy()
2862        self.Close()
2863       
2864    # def OnExportPatterns(self,event):
2865    #     names = ['All']
2866    #     exports = []
2867    #     item, cookie = self.PatternTree.GetFirstChild(self.root)
2868    #     while item:
2869    #         name = self.PatternTree.GetItemText(item)
2870    #         if 'PWDR' in name:
2871    #             names.append(name)
2872    #         item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2873    #     if names:
2874    #         dlg = wx.MultiChoiceDialog(self,'Select','Powder patterns to export',names)
2875    #         if dlg.ShowModal() == wx.ID_OK:
2876    #             sel = dlg.GetSelections()
2877    #             if sel[0] == 0:
2878    #                 exports = names[1:]
2879    #             else:
2880    #                 for x in sel:
2881    #                     exports.append(names[x])
2882    #         dlg.Destroy()
2883    #     if exports:
2884    #         dlg = wx.FileDialog(self, 'Choose output powder file name', '.', '',
2885    #             'GSAS fxye file (*.fxye)|*.fxye|xye file (*.xye)|*.xye',
2886    #             wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2887    #         try:
2888    #             if dlg.ShowModal() == wx.ID_OK:
2889    #                 powderfile = dlg.GetPath()
2890    #                 powderfile = G2IO.FileDlgFixExt(dlg,powderfile)
2891    #                 if 'fxye' in powderfile:
2892    #                     G2IO.powderFxyeSave(self,exports,powderfile)
2893    #                 else:       #just xye
2894    #                     G2IO.powderXyeSave(self,exports,powderfile)
2895    #         finally:
2896    #             dlg.Destroy()
2897       
2898    def OnExportPeakList(self,event):
2899        dlg = wx.FileDialog(self, 'Choose output peak list file name', '.', '', 
2900            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2901        try:
2902            if dlg.ShowModal() == wx.ID_OK:
2903                self.peaklistfile = dlg.GetPath()
2904                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2905                file = open(self.peaklistfile,'w')               
2906                item, cookie = self.PatternTree.GetFirstChild(self.root)
2907                while item:
2908                    name = self.PatternTree.GetItemText(item)
2909                    if 'PWDR' in name:
2910                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2911                        while item2:
2912                            name2 = self.PatternTree.GetItemText(item2)
2913                            if name2 == 'Peak List':
2914                                peaks = self.PatternTree.GetItemPyData(item2)['peaks']
2915                                file.write("%s \n" % (name+' Peak List'))               
2916                                for peak in peaks:
2917                                    file.write("%10.5f %12.2f %10.3f %10.3f \n" % \
2918                                        (peak[0],peak[2],peak[4],peak[6]))
2919                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2920                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2921                file.close()
2922        finally:
2923            dlg.Destroy()
2924       
2925    def OnExportHKL(self,event):
2926        dlg = wx.FileDialog(self, 'Choose output reflection list file name', '.', '', 
2927            '(*.*)|*.*',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.CHANGE_DIR)
2928        try:
2929            if dlg.ShowModal() == wx.ID_OK:
2930                self.peaklistfile = dlg.GetPath()
2931                self.peaklistfile = G2IO.FileDlgFixExt(dlg,self.peaklistfile)
2932                file = open(self.peaklistfile,'w')               
2933                item, cookie = self.PatternTree.GetFirstChild(self.root)
2934                while item:
2935                    name = self.PatternTree.GetItemText(item)
2936                    if 'PWDR' in name:
2937                        item2, cookie2 = self.PatternTree.GetFirstChild(item)
2938                        while item2:
2939                            name2 = self.PatternTree.GetItemText(item2)
2940                            if name2 == 'Reflection Lists':
2941                                data = self.PatternTree.GetItemPyData(item2)
2942                                phases = data.keys()
2943                                for phase in phases:
2944                                    peaks = data[phase]
2945                                    file.write("%s %s %s \n" % (name,phase,' Reflection List'))
2946                                    file.write('%s \n'%(' h  k  l  m  d-space 2-theta wid F**2'))               
2947                                    for peak in peaks:
2948                                        FWHM = G2pwd.getgamFW(peak[7],peak[6])/50.      #to get delta-2-theta in deg.
2949                                        file.write(" %3d %3d %3d %3d %10.5f %10.5f %10.5f %10.3f \n" % \
2950                                            (int(peak[0]),int(peak[1]),int(peak[2]),int(peak[3]),peak[4],peak[5],FWHM,peak[8]))
2951                            item2, cookie2 = self.PatternTree.GetNextChild(item, cookie2)                           
2952                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)                           
2953                file.close()
2954        finally:
2955            dlg.Destroy()
2956       
2957    def OnExportPDF(self,event):
2958        #need S(Q) and G(R) to be saved here - probably best from selection?
2959        names = ['All']
2960        exports = []
2961        item, cookie = self.PatternTree.GetFirstChild(self.root)
2962        while item:
2963            name = self.PatternTree.GetItemText(item)
2964            if 'PDF' in name:
2965                names.append(name)
2966            item, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2967        if names:
2968            dlg = wx.MultiChoiceDialog(self,'Select','PDF patterns to export',names)
2969            if dlg.ShowModal() == wx.ID_OK:
2970                sel = dlg.GetSelections()
2971                if sel[0] == 0:
2972                    exports = names[1:]
2973                else:
2974                    for x in sel:
2975                        exports.append(names[x])
2976            dlg.Destroy()
2977        if exports:
2978            G2IO.PDFSave(self,exports)
2979       
2980    def OnMakePDFs(self,event):
2981        '''Calculates PDFs
2982        '''
2983        tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
2984        TextList = ['All PWDR']
2985        PDFlist = []
2986        Names = []
2987        if self.PatternTree.GetCount():
2988            id, cookie = self.PatternTree.GetFirstChild(self.root)
2989            while id:
2990                name = self.PatternTree.GetItemText(id)
2991                Names.append(name)
2992                if 'PWDR' in name:
2993                    TextList.append(name)
2994                id, cookie = self.PatternTree.GetNextChild(self.root, cookie)
2995            if len(TextList) == 1:
2996                self.ErrorDialog('Nothing to make PDFs for','There must be at least one "PWDR" pattern')
2997                return
2998            dlg = wx.MultiChoiceDialog(self,'Make PDF controls','Make PDF controls for:',TextList, wx.CHOICEDLG_STYLE)
2999            try:
3000                if dlg.ShowModal() == wx.ID_OK:
3001                    result = dlg.GetSelections()
3002                    for i in result: PDFlist.append(TextList[i])
3003                    if 0 in result:
3004                        PDFlist = [item for item in TextList if item[:4] == 'PWDR']                       
3005                    for item in PDFlist:
3006                        PWDRname = item[4:]
3007                        Id = self.PatternTree.AppendItem(parent=self.root,text='PDF '+PWDRname)
3008                        Data = {
3009                            'Sample':{'Name':item,'Mult':1.0,'Add':0.0},
3010                            'Sample Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},
3011                            'Container':{'Name':'','Mult':-1.0,'Add':0.0},
3012                            'Container Bkg.':{'Name':'','Mult':-1.0,'Add':0.0},'ElList':{},
3013                            'Geometry':'Cylinder','Diam':1.0,'Pack':0.50,'Form Vol':10.0,
3014                            'DetType':'Image plate','ObliqCoeff':0.2,'Ruland':0.025,'QScaleLim':[0,100],
3015                            'Lorch':True,}
3016                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='PDF Controls'),Data)
3017                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='I(Q)'+PWDRname),[])       
3018                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='S(Q)'+PWDRname),[])       
3019                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='F(Q)'+PWDRname),[])       
3020                        self.PatternTree.SetItemPyData(self.PatternTree.AppendItem(Id,text='G(R)'+PWDRname),[])       
3021                for item in self.ExportPDF: item.Enable(True)
3022            finally:
3023                dlg.Destroy()
3024               
3025    def GetPWDRdatafromTree(self,PWDRname):
3026        ''' Returns powder data from GSASII tree
3027
3028        :param str PWDRname: a powder histogram name as obtained from
3029          :meth:`GSASIIstruct.GetHistogramNames`
3030
3031        :returns: PWDRdata = powder data dictionary with
3032          Powder data arrays, Limits, Instrument Parameters,
3033          Sample Parameters           
3034        '''
3035        PWDRdata = {}
3036        try:
3037            PWDRdata.update(self.PatternTree.GetItemPyData(PWDRname)[0])            #wtFactor + ?
3038        except ValueError:
3039            PWDRdata['wtFactor'] = 1.0
3040        PWDRdata['Data'] = self.PatternTree.GetItemPyData(PWDRname)[1]          #powder data arrays
3041        PWDRdata['Limits'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Limits'))
3042        PWDRdata['Background'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Background'))
3043        PWDRdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Instrument Parameters'))
3044        PWDRdata['Sample Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Sample Parameters'))
3045        PWDRdata['Reflection Lists'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,PWDRname,'Reflection Lists'))
3046        if 'ranId' not in PWDRdata:  # patch, add a random Id
3047            PWDRdata['ranId'] = ran.randint(0,sys.maxint)
3048        if 'ranId' not in PWDRdata['Sample Parameters']:  # I hope this becomes obsolete at some point
3049            PWDRdata['Sample Parameters']['ranId'] = PWDRdata['ranId']
3050        return PWDRdata
3051
3052    def GetHKLFdatafromTree(self,HKLFname):
3053        ''' Returns single crystal data from GSASII tree
3054
3055        :param str HKLFname: a single crystal histogram name as obtained
3056          from
3057          :meth:`GSASIIstruct.GetHistogramNames`
3058
3059        :returns: HKLFdata = single crystal data list of reflections
3060
3061        '''
3062        HKLFdata = {}
3063        HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3064#        try:
3065#            HKLFdata.update(self.PatternTree.GetItemPyData(HKLFname)[0])            #wtFactor + ?
3066#        except ValueError:
3067#            HKLFdata['wtFactor'] = 1.0
3068        HKLFdata['Data'] = self.PatternTree.GetItemPyData(HKLFname)[1]
3069        HKLFdata['Instrument Parameters'] = self.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(self,HKLFname,'Instrument Parameters'))
3070        return HKLFdata
3071       
3072    def GetPhaseData(self):
3073        '''Returns a dict with defined phases.
3074        Note routine :func:`GSASIIstrIO.GetPhaseData` also exists to
3075        get same info from GPX file.
3076        '''
3077        phaseData = {}
3078        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3079            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3080        else:
3081            print 'no phases found in GetPhaseData'
3082            sub = None
3083        if sub:
3084            item, cookie = self.PatternTree.GetFirstChild(sub)
3085            while item:
3086                phaseName = self.PatternTree.GetItemText(item)
3087                phaseData[phaseName] =  self.PatternTree.GetItemPyData(item)
3088                if 'ranId' not in phaseData[phaseName]:
3089                    phaseData[phaseName]['ranId'] = ran.randint(0,sys.maxint)         
3090                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3091        return phaseData
3092
3093    def GetPhaseInfofromTree(self):
3094        '''Get the phase names and their rId values,
3095        also the histograms used in each phase.
3096
3097        :returns: (phaseRIdList, usedHistograms) where
3098
3099          * phaseRIdList is a list of random Id values for each phase
3100          * usedHistograms is a dict where the keys are the phase names
3101            and the values for each key are a list of the histogram names
3102            used in each phase.
3103        '''
3104        phaseRIdList = []
3105        usedHistograms = {}
3106        sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3107        if sub:
3108            item, cookie = self.PatternTree.GetFirstChild(sub)
3109            while item:
3110                phaseName = self.PatternTree.GetItemText(item)
3111                ranId = self.PatternTree.GetItemPyData(item).get('ranId')
3112                if ranId: phaseRIdList.append(ranId)
3113                data = self.PatternTree.GetItemPyData(item)
3114                UseList = data['Histograms']
3115                usedHistograms[phaseName] = UseList.keys()
3116                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3117        return phaseRIdList,usedHistograms
3118
3119    def GetPhaseNames(self):
3120        '''Returns a list of defined phases.
3121        Note routine :func:`GSASIIstrIO.GetPhaseNames` also exists to
3122        get same info from GPX file.
3123        '''
3124        phaseNames = []
3125        if G2gd.GetPatternTreeItemId(self,self.root,'Phases'):
3126            sub = G2gd.GetPatternTreeItemId(self,self.root,'Phases')
3127        else:
3128            print 'no phases found in GetPhaseNames'
3129            sub = None
3130        if sub:
3131            item, cookie = self.PatternTree.GetFirstChild(sub)
3132            while item:
3133                phase = self.PatternTree.GetItemText(item)
3134                phaseNames.append(phase)
3135                item, cookie = self.PatternTree.GetNextChild(sub, cookie)
3136        return phaseNames
3137   
3138    def GetHistogramNames(self,hType):
3139        """ Returns a list of histogram names found in the GSASII data tree
3140        Note routine :func:`GSASIIstrIO.GetHistogramNames` also exists to
3141        get same info from GPX file.
3142       
3143        :param str hType: list of histogram types
3144        :return: list of histogram names
3145       
3146        """
3147        HistogramNames = []
3148        if self.PatternTree.GetCount():
3149            item, cookie = self.PatternTree.GetFirstChild(self.root)
3150            while item:
3151                name = self.PatternTree.GetItemText(item)
3152                if name[:4] in hType:
3153                    HistogramNames.append(name)       
3154                item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3155
3156        return HistogramNames
3157
3158                   
3159    def GetUsedHistogramsAndPhasesfromTree(self):
3160        ''' Returns all histograms that are found in any phase
3161        and any phase that uses a histogram.
3162        This also assigns numbers to used phases and histograms by the
3163        order they appear in the file.
3164        Note routine :func:`GSASIIstrIO.GetUsedHistogramsAndPhasesfromTree` also exists to
3165        get same info from GPX file.
3166
3167        :returns: (Histograms,Phases)
3168
3169            * Histograms = dictionary of histograms as {name:data,...}
3170            * Phases = dictionary of phases that use histograms
3171        '''
3172        Histograms = {}
3173        Phases = {}
3174        phaseNames = self.GetPhaseNames()
3175        phaseData = self.GetPhaseData()
3176        histoList = self.GetHistogramNames(['PWDR','HKLF'])
3177
3178        for phase in phaseData:
3179            Phase = phaseData[phase]
3180            pId = phaseNames.index(phase)
3181            Phase['pId'] = pId
3182            if Phase['Histograms']:
3183                if phase not in Phases:
3184                    Phases[phase] = Phase
3185                for hist in Phase['Histograms']:
3186                    if 'Use' not in Phase['Histograms'][hist]:      #patch: add Use flag as True
3187                        Phase['Histograms'][hist]['Use'] = True         
3188                    if hist not in Histograms and Phase['Histograms'][hist]['Use']:
3189                        item = G2gd.GetPatternTreeItemId(self,self.root,hist)
3190                        if item:
3191                            if 'PWDR' in hist[:4]: 
3192                                Histograms[hist] = self.GetPWDRdatafromTree(item)
3193                            elif 'HKLF' in hist[:4]:
3194                                Histograms[hist] = self.GetHKLFdatafromTree(item)
3195                            hId = histoList.index(hist)
3196                            Histograms[hist]['hId'] = hId
3197                        else: # would happen if a referenced histogram were renamed or deleted
3198                            print('For phase "'+str(phase)+
3199                                  '" unresolved reference to histogram "'+str(hist)+'"')
3200        #G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
3201        G2obj.IndexAllIds(Histograms=Histograms,Phases=phaseData)
3202        return Histograms,Phases
3203       
3204    def MakeLSParmDict(self):
3205        '''Load all parameters used for computation from the tree into a
3206        dict of paired values [value, refine flag]. Note that this is
3207        different than the parmDict used in the refinement, which only has
3208        values.
3209
3210        Note that similar things are done in
3211        :meth:`GSASIIIO.ExportBaseclass.loadParmDict` (from the tree) and
3212        :func:`GSASIIstrMain.Refine` and :func:`GSASIIstrMain.SeqRefine` (from
3213        a GPX file).
3214
3215        :returns: (parmDict,varyList) where:
3216
3217         * parmDict is a dict with values and refinement flags
3218           for each parameter and
3219         * varyList is a list of variables (refined parameters).
3220        '''
3221        parmDict = {}
3222        Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
3223        for phase in Phases:
3224            if 'pId' not in Phases[phase]:
3225                self.ErrorDialog('View parameter error','You must run least squares at least once')
3226                raise Exception,'No pId for phase '+str(phase)
3227        rigidbodyDict = self.PatternTree.GetItemPyData(   
3228            G2gd.GetPatternTreeItemId(self,self.root,'Rigid bodies'))
3229        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
3230        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
3231        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable = G2stIO.GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False)       
3232        hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
3233        histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
3234        varyList = rbVary+phaseVary+hapVary+histVary
3235        parmDict.update(rbDict)
3236        parmDict.update(phaseDict)
3237        parmDict.update(hapDict)
3238        parmDict.update(histDict)
3239        for parm in parmDict:
3240            if parm.split(':')[-1] in ['Azimuth','Gonio. radius','Lam1','Lam2',
3241                'Omega','Chi','Phi','nDebye','nPeaks']:
3242                parmDict[parm] = [parmDict[parm],'-']
3243            elif parm.split(':')[-2] in ['Ax','Ay','Az','SHmodel','SHord']:
3244                parmDict[parm] = [parmDict[parm],'-']
3245            elif parm in varyList:
3246                parmDict[parm] = [parmDict[parm],'T']
3247            else:
3248                parmDict[parm] = [parmDict[parm],'F']
3249        # for i in parmDict: print i,'\t',parmDict[i]
3250        # fl = open('parmDict.dat','wb')
3251        # import cPickle
3252        # cPickle.dump(parmDict,fl,1)
3253        # fl.close()
3254        return parmDict,varyList
3255
3256    def ShowLSParms(self,event):
3257        '''Displays a window showing all parameters in the refinement.
3258        Called from the Calculate/View LS Parms menu.
3259        '''
3260        parmDict,varyList = self.MakeLSParmDict()
3261        parmValDict = {}
3262        for i in parmDict:
3263            parmValDict[i] = parmDict[i][0]
3264           
3265        reqVaryList = tuple(varyList) # save requested variables
3266        try:
3267            # process constraints
3268            sub = G2gd.GetPatternTreeItemId(self,self.root,'Constraints') 
3269            Constraints = self.PatternTree.GetItemPyData(sub)
3270            constList = []
3271            for item in Constraints:
3272                if item.startswith('_'): continue
3273                constList += Constraints[item]
3274            G2mv.InitVars()
3275            constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
3276            groups,parmlist = G2mv.GroupConstraints(constrDict)
3277            G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict)
3278            G2mv.Map2Dict(parmValDict,varyList)
3279        except:
3280            pass
3281        dlg = G2gd.ShowLSParms(self,'Least Squares Parameters',parmValDict,varyList,reqVaryList)
3282        dlg.ShowModal()
3283        dlg.Destroy()
3284       
3285    def OnRefine(self,event):
3286        '''Perform a refinement.
3287        Called from the Calculate/Refine menu.
3288        '''       
3289        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3290        if Id:
3291            dlg = wx.MessageDialog(
3292                self,
3293                'Your last refinement was sequential. Continue with "Refine", removing previous sequential results?',
3294                'Remove sequential results?',wx.OK|wx.CANCEL)
3295            if dlg.ShowModal() == wx.ID_OK:
3296                self.PatternTree.Delete(Id)
3297                dlg.Destroy()
3298            else:
3299                dlg.Destroy()
3300                return
3301
3302        self.OnFileSave(event)
3303        # check that constraints are OK here
3304        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3305        if errmsg:
3306            print('Error in constraints:\n'+errmsg+
3307                  '\nRefinement not possible')
3308            self.ErrorDialog('Constraint Error',
3309                             'Error in constraints:\n'+errmsg+
3310                             '\nRefinement not possible')
3311            return
3312        if warnmsg:
3313            print('Conflict between refinment flag settings and constraints:\n'+
3314                  warnmsg+'\nRefinement not possible')
3315            self.ErrorDialog('Refinement Flag Error',
3316                             'Conflict between refinment flag settings and constraints:\n'+
3317                             warnmsg+
3318                             '\nRefinement not possible')
3319            return
3320        #works - but it'd be better if it could restore plots
3321        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0, 
3322            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3323        screenSize = wx.ClientDisplayRect()
3324        Size = dlg.GetSize()
3325        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3326        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3327        dlg.SetSize(Size)
3328        Rw = 100.00
3329        try:
3330            Rw = G2stMn.Refine(self.GSASprojectfile,dlg)
3331        finally:
3332            dlg.Destroy()
3333        oldId =  self.PatternTree.GetSelection()        #retain current selection
3334        oldName = self.PatternTree.GetItemText(oldId)
3335        parentId = self.PatternTree.GetItemParent(oldId)
3336        parentName = ''
3337        if parentId:
3338            parentName = self.PatternTree.GetItemText(parentId)     #find the current data tree name
3339        dlg = wx.MessageDialog(self,'Load new result?','Refinement results, Rw =%.3f'%(Rw),wx.OK|wx.CANCEL)
3340        try:
3341            if dlg.ShowModal() == wx.ID_OK:
3342                Id = 0
3343                self.PatternTree.DeleteChildren(self.root)
3344                if self.HKL: self.HKL = []
3345                if self.G2plotNB.plotList:
3346                    self.G2plotNB.clear()
3347                G2IO.ProjFileOpen(self)
3348                item, cookie = self.PatternTree.GetFirstChild(self.root)
3349                while item and not Id:
3350                    name = self.PatternTree.GetItemText(item)
3351                    if name[:4] in ['PWDR','HKLF']:
3352                        Id = item
3353                    item, cookie = self.PatternTree.GetNextChild(self.root, cookie)               
3354                if Id:
3355                    self.PatternTree.SelectItem(Id)
3356                if parentName:
3357                    parentId = G2gd.GetPatternTreeItemId(self, self.root, parentName)
3358                    if parentId:
3359                        itemId = G2gd.GetPatternTreeItemId(self, parentId, oldName)
3360                    else:
3361                        itemId = G2gd.GetPatternTreeItemId(self, self.root, oldName)
3362                    self.PatternTree.SelectItem(itemId)
3363        finally:
3364            dlg.Destroy()
3365
3366    def OnSeqRefine(self,event):
3367        '''Perform a sequential refinement.
3368        Called from the Calculate/Sequential refine menu.
3369        '''       
3370        Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3371        if not Id:
3372            Id = self.PatternTree.AppendItem(self.root,text='Sequential results')
3373            self.PatternTree.SetItemPyData(Id,{})           
3374        self.OnFileSave(event)
3375        # check that constraints are OK here
3376        errmsg, warnmsg = G2stIO.CheckConstraints(self.GSASprojectfile)
3377        if errmsg:
3378            print('Error in constraints:\n'+errmsg+
3379                  '\nRefinement not possible')
3380            self.ErrorDialog('Constraint Error',
3381                             'Error in constraints:\n'+errmsg+
3382                             '\nRefinement not possible')
3383            return
3384        if warnmsg:
3385            print('Conflict between refinment flag settings and constraints:\n'+
3386                  warnmsg+'\nRefinement not possible')
3387            self.ErrorDialog('Refinement Flag Error',
3388                             'Conflict between refinment flag settings and constraints:\n'+
3389                             warnmsg+'\nRefinement not possible')
3390            return
3391        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0, 
3392            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT)
3393        screenSize = wx.ClientDisplayRect()
3394        Size = dlg.GetSize()
3395        Size = (int(Size[0]*1.2),Size[1]) # increase size a bit along x
3396        dlg.SetPosition(wx.Point(screenSize[2]-Size[0]-305,screenSize[1]+5))
3397        dlg.SetSize(Size)
3398        try:
3399            G2stMn.SeqRefine(self.GSASprojectfile,dlg)
3400        finally:
3401            dlg.Destroy()       
3402        dlg = wx.MessageDialog(self,'Load new result?','Refinement results',wx.OK|wx.CANCEL)
3403        try:
3404            if dlg.ShowModal() == wx.ID_OK:
3405                Id = 0
3406                self.PatternTree.DeleteChildren(self.root)
3407                if self.HKL: self.HKL = []
3408                if self.G2plotNB.plotList:
3409                    self.G2plotNB.clear()
3410                G2IO.ProjFileOpen(self)
3411                Id = G2gd.GetPatternTreeItemId(self,self.root,'Sequential results')
3412                self.PatternTree.SelectItem(Id)
3413
3414        finally:
3415            dlg.Destroy()
3416       
3417    def ErrorDialog(self,title,message,parent=None, wtype=wx.OK):
3418        'Display an error message'
3419        result = None
3420        if parent is None:
3421            dlg = wx.MessageDialog(self, message, title,  wtype)
3422        else:
3423            dlg = wx.MessageDialog(parent, message, title,  wtype)
3424            dlg.CenterOnParent() # not working on Mac
3425        try:
3426            result = dlg.ShowModal()
3427        finally:
3428            dlg.Destroy()
3429        return result
3430
3431class GSASIImain(wx.App):
3432    '''Defines a wxApp for GSAS-II
3433
3434    Creates a wx frame (self.main) which contains the display of the
3435    data tree.
3436    '''
3437    def OnInit(self):
3438        '''Called automatically when the app is created.'''
3439        if '2.7' not in sys.version[:5]:
3440            dlg = wx.MessageDialog(None, 
3441                'GSAS-II requires Python 2.7.x\n Yours is '+sys.version[:5],
3442                'Python version error',  wx.OK)
3443            try:
3444                result = dlg.ShowModal()
3445            finally:
3446                dlg.Destroy()
3447            sys.exit()
3448        self.main = GSASII(None)
3449        self.main.Show()
3450        self.SetTopWindow(self.main)
3451        # DEBUG: jump to sequential results
3452        #Id = G2gd.GetPatternTreeItemId(self.main,self.main.root,'Sequential results')
3453        #self.main.PatternTree.SelectItem(Id)
3454        # end DEBUG
3455        return True
3456    # def MacOpenFile(self, filename):
3457    #     '''Called on Mac every time a file is dropped on the app when it is running,
3458    #     treat this like a File/Open project menu action.
3459    #     Should be ignored on other platforms
3460    #     '''
3461    #     # PATCH: Canopy 1.4 script main seems dropped on app; ignore .py files
3462    #     print 'MacOpen',filename
3463    #     if os.path.splitext(filename)[1] == '.py': return
3464    #     # end PATCH
3465    #     self.main.OnFileOpen(None,filename)
3466    # removed because this gets triggered when a file is on the command line in canopy 1.4 -- not likely used anyway
3467   
3468def main():
3469    '''Start up the GSAS-II application'''
3470    #application = GSASIImain() # don't redirect output, someday we
3471    # may want to do this if we can
3472    application = GSASIImain(0)
3473    if wxInspector:
3474        import wx.lib.inspection as wxeye
3475        wxeye.InspectionTool().Show()
3476
3477    #application.main.OnRefine(None)
3478    application.MainLoop()
3479   
3480if __name__ == '__main__':
3481    # print versions
3482    print "Python module versions loaded:"
3483    print "python:     ",sys.version[:5]
3484    print "wxpython:   ",wx.__version__
3485    print "matplotlib: ",mpl.__version__
3486    print "numpy:      ",np.__version__
3487    print "scipy:      ",sp.__version__
3488    print "OpenGL:     ",ogl.__version__
3489    try:
3490        from PIL import Image
3491        try:
3492            from PIL import PILLOW_VERSION
3493            version = PILLOW_VERSION
3494        except:
3495            version = Image.VERSION
3496        print "pillow:     ",version
3497    except ImportError:
3498        try:
3499            import Image
3500            print "Image (PIL):",Image.VERSION
3501        except ImportError:
3502            print "Image module not present; Note that PIL (Python Imaging Library) or pillow is needed for some image operations"
3503    try:
3504        import mkl
3505        print "Max threads ",mkl.get_max_threads()
3506    except:
3507        pass
3508    import platform
3509    print "Platform info:",sys.platform,platform.architecture()[0],platform.machine()
3510    #print "wxPython description",wx.PlatformInfo
3511    print "This is GSAS-II version:     ",__version__,' revision '+str(GSASIIpath.GetVersionNumber())
3512    main() # start the GUI
Note: See TracBrowser for help on using the repository browser.