source: trunk/GSASIIimgGUI.py @ 2826

Last change on this file since 2826 was 2826, checked in by vondreele, 6 years ago

add covariances to sequential strain ring fit results

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 161.3 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2017-05-11 02:28:05 +0000 (Thu, 11 May 2017) $
5# $Author: vondreele $
6# $Revision: 2826 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 2826 2017-05-11 02:28:05Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIimgGUI: Image GUI*
12-------------------------
13
14Control image display and processing
15
16'''
17import os
18import copy
19import glob
20import re
21import math
22import sys
23import wx
24import wx.lib.scrolledpanel as wxscroll
25import wx.lib.mixins.listctrl  as  listmix
26import matplotlib as mpl
27import numpy as np
28import GSASIIpath
29GSASIIpath.SetVersionNumber("$Revision: 2826 $")
30import GSASIIimage as G2img
31import GSASIImath as G2mth
32import GSASIIElem as G2elem
33import GSASIIpwdGUI as G2pdG
34import GSASIIplot as G2plt
35import GSASIIIO as G2IO
36import GSASIIgrid as G2gd
37import GSASIIctrls as G2G
38import GSASIIobj as G2obj
39import GSASIIpy3 as G2py3
40
41VERY_LIGHT_GREY = wx.Colour(235,235,235)
42WACV = wx.ALIGN_CENTER_VERTICAL
43
44# trig functions in degrees
45sind = lambda x: math.sin(x*math.pi/180.)
46tand = lambda x: math.tan(x*math.pi/180.)
47cosd = lambda x: math.cos(x*math.pi/180.)
48asind = lambda x: 180.*math.asin(x)/math.pi
49tth2q = lambda t,w:4.0*math.pi*sind(t/2.0)/w
50tof2q = lambda t,C:2.0*math.pi*C/t
51atand = lambda x: 180.*math.atan(x)/math.pi
52atan2d = lambda y,x: 180.*math.atan2(y,x)/math.pi
53
54################################################################################
55##### Image Data
56################################################################################
57
58def GetImageZ(G2frame,data,newRange=False):
59    '''Gets image & applies dark, background & flat background corrections
60    :param wx.Frame G2frame: main GSAS-II frame
61    param: dict data: Image Controls dictionary
62    return: array sumImg: corrected image for background/dark/flat back
63    '''
64   
65    Npix,imagefile,imagetag = G2IO.GetCheckImageFile(G2frame,G2frame.Image)
66    formatName = data.get('formatName','')
67    sumImg = G2IO.GetImageData(G2frame,imagefile,True,ImageTag=imagetag,FormatName=formatName)
68    if sumImg is None:
69        return []
70    darkImg = False
71    if 'dark image' in data:
72        darkImg,darkScale = data['dark image']
73        if darkImg:
74            Did = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, darkImg)
75            if Did:
76                Ddata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Did,'Image Controls'))
77                dformatName = Ddata.get('formatName','')
78                Npix,darkfile,imagetag = G2IO.GetCheckImageFile(G2frame,Did)
79                darkImage = G2IO.GetImageData(G2frame,darkfile,True,ImageTag=imagetag,FormatName=dformatName)
80                if darkImg is not None:               
81                    sumImg += np.array(darkImage*darkScale,dtype='int32')
82    if 'background image' in data:
83        backImg,backScale = data['background image']           
84        if backImg:     #ignores any transmission effect in the background image
85            Bid = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, backImg)
86            if Bid:
87                Npix,backfile,imagetag = G2IO.GetCheckImageFile(G2frame,Bid)
88                Bdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Bid,'Image Controls'))
89                bformatName = Bdata.get('formatName','')
90                backImage = G2IO.GetImageData(G2frame,backfile,True,ImageTag=imagetag,FormatName=bformatName)
91                if darkImg and backImage is not None:
92                    backImage += np.array(darkImage*darkScale/backScale,dtype='int32')
93                if backImage is not None:
94                    sumImg += np.array(backImage*backScale,dtype='int32')
95#    if darkImg: del darkImg         #force cleanup
96#    if backImg: del backImg
97    sumImg -= int(data.get('Flat Bkg',0))
98    Imax = np.max(sumImg)
99    if 'range' not in data or newRange:
100        data['range'] = [(0,Imax),[0,Imax]]
101    return np.asarray(sumImg,dtype='int32')
102
103def UpdateImageData(G2frame,data):
104   
105    def OnPixVal(invalid,value,tc):
106        G2plt.PlotExposedImage(G2frame,newPlot=True,event=tc.event)
107       
108    if G2frame.dataDisplay:
109        G2frame.dataDisplay.Destroy()
110    if not G2frame.dataFrame.GetStatusBar():
111        G2frame.dataFrame.CreateStatusBar()
112    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
113    G2frame.ImageZ = GetImageZ(G2frame,data)
114    mainSizer = wx.BoxSizer(wx.VERTICAL)
115    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,
116        label='Do not change anything here unless you are absolutely sure!'),0,WACV)
117    mainSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Image size: %d by %d'%(data['size'][0],data['size'][1])),0,WACV)
118    pixSize = wx.FlexGridSizer(0,4,5,5)
119    pixLabels = [u' Pixel X-dimension (\xb5m)',u' Pixel Y-dimension (\xb5m)']
120    for i,[pixLabel,pix] in enumerate(zip(pixLabels,data['pixelSize'])):
121        pixSize.Add(wx.StaticText(G2frame.dataDisplay,label=pixLabel),0,WACV)
122        pixVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['pixelSize'],i,nDig=(10,3),
123            typeHint=float,OnLeave=OnPixVal)
124        pixSize.Add(pixVal,0,WACV)
125    mainSizer.Add(pixSize,0)
126    distSizer = wx.BoxSizer(wx.HORIZONTAL)
127    distSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Set detector distance: '),0,WACV)
128    if 'setdist' not in data:
129        data['setdist'] = data['distance']
130    distSizer.Add(G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'setdist',nDig=(10,4),
131            typeHint=float),0,WACV)
132    distSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Polarization: '),0,WACV)
133    if 'PolaVal' not in data:       #patch
134        data['PolaVal'] = [0.99,False]
135    distSizer.Add(G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['PolaVal'],0,nDig=(10,4),
136        min=0.,max=1.,typeHint=float),0,WACV)
137    mainSizer.Add(distSizer,0)
138    mainSizer.Layout()   
139    G2frame.dataDisplay.SetSizer(mainSizer)
140    fitSize = mainSizer.Fit(G2frame.dataFrame)
141    G2frame.dataFrame.setSizePosLeft(fitSize)
142
143################################################################################
144##### Image Controls
145################################################################################                   
146def UpdateImageControls(G2frame,data,masks,IntegrateOnly=False):
147    '''Shows and handles the controls on the "Image Controls"
148    data tree entry
149    '''
150    import ImageCalibrants as calFile
151#patch
152    if 'Flat Bkg' not in data:
153        data['Flat Bkg'] = 0.0
154    if 'GonioAngles' not in data:
155        data['GonioAngles'] = [0.,0.,0.]
156    if 'DetDepth' not in data:
157        data['DetDepth'] = 0.
158    if 'SampleAbs' not in data:
159        data['SampleShape'] = 'Cylinder'
160        data['SampleAbs'] = [0.0,False]
161    if 'binType' not in data:
162        if 'PWDR' in data['type']:
163            data['binType'] = '2-theta'
164        elif 'SASD' in data['type']:
165            data['binType'] = 'log(q)'
166    if 'varyList' not in data:
167        data['varyList'] = {'dist':True,'det-X':True,'det-Y':True,'tilt':True,'phi':True,'dep':False,'wave':False}
168    if data['DetDepth'] > 0.5:
169        data['DetDepth'] /= data['distance']
170#end patch
171
172# Menu items
173           
174    def OnCalibrate(event):
175        G2frame.dataFrame.GetStatusBar().SetStatusText('Select > 4 points on 1st used ring; LB to pick, RB on point to delete else RB to finish')
176        G2frame.ifGetRing = True
177               
178    def OnRecalibrate(event):
179        G2img.ImageRecalibrate(G2frame,data,masks)
180        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
181       
182    def OnRecalibAll(event):
183        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
184        dlg = G2G.G2MultiChoiceDialog(G2frame,'Image calibration controls','Select images to recalibrate:',Names)
185        try:
186            if dlg.ShowModal() == wx.ID_OK:
187                Id =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,'Sequential image calibration results')
188                if Id:
189                    SeqResult = G2frame.PatternTree.GetItemPyData(Id)
190                else:
191                    Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text='Sequential image calibration results')
192                SeqResult = {'SeqPseudoVars':{},'SeqParFitEqList':[]}
193                items = dlg.GetSelections()
194                G2frame.EnablePlot = False
195                for item in items:
196                    name = Names[item]
197                    print 'calibrating',name
198                    G2frame.Image = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
199                    Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls'))
200                    G2frame.ImageZ = GetImageZ(G2frame,Data)
201                    Data['setRings'] = True
202                    Mid = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Masks')
203                    Masks = G2frame.PatternTree.GetItemPyData(Mid)
204                    vals,varyList,sigList,parmDict = G2img.ImageRecalibrate(G2frame,Data,Masks)
205                    sigList = list(sigList)
206                    if 'dist' not in varyList:
207                        vals.append(parmDict['dist'])
208                        varyList.append('dist')
209                        sigList.append(None)
210                    vals.append(Data['setdist'])
211                    varyList.append('setdist')
212                    sigList.append(None)
213                    SeqResult[name] = {'variables':vals,'varyList':varyList,'sig':sigList,'Rvals':[],
214                        'covMatrix':np.eye(len(varyList)),'title':name,'parmDict':parmDict}
215                SeqResult['histNames'] = Names               
216                G2frame.PatternTree.SetItemPyData(Id,SeqResult)
217        finally:
218            dlg.Destroy()
219        print 'All selected images recalibrated - results in Sequential image calibration results'
220        G2frame.G2plotNB.Delete('Sequential refinement')    #clear away probably invalid plot
221        G2plt.PlotExposedImage(G2frame,event=None)
222        G2frame.PatternTree.SelectItem(Id)
223       
224    def OnClearCalib(event):
225        data['ring'] = []
226        data['rings'] = []
227        data['ellipses'] = []
228        G2plt.PlotExposedImage(G2frame,event=event)
229           
230    def ResetThresholds():
231        Imin = max(0.,np.min(G2frame.ImageZ))
232        Imax = np.max(G2frame.ImageZ)
233        data['range'] = [(0,Imax),[Imin,Imax]]
234        masks['Thresholds'] = [(0,Imax),[Imin,Imax]]
235        MaxSizer.GetChildren()[2].Window.SetValue(Imax)   #tricky
236        MaxSizer.GetChildren()[5].Window.SetValue(Imin)   #tricky
237         
238    def OnIntegrate(event):
239        '''Integrate image in response to a menu event or from the AutoIntegrate
240        dialog. In the latter case, event=None.
241        '''
242        CleanupMasks(masks)
243        blkSize = 128   #this seems to be optimal; will break in polymask if >1024
244        sumImg = GetImageZ(G2frame,data)
245        wx.BeginBusyCursor()
246        try:
247            G2frame.Integrate = G2img.ImageIntegrate(sumImg,data,masks,blkSize)           
248        finally:
249            wx.EndBusyCursor()   
250        G2frame.PauseIntegration = G2frame.Integrate[-1]
251        del sumImg  #force cleanup
252        Id = G2IO.SaveIntegration(G2frame,G2frame.PickId,data,(event is None))
253        G2frame.PatternId = Id
254        G2frame.PatternTree.SelectItem(Id)
255        G2frame.PatternTree.Expand(Id)
256        for item in G2frame.MakePDF: item.Enable(True)
257       
258    def OnIntegrateAll(event):
259        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
260        dlg = G2G.G2MultiChoiceDialog(G2frame,'Image integration controls','Select images to integrate:',Names)
261        try:
262            if dlg.ShowModal() == wx.ID_OK:
263                items = dlg.GetSelections()
264                G2frame.EnablePlot = False
265                dlgp = wx.ProgressDialog("Elapsed time","2D image integrations",len(items)+1,
266                    style = wx.PD_ELAPSED_TIME|wx.PD_CAN_ABORT)
267                try:
268                    for icnt,item in enumerate(items):
269                        GoOn = dlgp.Update(icnt)
270                        if not GoOn[0]:
271                            break
272                        name = Names[item]
273                        G2frame.Image = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
274                        CId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')
275                        Data = G2frame.PatternTree.GetItemPyData(CId)
276                        Masks = G2frame.PatternTree.GetItemPyData(
277                            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Masks'))
278                        image = GetImageZ(G2frame,Data)
279                        blkSize = 128   #this seems to be optimal; will break in polymask if >1024
280                        G2frame.Integrate = G2img.ImageIntegrate(image,Data,Masks,blkSize)
281                        del image   #force cleanup
282                        pId = G2IO.SaveIntegration(G2frame,CId,Data)
283                finally:   
284                    dlgp.Destroy()
285                    G2frame.EnablePlot = True
286                    G2frame.PatternTree.SelectItem(pId)
287                    G2frame.PatternTree.Expand(pId)
288                    G2frame.PatternId = pId
289        finally:
290            dlg.Destroy()
291       
292    def OnCopyControls(event):
293        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
294        if len(Names) == 1:
295            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
296            return
297        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
298        Names.pop(Names.index(Source))
299# select targets & do copy
300        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy image controls','Copy controls from '+Source+' to:',Names)
301        try:
302            if dlg.ShowModal() == wx.ID_OK:
303                items = dlg.GetSelections()
304                G2frame.EnablePlot = False
305                for item in items:
306                    name = Names[item]
307                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
308                    CId = G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls')
309                    oldData = copy.deepcopy(G2frame.PatternTree.GetItemPyData(CId))
310                    Data = copy.deepcopy(data)
311                    Data['range'][0] = oldData['range'][0]
312                    Data['size'] = oldData['size']
313                    Data['GonioAngles'] = oldData.get('GonioAngles', [0.,0.,0.])
314                    Data['ring'] = []
315                    Data['rings'] = []
316                    Data['ellipses'] = []
317                    if name == Data['dark image'][0]:
318                        Data['dark image'] = ['',-1.]
319                    if name == Data['background image'][0]:
320                        Data['background image'] = ['',-1.]
321                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Image Controls'),Data)
322        finally:
323            dlg.Destroy()
324            G2frame.PatternTree.SelectItem(G2frame.PickId)
325           
326    def OnCopySelected(event):
327        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
328        if len(Names) == 1:
329            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
330            return
331        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
332        # Assemble a list of item labels
333        keyList = ['type','wavelength','calibrant','distance','center','Oblique',
334                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth','setdist',
335                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
336                    'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
337                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
338        keyList.sort(key=lambda s: s.lower())
339        keyText = [i+' = '+str(data[i]) for i in keyList]
340        # sort both lists together, ordered by keyText
341        selectedKeys = []
342        dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame,'Select which image controls\nto copy',
343            'Select image controls', keyText)
344        try:
345            if dlg.ShowModal() == wx.ID_OK:
346                selectedKeys = [keyList[i] for i in dlg.GetSelections()]
347        finally:
348            dlg.Destroy()
349        if not selectedKeys: return # nothing to copy
350        copyDict = {}
351        for parm in selectedKeys:
352            copyDict[parm] = data[parm]
353        dlg = G2G.G2MultiChoiceDialog(G2frame.dataFrame,'Copy image controls from\n'+Source+' to...',
354            'Copy image controls', Names)
355        try:
356            if dlg.ShowModal() == wx.ID_OK:
357                result = dlg.GetSelections()
358                for i in result: 
359                    item = Names[i]
360                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,item)
361                    Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
362                    Controls.update(copy.deepcopy(copyDict))
363        finally:
364            dlg.Destroy()           
365               
366    def OnSaveControls(event):
367        pth = G2G.GetExportPath(G2frame)
368        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
369            'image control files (*.imctrl)|*.imctrl',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
370        try:
371            if dlg.ShowModal() == wx.ID_OK:
372                filename = dlg.GetPath()
373                # make sure extension is .imctrl
374                filename = os.path.splitext(filename)[0]+'.imctrl'
375                WriteControls(filename,data)
376        finally:
377            dlg.Destroy()
378
379    def WriteControls(filename,data):
380        File = open(filename,'w')
381        keys = ['type','wavelength','calibrant','distance','center','Oblique',
382            'tilt','rotation','azmthOff','fullIntegrate','LRazimuth','setdist',
383            'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
384            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList',
385            'binType','SampleShape','PolaVal','SampleAbs','dark image','background image',
386            ]
387        for key in keys:
388            if key not in data:     #uncalibrated!
389                continue
390            File.write(key+':'+str(data[key])+'\n')
391        File.close()
392       
393    def OnSaveMultiControls(event):
394        '''Save controls from multiple images
395        '''
396        imglist = []
397        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
398        while item:
399            name = G2frame.PatternTree.GetItemText(item)
400            if name.startswith('IMG '): 
401                imglist.append(name)               
402            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
403        if not imglist:
404            print('No images!')
405            return
406        dlg = G2G.G2MultiChoiceDialog(G2frame, 'Which images to select?',
407            'Select images', imglist, wx.CHOICEDLG_STYLE)
408        try:
409            if dlg.ShowModal() == wx.ID_OK:
410                treeEntries = [imglist[i] for i in dlg.GetSelections()]
411        finally:
412            dlg.Destroy()
413        if not treeEntries:
414            print('No images selected!')
415            return
416        pth = G2G.GetExportPath(G2frame)
417        dlg = wx.DirDialog(
418            G2frame, 'Select directory for output files',pth,wx.DD_DEFAULT_STYLE)
419        dlg.CenterOnParent()
420        outdir = None
421        try:
422            if dlg.ShowModal() == wx.ID_OK:
423                outdir = dlg.GetPath()
424        finally:
425            dlg.Destroy()
426        if not outdir:
427            print('No directory')
428            return
429        for img in treeEntries:
430            item = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
431            data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
432                G2frame,item,'Image Controls'))
433            Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(item)
434            filename = os.path.join(outdir,
435                                    os.path.splitext(os.path.split(imagefile)[1])[0]
436                                    + '.imctrl')
437            print('writing '+filename)
438            WriteControls(filename,data)
439           
440    def OnLoadControls(event):
441        cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type','Oblique',
442            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
443            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg','varyList','setdist',
444            'PolaVal','SampleAbs','dark image','background image']
445        pth = G2G.GetImportPath(G2frame)
446        if not pth: pth = '.'
447        dlg = wx.FileDialog(G2frame, 'Choose image controls file', pth, '', 
448            'image control files (*.imctrl)|*.imctrl',wx.OPEN)
449        try:
450            if dlg.ShowModal() == wx.ID_OK:
451                filename = dlg.GetPath()
452                File = open(filename,'r')
453                save = {}
454                S = File.readline()
455                while S:
456                    if S[0] == '#':
457                        S = File.readline()
458                        continue
459                    [key,val] = S.strip().split(':',1)
460                    if key in ['type','calibrant','binType','SampleShape',]:    #strings
461                        save[key] = val
462                    elif key in ['varyList',]:
463                        save[key] = eval(val)   #dictionary
464                    elif key in ['rotation']:
465                        save[key] = float(val)
466                    elif key in ['center',]:
467                        if ',' in val:
468                            save[key] = eval(val)
469                        else:
470                            vals = val.strip('[] ').split()
471                            save[key] = [float(vals[0]),float(vals[1])] 
472                    elif key in cntlList:
473                        save[key] = eval(val)
474                    S = File.readline()
475                data.update(save)
476                # next line removed. Previous updates tree contents. The next
477                # makes a copy of data, puts it into tree and "disconnects" data
478                # from tree contents (later changes to data are lost!)
479                #G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'),copy.deepcopy(data))
480                File.close()
481        finally:
482            dlg.Destroy()
483        G2frame.ImageZ = GetImageZ(G2frame,data)
484        ResetThresholds()
485        G2plt.PlotExposedImage(G2frame,event=event)
486        wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
487       
488    def OnTransferAngles(event):
489        '''Sets the integration range for the selected Images based on the difference in detector distance
490        '''
491        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
492        if len(Names) == 1:
493            G2frame.ErrorDialog('No images to transfer integration angles to','Need more "IMG"s')
494            return
495        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
496        Names.pop(Names.index(Source))
497        # select targets & do copy
498        extraopts = {"label_1":"Xfer scaled calib d-min", "value_1":False,
499                     "label_2":"Xfer scaled 2-theta min", "value_2":False,
500                     "label_3":"Xfer scaled 2-theta max", "value_3":True,
501                     }
502        dlg = G2G.G2MultiChoiceDialog(G2frame,'Xfer angles','Transfer integration range from '+Source+' to:',
503            Names,extraOpts=extraopts)
504        try:
505            if dlg.ShowModal() == wx.ID_OK:
506                for i in '_1','_2','_3':
507                    if extraopts['value'+i]: break
508                else:
509                    G2G.G2MessageBox(G2frame,'Nothing to do!')
510                    return
511                xferAng = lambda tth,dist1,dist2: atand(dist1 * tand(tth) / dist2)
512                items = dlg.GetSelections()
513                G2frame.EnablePlot = False
514                Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Source)
515                data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
516                ttmin0,ttmax0 = data['IOtth']
517                dist0 = data['distance']
518                wave0 = data['wavelength']
519                dsp0 = data['calibdmin']
520                print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
521                            .format(dist0,ttmin0,ttmax0,dsp0))
522                for item in items:
523                    name = Names[item]
524                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
525                    data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
526                    dist1 = data['distance']
527                    if extraopts["value_2"]:
528                        data['IOtth'][0] = xferAng(ttmin0,dist0,dist1)
529                    if extraopts["value_3"]:
530                        data['IOtth'][1] = xferAng(ttmax0,dist0,dist1)
531                    if extraopts["value_1"]:
532                        ang1 = xferAng(2.0*asind(wave0/(2.*dsp0)),dist0,dist1)
533                        data['calibdmin'] = data['wavelength']/(2.*sind(ang1/2.))
534                        print('distance = {:.2f} integration range: [{:.4f}, {:.4f}], calib dmin {:.3f}'
535                            .format(dist1,data['IOtth'][0],data['IOtth'][1],data['calibdmin']))
536                    else:
537                        print('distance = {:.2f} integration range: [{:.4f}, {:.4f}]'
538                            .format(dist1,data['IOtth'][0],data['IOtth'][1]))
539        finally:
540            dlg.Destroy()
541            G2frame.PatternTree.SelectItem(G2frame.PickId)       
542           
543    def OnResetDist(event):
544        dlg = wx.MessageDialog(G2frame,'Are you sure you want to do this?',caption='Reset dist to set dist',style=wx.YES_NO|wx.ICON_EXCLAMATION)
545        if dlg.ShowModal() != wx.ID_YES:
546            dlg.Destroy()
547            return
548        dlg.Destroy()
549        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
550        dlg = G2G.G2MultiChoiceDialog(G2frame,'Reset dist','Reset dist to set dist for:',Names)
551        try:
552            if dlg.ShowModal() == wx.ID_OK:
553                items = dlg.GetSelections()
554                for item in items:
555                    name = Names[item]
556                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
557                    Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
558                    Data['distance'] = Data['setdist']
559        finally:
560            dlg.Destroy()
561        wx.CallAfter(UpdateImageControls,G2frame,data,masks)
562           
563# Sizers
564    Indx = {}                                   
565    def ComboSizer():
566
567        def OnDataType(event):
568            data['type'] = typeSel.GetValue()[:4]
569            if 'SASD' in data['type']:
570                data['SampleAbs'][0] = np.exp(-data['SampleAbs'][0]) #switch from muT to trans!
571                if data['binType'] == '2-theta': data['binType'] = 'log(q)'  #switch default bin type
572            elif 'PWDR' in data['type']:
573                data['SampleAbs'][0] = -np.log(data['SampleAbs'][0])  #switch from trans to muT!
574                if data['binType'] == 'log(q)': data['binType'] = '2-theta'  #switch default bin type                 
575            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
576   
577        def OnNewColorBar(event):
578            data['color'] = colSel.GetValue()
579            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
580       
581        def OnAzmthOff(invalid,value,tc):
582            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
583       
584        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
585        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Type of image data: '),0,WACV)
586        typeSel = wx.ComboBox(parent=G2frame.dataDisplay,value=typeDict[data['type']],choices=typeList,
587            style=wx.CB_READONLY|wx.CB_DROPDOWN)
588        typeSel.SetValue(data['type'])
589        typeSel.Bind(wx.EVT_COMBOBOX, OnDataType)
590        comboSizer.Add(typeSel,0,WACV)
591        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Color bar '),0,WACV)
592        colSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['color'],choices=colorList,
593            style=wx.CB_READONLY|wx.CB_DROPDOWN)
594        colSel.Bind(wx.EVT_COMBOBOX, OnNewColorBar)
595        comboSizer.Add(colSel,0,WACV)
596        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Azimuth offset '),0,WACV)
597        azmthOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'azmthOff',nDig=(10,2),
598            typeHint=float,OnLeave=OnAzmthOff)
599        comboSizer.Add(azmthOff,0,WACV)
600        return comboSizer
601       
602    def MaxSizer():
603        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
604        for the image, as well as callback routines.
605        '''
606        def OnNewVal(invalid,value,tc):
607            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
608            the value into the data['range'] nested list).
609            This adjusts the slider positions to match the current values
610            '''
611            scaleSel.SetSelection(len(scaleChoices)-1)
612            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
613            if r11 != Range[1][1]:
614                Range[1][1] = r11
615                maxVal.SetValue(int(Range[1][1]))
616            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
617            if r10 != Range[1][0]:
618                Range[1][0] = r10
619                minVal.SetValue(int(Range[1][0]))
620            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
621            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
622            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
623            maxSel.SetValue(sv1)
624            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
625            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
626            minSel.SetValue(sv0)
627            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
628            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
629            if mplOld:
630                Page.canvas.draw()
631            else:
632                Page.canvas.draw_idle()
633           
634        G2frame.prevMaxValue = None   
635        def OnMaxSlider(event):
636            val = maxSel.GetValue()
637            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
638            scaleSel.SetSelection(len(scaleChoices)-1)
639            G2frame.prevMaxValue = val
640            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
641            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
642            maxVal.SetValue(int(0.5+Range[1][1]))
643            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
644            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
645            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
646            minSel.SetValue(sv0)
647            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
648            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
649            if mplOld:
650                Page.canvas.draw()
651            else:
652                Page.canvas.draw_idle()
653           
654        G2frame.prevMinValue = None   
655        def OnMinSlider(event):
656            val = minSel.GetValue()
657            scaleSel.SetSelection(len(scaleChoices)-1)
658            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
659            G2frame.prevMinValue = val
660            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
661            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
662            minVal.SetValue(int(Range[1][0]))
663            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
664            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
665            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
666            maxSel.SetValue(sv1)
667            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
668            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
669            if mplOld:
670                Page.canvas.draw()
671            else:
672                Page.canvas.draw_idle()
673           
674        def OnAutoSet(event):
675            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
676            for the image so that 95% (etc.) of pixels are inside the color map limits.
677            An equal number of pixels are dropped at the minimum and maximum levels.
678            '''
679            try:
680                val = int(event.GetEventObject().GetStringSelection()[:-1])
681                margin = (100-val)/2.
682            except:
683                margin = 0
684                event.GetEventObject().SetSelection(0)
685            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
686            if margin == 0:
687                Range[1] = list(Range[0])
688            else:
689                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
690                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
691            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
692            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
693            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
694            maxSel.SetValue(sv1)
695            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
696            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
697            minSel.SetValue(sv0)
698            minVal.SetValue(int(Range[1][0]))
699            maxVal.SetValue(int(Range[1][1]))
700            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
701            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
702            if mplOld:
703                Page.canvas.draw()
704            else:
705                Page.canvas.draw_idle()
706
707        mplv = mpl.__version__.split('.')
708        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
709        # Plot color scaling uses limits as below:
710        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
711        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
712        maxSizer = wx.GridBagSizer(0,0)
713        r = c = 0
714        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Max intensity'),(r,c))
715        c += 1
716        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
717        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
718        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
719        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
720        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
721        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
722        maxSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
723        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
724        maxSizer.AddGrowableCol(c)
725        c += 1
726        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
727        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],1,min=Range[0][0]+1,
728            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
729        maxSizer.Add(maxVal,(r,c))
730        c += 1
731        scaleChoices = ("100%","99%","95%","90%","80%","?")
732        scaleSel = wx.Choice(G2frame.dataDisplay,choices=scaleChoices,size=(-1,-1))
733        if (Range[1][0] == Range[0][0] and
734            Range[1][1] == Range[0][1]):
735            scaleSel.SetSelection(0)
736        else:
737            scaleSel.SetSelection(len(scaleChoices)-1)
738        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
739        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
740        c = 0
741        r = 1
742        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min intensity'),(r,c))
743        c += 1
744        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
745        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
746        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
747        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
748        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
749        minSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
750        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
751        c += 1
752        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
753        minVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],0,
754            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
755        maxSizer.Add(minVal,(r,c))
756        return maxSizer
757       
758    def CalibCoeffSizer():
759       
760        def OnCalRef(event):
761            Obj = event.GetEventObject()
762            name = Indx[Obj]
763            data['varyList'][name] = Obj.GetValue()
764           
765        calibSizer = wx.FlexGridSizer(0,2,5,5)
766        calibSizer.SetFlexibleDirection(wx.HORIZONTAL)
767        calibSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibration coefficients'),0,WACV)   
768        calibSizer.Add((5,0),0)
769        Names = ['det-X','det-Y','wave','dist','tilt','phi']
770        if 'PWDR' in data['type']:
771            Names.append('dep') 
772        Parms = {'dist':['Distance',(10,3),data,'distance'],'det-X':['Beam center X',(10,3),data['center'],0],
773            'det-Y':['Beam center Y',(10,3),data['center'],1],'tilt':['Tilt angle',(10,3),data,'tilt'],
774            'phi':['Tilt rotation',(10,2),data,'rotation'],'dep':['Penetration',(10,4),data,'DetDepth'],
775            'wave':['Wavelength',(10,6),data,'wavelength']}
776        for name in Names:
777            calSel = wx.CheckBox(parent=G2frame.dataDisplay,label=Parms[name][0])
778            calibSizer.Add(calSel,0,WACV)
779            calSel.Bind(wx.EVT_CHECKBOX, OnCalRef)
780            calSel.SetValue(data['varyList'][name])
781            Indx[calSel] = name
782            if name == 'wave':
783                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
784                    Parms[name][3],min=0.01,max=10.,nDig=Parms[name][1],typeHint=float)
785            elif name == 'dep':
786                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
787                    Parms[name][3],min=0.0,max=0.2,nDig=Parms[name][1],typeHint=float)
788            else:
789                calVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Parms[name][2],
790                    Parms[name][3],nDig=Parms[name][1],typeHint=float)
791            calibSizer.Add(calVal,0,WACV)
792        return calibSizer
793   
794    def IntegrateSizer():
795       
796        def OnNewBinType(event):
797            data['binType'] = binSel.GetValue()
798            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
799       
800        def OnIOtth(invalid,value,tc):
801            Ltth = float(G2frame.InnerTth.GetValue())
802            Utth = float(G2frame.OuterTth.GetValue())
803            if Ltth > Utth:
804                Ltth,Utth = Utth,Ltth
805            if 'q' in data['binType'].lower():
806                data['IOtth'] = [2.*asind(Ltth*wave/(4.*math.pi)),2.*asind(Utth*wave/(4.*math.pi))]
807            else:
808                data['IOtth'] = [Ltth,Utth]
809            G2frame.InnerTth.SetValue(Ltth)
810            G2frame.OuterTth.SetValue(Utth)
811            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
812       
813        def OnLRazim(invalid,value,tc):
814            Lazm = float(G2frame.Lazim.GetValue())%360.
815            Razm = float(G2frame.Razim.GetValue())%360.
816            if Lazm > Razm:
817                Razm += 360.
818            if data['fullIntegrate']:
819                Razm = Lazm+360.
820            G2frame.Lazim.SetValue(Lazm)
821            G2frame.Razim.SetValue(Razm)
822            data['LRazimuth'] = [Lazm,Razm]
823            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
824               
825        def OnNumOutAzms(invalid,value,tc):
826            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
827       
828        def OnOblique(event):
829            data['Oblique'][1] = not data['Oblique'][1]
830               
831        def OnSampleShape(event):
832            data['SampleShape'] = samShape.GetValue()
833            if 'Cylind' in data['SampleShape']:
834                data['SampleAbs'][0] = 0.0
835            elif 'Fixed' in data['SampleShape']:
836                data['SampleAbs'][0] = 1.0
837            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
838                           
839        def OnSamAbs(event):
840            data['SampleAbs'][1] = not data['SampleAbs'][1]
841                           
842        def OnShowLines(event):
843            data['showLines'] = not data['showLines']
844            G2plt.PlotExposedImage(G2frame,event=event)
845           
846        def OnFullIntegrate(event):
847            Lazm = float(G2frame.Lazim.GetValue())
848            if data['fullIntegrate']:
849                data['fullIntegrate'] = False
850                data['LRazimuth'] = [Lazm,Lazm+20.]
851            else:
852                data['fullIntegrate'] = True
853                data['LRazimuth'] = [Lazm,Lazm+360.]
854            wx.CallLater(100,UpdateImageControls,G2frame,data,masks)
855            G2plt.PlotExposedImage(G2frame,event=event)
856           
857        def OnSetDefault(event):
858            if data['setDefault']:
859                G2frame.imageDefault = {}
860                data['setDefault'] = False
861            else:
862                G2frame.imageDefault = copy.copy(data)
863                data['setDefault'] = True
864               
865        def OnCenterAzm(event):
866            data['centerAzm'] = not data['centerAzm']
867            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
868               
869        def OnApplyPola(event):
870            data['PolaVal'][1] = not data['PolaVal'][1]
871               
872        dataSizer = wx.FlexGridSizer(0,2,5,3)
873        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Integration coefficients'),0,WACV)   
874        dataSizer.Add((5,0),0)
875        if 'PWDR' in data['type']:
876            binChoice = ['2-theta','Q']
877        elif 'SASD' in data['type']:
878            binChoice = ['2-theta','Q','log(q)']
879        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Bin style: Constant step bins in'),0,WACV)           
880        binSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['binType'],choices=binChoice,
881            style=wx.CB_READONLY|wx.CB_DROPDOWN)
882        binSel.Bind(wx.EVT_COMBOBOX, OnNewBinType)
883        dataSizer.Add(binSel,0,WACV)
884        binType = '2-theta'
885        if 'q' in data['binType'].lower():
886            binType = 'Q'
887        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Inner/Outer '+binType),0,WACV)           
888        IOtth = data['IOtth'][:]
889        if 'q' in data['binType'].lower():
890            wave = data['wavelength']
891            IOtth = [4.*math.pi*sind(IOtth[0]/2.)/wave,4.*math.pi*sind(IOtth[1]/2.)/wave]
892        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
893        G2frame.InnerTth = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,IOtth,0,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
894        littleSizer.Add(G2frame.InnerTth,0,WACV)
895        G2frame.OuterTth = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,IOtth,1,nDig=(8,3,'f'),min=0.001,typeHint=float,OnLeave=OnIOtth)
896        littleSizer.Add(G2frame.OuterTth,0,WACV)
897        dataSizer.Add(littleSizer,0,)
898        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Start/End azimuth'),0,WACV)
899        LRazim = data['LRazimuth']
900        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
901        G2frame.Lazim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,LRazim,0,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
902        littleSizer.Add(G2frame.Lazim,0,WACV)
903        G2frame.Razim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,LRazim,1,nDig=(6,1,'f'),typeHint=float,OnLeave=OnLRazim)
904        if data['fullIntegrate']:
905            G2frame.Razim.Enable(False)
906            G2frame.Razim.SetBackgroundColour(VERY_LIGHT_GREY)
907            G2frame.Razim.SetValue(LRazim[0]+360.)
908        littleSizer.Add(G2frame.Razim,0,WACV)
909        dataSizer.Add(littleSizer,0,)
910        dataSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' No. 2-theta/azimuth bins'),0,WACV)
911        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
912        outChan = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'outChannels',typeHint=int,min=10)
913        littleSizer.Add(outChan,0,WACV)
914        outAzim = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'outAzimuths',nDig=(10,4),min=1,typeHint=int,OnLeave=OnNumOutAzms)
915        littleSizer.Add(outAzim,0,WACV)
916        dataSizer.Add(littleSizer,0,)
917        samplechoice = ['Cylinder','Fixed flat plate',]
918        dataSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Select sample shape'),0,WACV)
919        samShape = wx.ComboBox(G2frame.dataDisplay,value=data['SampleShape'],choices=samplechoice,
920            style=wx.CB_READONLY|wx.CB_DROPDOWN)
921        samShape.Bind(wx.EVT_COMBOBOX,OnSampleShape)
922        dataSizer.Add(samShape,0,WACV)
923        #SampleShape - cylinder or flat plate choice?
924        littleSizer = wx.BoxSizer(wx.HORIZONTAL)
925        samabs = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply sample absorption?')
926        dataSizer.Add(samabs,0,WACV)
927        samabs.Bind(wx.EVT_CHECKBOX, OnSamAbs)
928        samabs.SetValue(data['SampleAbs'][1])
929        minmax = [0.,2.]
930        if 'Cylind' in data['SampleShape']: #cylinder mu*R; flat plate transmission
931            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='mu*R (0.00-2.0) '),0,WACV)
932        elif 'Fixed' in data['SampleShape']:
933            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='transmission '),0,WACV) #for flat plate
934            minmax = [.05,1.0]
935        samabsVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['SampleAbs'],0,nDig=(10,3),
936            typeHint=float,min=minmax[0],max=minmax[1])
937        littleSizer.Add(samabsVal,0,WACV)
938        dataSizer.Add(littleSizer,0,)       
939        if 'PWDR' in data['type']:
940            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
941            oblique = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply detector absorption?')
942            dataSizer.Add(oblique,0,WACV)
943            oblique.Bind(wx.EVT_CHECKBOX, OnOblique)
944            oblique.SetValue(data['Oblique'][1])
945            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Value (0.01-0.99)  '),0,WACV)
946            obliqVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['Oblique'],0,nDig=(10,3),typeHint=float,min=0.01,max=0.99)
947            littleSizer.Add(obliqVal,0,WACV)
948            dataSizer.Add(littleSizer,0,)
949        if 'SASD' in data['type']:
950            littleSizer = wx.BoxSizer(wx.HORIZONTAL)
951            setPolariz = wx.CheckBox(parent=G2frame.dataDisplay,label='Apply polarization?')
952            dataSizer.Add(setPolariz,0,WACV)
953            setPolariz.Bind(wx.EVT_CHECKBOX, OnApplyPola)
954            setPolariz.SetValue(data['PolaVal'][1])
955            littleSizer.Add(wx.StaticText(G2frame.dataDisplay,label='Value (0.001-0.999)  '),0,WACV)
956            polaVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['PolaVal'],0,nDig=(10,3),typeHint=float,min=0.001,max=0.999)
957            littleSizer.Add(polaVal,0,WACV)
958            dataSizer.Add(littleSizer,0,)
959       
960        showLines = wx.CheckBox(parent=G2frame.dataDisplay,label='Show integration limits?')
961        dataSizer.Add(showLines,0,WACV)
962        showLines.Bind(wx.EVT_CHECKBOX, OnShowLines)
963        showLines.SetValue(data['showLines'])
964        fullIntegrate = wx.CheckBox(parent=G2frame.dataDisplay,label='Do full integration?')
965        dataSizer.Add(fullIntegrate,0,WACV)
966        fullIntegrate.Bind(wx.EVT_CHECKBOX, OnFullIntegrate)
967        fullIntegrate.SetValue(data['fullIntegrate'])
968        setDefault = wx.CheckBox(parent=G2frame.dataDisplay,label='Use for all new images?')
969        dataSizer.Add(setDefault,0,WACV)
970        setDefault.Bind(wx.EVT_CHECKBOX, OnSetDefault)
971        setDefault.SetValue(data['setDefault'])
972        centerAzm = wx.CheckBox(parent=G2frame.dataDisplay,label='Azimuth at bin center?')
973        dataSizer.Add(centerAzm,0,WACV)
974        centerAzm.Bind(wx.EVT_CHECKBOX, OnCenterAzm)
975        centerAzm.SetValue(data['centerAzm'])
976        return dataSizer
977       
978    def BackSizer():
979       
980        global oldFlat
981        def OnBackImage(event):
982            data['background image'][0] = backImage.GetValue()
983            G2frame.ImageZ = GetImageZ(G2frame,data,newRange=True)
984            ResetThresholds()
985            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
986           
987        def OnDarkImage(event):
988            data['dark image'][0] = darkImage.GetValue()
989            G2frame.ImageZ = GetImageZ(G2frame,data,newRange=True)
990            ResetThresholds()
991            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
992           
993        def OnFlatBkg(invalid,value,tc):
994            global oldFlat
995            G2frame.ImageZ += int(oldFlat-data['Flat Bkg'])
996            oldFlat = data['Flat Bkg']
997            ResetThresholds()
998            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
999
1000        def OnMult(invalid,value,tc):
1001            G2frame.ImageZ = GetImageZ(G2frame,data,newRange=True)
1002            ResetThresholds()
1003            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=tc.event)
1004       
1005        backSizer = wx.FlexGridSizer(0,6,5,5)
1006        oldFlat = data.get('Flat Bkg',0.)
1007
1008        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Dark image'),0,WACV)
1009        Choices = ['',]+G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1010        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1011        Choices.pop(Choices.index(Source))
1012        darkImage = wx.ComboBox(parent=G2frame.dataDisplay,value=data['dark image'][0],choices=Choices,
1013            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1014        darkImage.Bind(wx.EVT_COMBOBOX,OnDarkImage)
1015        backSizer.Add(darkImage)
1016        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' multiplier'),0,WACV)
1017        darkMult = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['dark image'],1,nDig=(10,3),
1018            typeHint=float,OnLeave=OnMult)
1019        backSizer.Add(darkMult,0,WACV)
1020        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Flat Bkg: '),0,WACV)
1021        flatbkg = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Flat Bkg',nDig=(10,0),
1022            typeHint=float,OnLeave=OnFlatBkg)
1023        backSizer.Add(flatbkg,0,WACV)
1024
1025        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' Background image'),0,WACV)
1026        backImage = wx.ComboBox(parent=G2frame.dataDisplay,value=data['background image'][0],choices=Choices,
1027            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1028        backImage.Bind(wx.EVT_COMBOBOX,OnBackImage)
1029        backSizer.Add(backImage)
1030        backSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,' multiplier'),0,WACV)
1031        backMult = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['background image'],1,nDig=(10,3),
1032            typeHint=float,OnLeave=OnMult)
1033        backSizer.Add(backMult,0,WACV)
1034        return backSizer
1035                       
1036    def CalibSizer():
1037               
1038        def OnNewCalibrant(event):
1039            data['calibrant'] = calSel.GetValue().strip()
1040            if data['calibrant']:
1041                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=True)
1042                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=True)
1043                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=True)
1044                data['calibskip'] = calFile.Calibrants[data['calibrant']][3]
1045                limits = calFile.Calibrants[data['calibrant']][4]
1046                data['calibdmin'],data['pixLimit'],data['cutoff'] = limits
1047                pixLimit.SetValue(str(limits[1]))
1048                cutOff.SetValue(limits[2])
1049                calibSkip.SetValue(str(data['calibskip']))
1050                G2frame.calibDmin.SetValue(limits[0])
1051            else:
1052                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=False)
1053                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=False)
1054                G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=False)
1055               
1056        def OnCalibSkip(event):
1057            data['calibskip'] = int(calibSkip.GetValue())
1058           
1059        def OnPixLimit(event):
1060            data['pixLimit'] = int(pixLimit.GetValue())
1061           
1062        def OnSetRings(event):
1063            data['setRings'] = not data['setRings']
1064            G2plt.PlotExposedImage(G2frame,event=event)
1065   
1066        calibSizer = wx.FlexGridSizer(0,3,5,5)
1067        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1068        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibrant '),0,WACV)
1069        calSel = wx.ComboBox(parent=G2frame.dataDisplay,value=data['calibrant'],choices=calList,
1070            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1071        calSel.Bind(wx.EVT_COMBOBOX, OnNewCalibrant)
1072        comboSizer.Add(calSel,0,WACV)
1073        calibSizer.Add(comboSizer,0)
1074       
1075        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1076        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calib lines to skip   '),0,WACV)
1077        calibSkip  = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['calibskip']),choices=[str(i) for i in range(25)],
1078            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1079        calibSkip.Bind(wx.EVT_COMBOBOX, OnCalibSkip)
1080        comboSizer.Add(calibSkip,0,WACV)
1081        calibSizer.Add(comboSizer,0)
1082       
1083        comboSizer = wx.BoxSizer(wx.HORIZONTAL)       
1084        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min calib d-spacing '),0,WACV)
1085        G2frame.calibDmin = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'calibdmin',nDig=(10,2),typeHint=float,min=0.25)
1086        comboSizer.Add(G2frame.calibDmin,0,WACV)
1087        calibSizer.Add(comboSizer,0)
1088       
1089        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1090        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min ring I/Ib '),0,WACV)
1091        cutOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'cutoff',nDig=(10,2),min=0.1)
1092        comboSizer.Add(cutOff,0,WACV)
1093        calibSizer.Add(comboSizer,0)
1094       
1095        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1096        comboSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Pixel search range '),0,WACV)
1097        pixLimit = wx.ComboBox(parent=G2frame.dataDisplay,value=str(data['pixLimit']),choices=['1','2','5','10','15','20'],
1098            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1099        pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
1100        comboSizer.Add(pixLimit,0,WACV)
1101        calibSizer.Add(comboSizer,0)
1102       
1103        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1104        setRings = wx.CheckBox(parent=G2frame.dataDisplay,label='Show ring picks?')
1105        comboSizer.Add(setRings,0)
1106        setRings.Bind(wx.EVT_CHECKBOX, OnSetRings)
1107        setRings.SetValue(data['setRings'])
1108        calibSizer.Add(comboSizer,0)
1109        return calibSizer
1110       
1111    def GonioSizer():
1112       
1113        def OnGlobalEdit(event):
1114            Names = []
1115            Items = []
1116            if G2frame.PatternTree.GetCount():
1117                id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1118                while id:
1119                    name = G2frame.PatternTree.GetItemText(id)
1120                    if 'IMG' in name:
1121                        ctrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,id,'Image Controls'))
1122                        Names.append(name)
1123                        Items.append(ctrls['GonioAngles'])
1124                    id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1125                if len(Names) == 1:
1126                    G2frame.ErrorDialog('Nothing for global editing','There must be more than one "IMG" pattern')
1127                    return
1128                dlg = G2G.G2HistoDataDialog(G2frame,' Edit sample goniometer data:',
1129                    'Edit data',['Omega','Chi','Phi'],['%.2f','%.2f','%.2f'],Names,Items)
1130                try:
1131                    if dlg.ShowModal() == wx.ID_OK:
1132                        id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
1133                        while id:
1134                            name = G2frame.PatternTree.GetItemText(id)
1135                            if 'IMG' in name:
1136                                ctrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,id,'Image Controls'))
1137                                vals = Items[Names.index(name)]
1138                                ctrls['GonioAngles'] = vals
1139                            id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1140                finally:
1141                    dlg.Destroy()
1142                    G2frame.PatternTree.SelectItem(G2frame.PickId)
1143       
1144        gonioSizer = wx.BoxSizer(wx.HORIZONTAL)
1145        names = ['Omega','Chi','Phi']
1146        gonioSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,'Sample goniometer angles: '),0,WACV)
1147        for i,name in enumerate(names):
1148            gonioSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,name),0,WACV)
1149            angle = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['GonioAngles'],i,nDig=(8,2),typeHint=float)
1150            gonioSizer.Add(angle,0,WACV)
1151        globEdit = wx.Button(G2frame.dataDisplay,-1,'Global edit')
1152        globEdit.Bind(wx.EVT_BUTTON,OnGlobalEdit)
1153        gonioSizer.Add(globEdit,0,WACV)
1154        return gonioSizer
1155       
1156# Image Controls main code             
1157                           
1158    #fix for old files:
1159    if 'azmthOff' not in data:
1160        data['azmthOff'] = 0.0
1161    if 'background image' not in data:
1162        data['background image'] = ['',-1.0]
1163    if 'dark image' not in data:
1164        data['dark image'] = ['',-1.0]
1165    if 'centerAzm' not in data:
1166        data['centerAzm'] = False
1167    if 'Oblique' not in data:
1168        data['Oblique'] = [0.5,False]
1169    if 'PolaVal' not in data:
1170        data['PolaVal'] = [0.99,False]
1171    #end fix
1172   
1173    if IntegrateOnly:
1174        OnIntegrate(None)
1175        return
1176   
1177    colorList = sorted([m for m in mpl.cm.datad.keys() if not m.endswith("_r")],key=lambda s: s.lower())
1178    calList = sorted([m for m in calFile.Calibrants.keys()],key=lambda s: s.lower())
1179    typeList = ['PWDR - powder diffraction data','SASD - small angle scattering data',]
1180    if not data.get('type'):                        #patch for old project files
1181        data['type'] = 'PWDR'
1182    typeDict = {'PWDR':typeList[0],'SASD':typeList[1],}
1183    if G2frame.dataDisplay:
1184        G2frame.dataDisplay.Destroy()
1185    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.ImageMenu)
1186    if not G2frame.dataFrame.GetStatusBar():
1187        G2frame.dataFrame.CreateStatusBar()
1188    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCalibrate, id=G2gd.wxID_IMCALIBRATE)
1189    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRecalibrate, id=G2gd.wxID_IMRECALIBRATE)
1190    G2frame.dataFrame.Bind(wx.EVT_MENU, OnRecalibAll, id=G2gd.wxID_IMRECALIBALL)
1191    G2frame.dataFrame.Bind(wx.EVT_MENU, OnClearCalib, id=G2gd.wxID_IMCLEARCALIB)
1192    if data.get('calibrant'):
1193        mode = True
1194    else:
1195        mode = False
1196    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBRATE,enable=mode)
1197    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMCALIBRATE,enable=mode)
1198    G2frame.dataFrame.ImageEdit.Enable(id=G2gd.wxID_IMRECALIBALL,enable=mode)
1199    G2frame.dataFrame.Bind(wx.EVT_MENU, OnIntegrate, id=G2gd.wxID_IMINTEGRATE)
1200    G2frame.dataFrame.Bind(wx.EVT_MENU, OnIntegrateAll, id=G2gd.wxID_INTEGRATEALL)
1201    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyControls, id=G2gd.wxID_IMCOPYCONTROLS)
1202    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopySelected, id=G2gd.wxID_IMCOPYSELECTED)
1203    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveControls, id=G2gd.wxID_IMSAVECONTROLS)
1204    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveMultiControls, id=G2gd.wxID_SAVESELECTEDCONTROLS)
1205    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadControls, id=G2gd.wxID_IMLOADCONTROLS)
1206    G2frame.dataFrame.Bind(wx.EVT_MENU, OnTransferAngles, id=G2gd.wxID_IMXFERCONTROLS)
1207    G2frame.dataFrame.Bind(wx.EVT_MENU, OnResetDist, id=G2gd.wxID_IMRESETDIST)
1208    def OnDestroy(event):
1209        G2frame.autoIntFrame = None
1210    def OnAutoInt(event):
1211        if G2frame.autoIntFrame: # ensure only one open at a time
1212            G2frame.autoIntFrame.Raise()
1213            return
1214        PollTime = GSASIIpath.GetConfigValue('Autoint_PollTime',30.)
1215        G2frame.autoIntFrame = AutoIntFrame(G2frame,PollTime=PollTime)
1216        G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
1217    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoInt, id=G2gd.wxID_IMAUTOINTEG)
1218    G2frame.dataDisplay = wx.Panel(G2frame.dataFrame)
1219
1220    mainSizer = wx.BoxSizer(wx.VERTICAL)
1221    mainSizer.Add((5,10),0)   
1222    mainSizer.Add(ComboSizer(),0,wx.ALIGN_LEFT)
1223    mainSizer.Add((5,5),0)
1224    Range = data['range'] # allows code to be same in Masks
1225    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1226    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1227   
1228    mainSizer.Add((5,5),0)
1229    DataSizer = wx.FlexGridSizer(0,2,5,0)
1230    DataSizer.Add(CalibCoeffSizer(),0)
1231    DataSizer.Add(IntegrateSizer(),0)       
1232    mainSizer.Add(DataSizer,0)
1233    mainSizer.Add((5,5),0)           
1234    mainSizer.Add(BackSizer(),0)
1235    mainSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Calibration controls:'),0,WACV)
1236    mainSizer.Add((5,5),0)
1237    mainSizer.Add(CalibSizer(),0,WACV)
1238    mainSizer.Add((5,5),0)
1239    mainSizer.Add(GonioSizer(),0,WACV)   
1240       
1241    mainSizer.Layout()   
1242    G2frame.dataDisplay.SetSizer(mainSizer)
1243    fitSize = mainSizer.Fit(G2frame.dataFrame)
1244    G2frame.dataFrame.setSizePosLeft(fitSize)
1245    G2frame.dataFrame.SendSizeEvent()
1246   
1247################################################################################
1248##### Masks
1249################################################################################
1250def CleanupMasks(data):
1251    '''If a mask creation is not completed, an empty mask entry is created in the
1252    masks array. This cleans them out. It is called when the masks page is first loaded
1253    and before saving them or after reading them in. This should also probably be done
1254    before they are used for integration.
1255    '''
1256    for key in ['Points','Rings','Arcs','Polygons']:
1257        data[key] = data.get(key,[])
1258        l1 = len(data[key])
1259        data[key] = [i for i in data[key] if len(i)]
1260        l2 = len(data[key])
1261        if GSASIIpath.GetConfigValue('debug') and l1 != l2:
1262            print 'Mask Cleanup:',key,'was',l1,'entries','now',l2
1263   
1264def UpdateMasks(G2frame,data):
1265    '''Shows and handles the controls on the "Masks" data tree entry
1266    '''
1267   
1268    def OnTextMsg(event):
1269        Obj = event.GetEventObject()
1270        Obj.SetToolTipString('Drag this mask on 2D Powder Image with mouse to change ')
1271
1272    def Replot(*args,**kwargs):
1273        wx.CallAfter(G2plt.PlotExposedImage,G2frame)
1274
1275    def newReplot(*args,**kwargs):
1276        wx.CallAfter(G2plt.PlotExposedImage,G2frame,newPlot=True)
1277
1278    def onDeleteMask(event):
1279        Obj = event.GetEventObject()
1280        typ = Obj.locationcode.split('+')[1]
1281        num = int(Obj.locationcode.split('+')[2])
1282        del(data[typ][num])
1283        wx.CallAfter(UpdateMasks,G2frame,data)
1284        G2plt.PlotExposedImage(G2frame,event=event)
1285
1286    def onDeleteFrame(event):
1287        data['Frames'] = []
1288        wx.CallAfter(UpdateMasks,G2frame,data)
1289        G2plt.PlotExposedImage(G2frame,event=event)
1290
1291    def OnCopyMask(event):
1292        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1293        if len(Names) == 1:
1294            G2frame.ErrorDialog('Nothing to copy masks to','There must be more than one "IMG" pattern')
1295            return
1296        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1297        Names.pop(Names.index(Source))
1298        Data = copy.deepcopy(data)
1299        Thresh = Data.pop('Thresholds')     # & remove it as well
1300        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy mask data','Copy masks from '+Source+' to:',Names)
1301        try:
1302            if dlg.ShowModal() == wx.ID_OK:
1303                items = dlg.GetSelections()
1304                for item in items:
1305                    name = Names[item]
1306                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1307                    MId = G2gd.GetPatternTreeItemId(G2frame,Id,'Masks')
1308                    Mask = G2frame.PatternTree.GetItemPyData(MId)
1309                    Mask.update(copy.deepcopy(Data))
1310                    Mask['Thresholds'][1][0] = Thresh[1][0]  #copy only lower threshold
1311                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Masks'),Mask)
1312        finally:
1313            dlg.Destroy()
1314
1315    def OnSaveMask(event):
1316        CleanupMasks(data)
1317        pth = G2G.GetExportPath(G2frame)
1318        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1319            'image mask files (*.immask)|*.immask',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1320        try:
1321            if dlg.ShowModal() == wx.ID_OK:
1322                filename = dlg.GetPath()
1323                filename = os.path.splitext(filename)[0]+'.immask'
1324                File = open(filename,'w')
1325                keys = ['Points','Rings','Arcs','Polygons','Frames','Thresholds']
1326                for key in keys:
1327                    File.write(key+':'+str(data[key])+'\n')
1328                File.close()
1329        finally:
1330            dlg.Destroy()
1331       
1332    def OnLoadMask(event):
1333        if event.Id == G2gd.wxID_MASKLOADNOT:
1334            ignoreThreshold = True
1335        else:
1336            ignoreThreshold = False
1337        pth = G2G.GetImportPath(G2frame)
1338        if not pth: pth = '.'
1339        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1340            'image mask files (*.immask)|*.immask',wx.OPEN)
1341        try:
1342            if dlg.ShowModal() == wx.ID_OK:
1343                filename = dlg.GetPath()
1344                File = open(filename,'r')
1345                save = {}
1346                oldThreshold = data['Thresholds'][0]
1347                S = File.readline()
1348                while S:
1349                    if S[0] == '#':
1350                        S = File.readline()
1351                        continue
1352                    [key,val] = S.strip().split(':',1)
1353                    if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
1354                        if ignoreThreshold and key == 'Thresholds':
1355                            S = File.readline() 
1356                            continue
1357                        save[key] = eval(val)
1358                        if key == 'Thresholds':
1359                            save[key][0] = oldThreshold
1360                            save[key][1][1] = min(oldThreshold[1],save[key][1][1])
1361                    S = File.readline()
1362                File.close()
1363                data.update(save)
1364                CleanupMasks(data)
1365                wx.CallAfter(UpdateMasks,G2frame,data)
1366                G2plt.PlotExposedImage(G2frame,event=event)               
1367        finally:
1368            dlg.Destroy()
1369           
1370    def OnAutoSpotMask(event):
1371        'Do auto search for spot masks'
1372        if wx.MessageDialog(G2frame.dataDisplay,'NB: This will clear any old spot masks',
1373            'Auto Spot Masks', wx.OK|wx.CANCEL).ShowModal() == wx.ID_OK:
1374            Controls = copy.deepcopy(G2frame.PatternTree.GetItemPyData( 
1375                G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')))
1376            Error = G2img.AutoSpotMasks(G2frame.ImageZ,data,Controls)
1377            if not Error is None:
1378                G2frame.ErrorDialog('Auto spot search error',Error)
1379            wx.CallAfter(UpdateMasks,G2frame,data)
1380            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
1381
1382    def OnDeleteSpotMask(event):
1383        data['Points'] = []
1384        wx.CallAfter(UpdateMasks,G2frame,data)
1385        wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)         
1386           
1387    def ToggleSpotMaskMode(event):
1388        G2plt.ToggleMultiSpotMask(G2frame)
1389       
1390    def OnNewArcMask(event):
1391        'Start a new arc mask'
1392        G2frame.MaskKey = 'a'
1393        G2plt.OnStartMask(G2frame)
1394       
1395    def OnNewRingMask(event):
1396        'Start a new ring mask'
1397        G2frame.MaskKey = 'r'
1398        G2plt.OnStartMask(G2frame)
1399       
1400    def OnNewPolyMask(event):
1401        'Start a new polygon mask'
1402        G2frame.MaskKey = 'p'
1403        G2plt.OnStartMask(G2frame)
1404       
1405    def OnNewFrameMask(event):
1406        'Start a new Frame mask'
1407        G2frame.MaskKey = 'f'
1408        G2plt.OnStartMask(G2frame)
1409       
1410    def MaxSizer():
1411        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1412        for the image, as well as callback routines.
1413        '''
1414        def OnNewVal(invalid,value,tc):
1415            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1416            the value into the data['range'] nested list).
1417            This adjusts the slider positions to match the current values
1418            '''
1419            scaleSel.SetSelection(len(scaleChoices)-1)
1420            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1421            if r11 != Range[1][1]:
1422                Range[1][1] = r11
1423                maxVal.SetValue(int(Range[1][1]))
1424            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1425            if r10 != Range[1][0]:
1426                Range[1][0] = r10
1427                minVal.SetValue(int(Range[1][0]))
1428            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1429            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1430            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1431            maxSel.SetValue(sv1)
1432            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1433            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1434            minSel.SetValue(sv0)
1435            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1436            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1437            if mplOld:
1438                Page.canvas.draw()
1439            else:
1440                Page.canvas.draw_idle()
1441           
1442        G2frame.prevMaxValue = None   
1443        def OnMaxSlider(event):
1444            val = maxSel.GetValue()
1445            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1446            scaleSel.SetSelection(len(scaleChoices)-1)
1447            G2frame.prevMaxValue = val
1448            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1449            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1450            maxVal.SetValue(int(0.5+Range[1][1]))
1451            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1452            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1453            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1454            minSel.SetValue(sv0)
1455            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1456            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1457            if mplOld:
1458                Page.canvas.draw()
1459            else:
1460                Page.canvas.draw_idle()
1461           
1462        G2frame.prevMinValue = None   
1463        def OnMinSlider(event):
1464            val = minSel.GetValue()
1465            scaleSel.SetSelection(len(scaleChoices)-1)
1466            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1467            G2frame.prevMinValue = val
1468            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1469            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1470            minVal.SetValue(int(Range[1][0]))
1471            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1472            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1473            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1474            maxSel.SetValue(sv1)
1475            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1476            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1477            if mplOld:
1478                Page.canvas.draw()
1479            else:
1480                Page.canvas.draw_idle()
1481           
1482        def OnAutoSet(event):
1483            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1484            for the image so that 95% (etc.) of pixels are inside the color map limits.
1485            An equal number of pixels are dropped at the minimum and maximum levels.
1486            '''
1487            try:
1488                val = int(event.GetEventObject().GetStringSelection()[:-1])
1489                margin = (100-val)/2.
1490            except:
1491                margin = 0
1492                event.GetEventObject().SetSelection(0)
1493            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1494            if margin == 0:
1495                Range[1] = list(Range[0])
1496            else:
1497                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1498                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1499            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1500            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1501            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1502            maxSel.SetValue(sv1)
1503            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1504            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1505            minSel.SetValue(sv0)
1506            minVal.SetValue(int(Range[1][0]))
1507            maxVal.SetValue(int(Range[1][1]))
1508            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1509            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1510            if mplOld:
1511                Page.canvas.draw()
1512            else:
1513                Page.canvas.draw_idle()
1514
1515        mplv = mpl.__version__.split('.')
1516        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1517        # Plot color scaling uses limits as below:
1518        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1519        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1520        maxSizer = wx.GridBagSizer(0,0)
1521        r = c = 0
1522        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Max intensity'),(r,c))
1523        c += 1
1524        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1525        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1526        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1527        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1528        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1529        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1530        maxSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv1,size=(300,-1))
1531        maxSizer.Add(maxSel,(r,c),flag=wx.EXPAND)
1532        maxSizer.AddGrowableCol(c)
1533        c += 1
1534        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1535        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],1,min=Range[0][0]+1,
1536            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1537        maxSizer.Add(maxVal,(r,c))
1538        c += 1
1539        scaleChoices = ("100%","99%","95%","90%","80%","?")
1540        scaleSel = wx.Choice(G2frame.dataDisplay,choices=scaleChoices,size=(-1,-1))
1541        if (Range[1][0] == Range[0][0] and
1542            Range[1][1] == Range[0][1]):
1543            scaleSel.SetSelection(0)
1544        else:
1545            scaleSel.SetSelection(len(scaleChoices)-1)
1546        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1547        maxSizer.Add(scaleSel,(r,c),(2,1),flag=wx.ALIGN_CENTER)
1548        c = 0
1549        r = 1
1550        maxSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Min intensity'),(r,c))
1551        c += 1
1552        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1553        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1554        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1555        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1556        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1557        minSel = wx.Slider(parent=G2frame.dataDisplay,style=wx.SL_HORIZONTAL,value=sv0,size=(300,-1))
1558        maxSizer.Add(minSel,(r,c),flag=wx.EXPAND|wx.ALL)
1559        c += 1
1560        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1561        minVal = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,Range[1],0,
1562            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1563        maxSizer.Add(minVal,(r,c))
1564        return maxSizer
1565
1566    startScroll = None
1567    if G2frame.dataDisplay:
1568        startScroll = G2frame.dataDisplay.GetScrollPos(wx.VERTICAL) # save scroll position
1569        G2frame.dataDisplay.Destroy()
1570    else:
1571        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1572    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.MaskMenu)
1573    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyMask, id=G2gd.wxID_MASKCOPY)
1574    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadMask, id=G2gd.wxID_MASKLOAD)
1575    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadMask, id=G2gd.wxID_MASKLOADNOT)
1576    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveMask, id=G2gd.wxID_MASKSAVE)
1577    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAutoSpotMask, id=G2gd.wxID_FINDSPOTS)
1578    G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteSpotMask, id=G2gd.wxID_DELETESPOTS)
1579    G2frame.dataFrame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2gd.wxID_NEWMASKSPOT)
1580    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2gd.wxID_NEWMASKARC)
1581    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2gd.wxID_NEWMASKRING)
1582    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2gd.wxID_NEWMASKPOLY)
1583    G2frame.dataFrame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2gd.wxID_NEWMASKFRAME)
1584    if not G2frame.dataFrame.GetStatusBar():
1585        G2frame.dataFrame.CreateStatusBar()
1586    if G2frame.MaskKey == 'f':
1587        G2frame.dataFrame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon')
1588    elif G2frame.MaskKey == 'p':
1589        G2frame.dataFrame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon')
1590    elif G2frame.MaskKey == 'a':
1591        G2frame.dataFrame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location')
1592    elif G2frame.MaskKey == 'r':
1593        G2frame.dataFrame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location')
1594    else:
1595        G2frame.dataFrame.GetStatusBar().SetStatusText("To add mask: press a,r,s,p or f on 2D image for arc/ring/spot/polygon/frame")
1596    G2frame.dataDisplay = wxscroll.ScrolledPanel(G2frame.dataFrame)
1597    mainSizer = wx.BoxSizer(wx.VERTICAL)
1598    mainSizer.Add((5,10),0)
1599
1600    thresh = data['Thresholds']         #min/max intensity range
1601    Spots = data['Points']               #x,y,radius in mm
1602    Rings = data['Rings']               #radius, thickness
1603    Polygons = data['Polygons']         #3+ x,y pairs
1604    if 'Frames' not in data:
1605        data['Frames'] = []
1606    frame = data['Frames']             #3+ x,y pairs
1607    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1608
1609    ######################################################################
1610    CId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image,'Image Controls')
1611    controlData = G2frame.PatternTree.GetItemPyData(CId)
1612    Range = controlData['range']
1613    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1614    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1615
1616    littleSizer = wx.FlexGridSizer(0,3,0,5)
1617    littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Lower/Upper limits '),0,WACV)
1618    Text = wx.TextCtrl(G2frame.dataDisplay,value=str(thresh[0][0]),style=wx.TE_READONLY)
1619    littleSizer.Add(Text,0,WACV)
1620    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1621    Text = wx.TextCtrl(G2frame.dataDisplay,value=str(thresh[0][1]),style=wx.TE_READONLY)
1622    littleSizer.Add(Text,0,WACV)
1623    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1624    littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' Lower/Upper thresholds '),0,WACV)
1625    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=thresh[1],key=0,
1626        min=thresh[0][0],OnLeave=newReplot,typeHint=int)
1627    littleSizer.Add(lowerThreshold,0,WACV)
1628    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=thresh[1],key=1,
1629        max=thresh[0][1],OnLeave=newReplot,typeHint=int)
1630    littleSizer.Add(upperThreshold,0,WACV)
1631    mainSizer.Add(littleSizer,0,)
1632    if len(Spots):
1633        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Spot masks')
1634        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1635        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1636        littleSizer = wx.FlexGridSizer(0,3,0,5)
1637        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' position, mm'),0,WACV)
1638        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' diameter, mm'),0,WACV)
1639        littleSizer.Add((5,0),0)
1640        for i in range(len(Spots)):
1641            if len(Spots[i]):
1642                x,y,d = Spots[i]
1643                spotText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.2f,%.2f" % (x,y)),
1644                    style=wx.TE_READONLY)
1645                spotText.SetBackgroundColour(VERY_LIGHT_GREY)
1646                littleSizer.Add(spotText,0,WACV)
1647                spotText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1648                spotDiameter = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Spots[i],key=2,
1649                    max=100.,OnLeave=Replot,nDig=[8,2])
1650                littleSizer.Add(spotDiameter,0,WACV)
1651                spotDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1652                    locationcode='Delete+Points+'+str(i),handler=onDeleteMask)
1653                littleSizer.Add(spotDelete,0,WACV)
1654        mainSizer.Add(littleSizer,0,)
1655    if Rings:
1656        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Ring masks')
1657        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1658        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1659        littleSizer = wx.FlexGridSizer(0,3,0,5)
1660        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' 2-theta,deg'),0,WACV)
1661        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' thickness, deg'),0,WACV)
1662        littleSizer.Add((5,0),0)
1663        for i in range(len(Rings)):
1664            if Rings[i]:
1665                ringText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.3f" % (Rings[i][0])),
1666                    style=wx.TE_READONLY)
1667                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
1668                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1669                littleSizer.Add(ringText,0,WACV)
1670                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Rings[i],key=1,
1671                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
1672                littleSizer.Add(ringThick,0,WACV)
1673                ringDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1674                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
1675                littleSizer.Add(ringDelete,0,WACV)
1676        mainSizer.Add(littleSizer,0,)
1677    if Arcs:
1678        lbl = wx.StaticText(parent=G2frame.dataDisplay,label=' Arc masks')
1679        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1680        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1681        littleSizer = wx.FlexGridSizer(0,4,0,5)
1682        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' 2-theta,deg'),0,WACV)
1683        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' azimuth, deg'),0,WACV)
1684        littleSizer.Add(wx.StaticText(parent=G2frame.dataDisplay,label=' thickness, deg'),0,WACV)
1685        littleSizer.Add((5,0),0)
1686        for i in range(len(Arcs)):
1687            if Arcs[i]:
1688                tth,azimuth,thick = Arcs[i]
1689                arcText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%.3f" % (tth)),
1690                    style=wx.TE_READONLY)
1691                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
1692                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1693                littleSizer.Add(arcText,0,WACV)
1694                azmText = wx.TextCtrl(parent=G2frame.dataDisplay,value=("%d,%d" % (azimuth[0],azimuth[1])),
1695                    style=wx.TE_READONLY)
1696                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
1697                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1698                littleSizer.Add(azmText,0,WACV)
1699                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,loc=Arcs[i],key=2,
1700                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
1701                littleSizer.Add(arcThick,0,WACV)
1702                arcDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1703                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
1704                littleSizer.Add(arcDelete,0,WACV)
1705        mainSizer.Add(littleSizer,0,)
1706    if Polygons:
1707        lbl = wx.StaticText(parent=G2frame.dataDisplay,
1708            label=' Polygon masks (on plot RB vertex drag to move, LB vertex drag to insert)')
1709        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1710        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1711        littleSizer = wx.FlexGridSizer(0,2,0,5)
1712        for i in range(len(Polygons)):
1713            if Polygons[i]:
1714                polyList = []
1715                for x,y in Polygons[i]:
1716                    polyList.append("%.2f, %.2f"%(x,y))
1717                polyText = wx.ComboBox(G2frame.dataDisplay,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
1718                littleSizer.Add(polyText,0,WACV)
1719                polyDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1720                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
1721                littleSizer.Add(polyDelete,0,WACV)
1722        mainSizer.Add(littleSizer,0,)
1723    if frame:
1724        lbl = wx.StaticText(parent=G2frame.dataDisplay,
1725            label=' Frame mask (on plot RB vertex drag to move, LB vertex drag to insert)')
1726        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1727        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1728        littleSizer = wx.FlexGridSizer(0,2,0,5)
1729        frameList = []
1730        for x,y in frame:
1731            frameList.append("%.2f, %.2f"%(x,y))
1732        frameText = wx.ComboBox(G2frame.dataDisplay,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
1733        littleSizer.Add(frameText,0,WACV)
1734        frameDelete = G2G.G2LoggedButton(G2frame.dataDisplay,label='delete?',
1735            locationcode='Delete+Frame',handler=onDeleteFrame)
1736        littleSizer.Add(frameDelete,0,WACV)
1737        mainSizer.Add(littleSizer,0,)
1738    mainSizer.Layout()   
1739    G2frame.dataDisplay.SetSizer(mainSizer)
1740    G2frame.dataDisplay.SetupScrolling()
1741    Size = mainSizer.Fit(G2frame.dataFrame)
1742    Size[0] += 50 # room for scrollbar & status msg
1743    Size[1] = min(Size[1],500)
1744    G2frame.dataFrame.setSizePosLeft(Size)   
1745    if startScroll: # reset scroll to saved position
1746        G2frame.dataDisplay.Scroll(0,startScroll) # set to saved scroll position
1747        wx.Yield()
1748
1749################################################################################
1750##### Stress/Strain
1751################################################################################
1752
1753def UpdateStressStrain(G2frame,data):
1754    '''Shows and handles the controls on the "Stress/Strain"
1755    data tree entry
1756    '''
1757   
1758    def OnAppendDzero(event):
1759        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
1760            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0],'fixDset':False})
1761        UpdateStressStrain(G2frame,data)
1762       
1763    def OnUpdateDzero(event):
1764        for item in data['d-zero']:
1765            if item['Dcalc']:   #skip unrefined ones
1766                item['Dset'] = item['Dcalc']
1767        UpdateStressStrain(G2frame,data)
1768           
1769    def OnCopyStrSta(event):
1770        Names = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1771        if len(Names) == 1:
1772            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
1773            return
1774        Source = G2frame.PatternTree.GetItemText(G2frame.Image)
1775        Names.pop(Names.index(Source))
1776        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
1777        try:
1778            if dlg.ShowModal() == wx.ID_OK:
1779                items = dlg.GetSelections()
1780                for item in items:
1781                    name = Names[item]
1782                    Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1783                    CId = G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain')
1784                    oldData = G2frame.PatternTree.GetItemPyData(CId)
1785                    load = oldData.get('Sample load',0.0)
1786                    Data = copy.deepcopy(data)
1787                    Data['Sample load'] = load
1788                    G2frame.PatternTree.SetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain'),Data)
1789        finally:
1790            dlg.Destroy()
1791            G2frame.PatternTree.SelectItem(G2frame.PickId)
1792
1793    def OnLoadStrSta(event):
1794        pth = G2G.GetImportPath(G2frame)
1795        if not pth: pth = '.'
1796        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1797            'image control files (*.strsta)|*.strsta',wx.OPEN)
1798        try:
1799            if dlg.ShowModal() == wx.ID_OK:
1800                filename = dlg.GetPath()
1801                File = open(filename,'r')
1802                S = File.read()
1803                data = eval(S)
1804                Controls = G2frame.PatternTree.GetItemPyData(
1805                    G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1806                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1807                UpdateStressStrain(G2frame,data)
1808                G2plt.PlotExposedImage(G2frame,event=event)
1809                G2plt.PlotStrain(G2frame,data,newPlot=True)
1810                File.close()
1811        finally:
1812            dlg.Destroy()
1813
1814    def OnSaveStrSta(event):
1815        pth = G2G.GetExportPath(G2frame)
1816        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
1817            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1818        try:
1819            if dlg.ShowModal() == wx.ID_OK:
1820                filename = dlg.GetPath()
1821                File = open(filename,'w')
1822                keys = ['Type','Sample phi','Sample z','Sample load']
1823                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat','fixDset']
1824                File.write('{\n\t')
1825                for key in keys:
1826                    if key in 'Type':
1827                        File.write("'"+key+"':'"+data[key]+"',")
1828                    else:
1829                        File.write("'"+key+"':"+str(data[key])+',')
1830                File.write('\n\t'+"'d-zero':[\n")
1831                for data2 in data['d-zero']:
1832                    File.write('\t\t{')
1833                    for key in keys2:
1834                        File.write("'"+key+"':"+str(data2[key])+',')
1835                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
1836                File.write('\t]\n}')
1837                File.close()
1838        finally:
1839            dlg.Destroy()
1840           
1841    def OnStrStaSample(event):
1842        filename = ''
1843        pth = G2G.GetImportPath(G2frame)
1844        if not pth: pth = '.'
1845        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
1846            'metadata file (*.*)|*.*',wx.OPEN)
1847        try:
1848            if dlg.ShowModal() == wx.ID_OK:
1849                filename = dlg.GetPath()
1850                File = open(filename,'r')
1851                S = File.readline()
1852                newItems = []
1853                itemNames = []
1854                Comments = []
1855                while S:
1856                    if S[0] == '#':
1857                        Comments.append(S)
1858                        S = File.readline()
1859                        continue
1860                    S = S.replace(',',' ').replace('\t',' ')
1861                    Stuff = S[:-1].split()
1862                    itemNames.append(Stuff[0])
1863                    newItems.append(Stuff[1:])
1864                    S = File.readline()               
1865                File.close()
1866        finally:
1867            dlg.Destroy()
1868        if not filename:
1869            G2frame.ErrorDialog('Nothing to do','No file selected')
1870            return
1871        dataDict = dict(zip(itemNames,newItems))
1872        ifany = False
1873        Names = [' ','Sample phi','Sample z','Sample load']
1874        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
1875            'Select columns',Comments,Names,np.array(newItems).T)
1876        try:
1877            if dlg.ShowModal() == wx.ID_OK:
1878                colNames,newData = dlg.GetSelection()
1879                dataDict = dict(zip(itemNames,newData.T))
1880                for item in colNames:
1881                    if item != ' ':
1882                        ifany = True
1883        finally:
1884            dlg.Destroy()
1885        if not ifany:
1886            G2frame.ErrorDialog('Nothing to do','No columns identified')
1887            return
1888        histList = []
1889        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)       
1890        while item:
1891            name = G2frame.PatternTree.GetItemText(item)
1892            if name.startswith('IMG'):
1893                histList.append(name)
1894            item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
1895        colIds = {}
1896        for i,name in enumerate(colNames):
1897            if name != ' ':
1898                colIds[name] = i
1899        for hist in histList:
1900            name = hist.split()[1]  #this is file name
1901            if name in dataDict:
1902                newItems = {}
1903                for item in colIds:
1904                    newItems[item] = float(dataDict[name][colIds[item]])
1905                Id = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,hist)
1906                stsrData = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Stress/Strain'))
1907                stsrData.update(newItems)       
1908        UpdateStressStrain(G2frame,data)       
1909   
1910    def OnPlotStrSta(event):
1911        Controls = G2frame.PatternTree.GetItemPyData(
1912            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1913        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1914        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1915        G2plt.PlotExposedImage(G2frame,event=event)
1916        G2frame.G2plotNB.Delete('Ring Intensities')
1917        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
1918            labelY='MRD',newPlot=True,Title='Ring Intensities',
1919            names=Names,lines=True)
1920       
1921    def OnSaveStrRing(event):
1922        Controls = G2frame.PatternTree.GetItemPyData(
1923            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1924        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
1925        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
1926        pth = G2G.GetExportPath(G2frame)
1927        dlg = wx.FileDialog(G2frame, 'Choose strain ring intensity file', pth, '', 
1928            'ring intensity file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1929        try:
1930            if dlg.ShowModal() == wx.ID_OK:
1931                filename = dlg.GetPath()
1932                File = open(filename,'w')
1933                for i,name in enumerate(Names):
1934                    File.write('%s%s\n'%(' Ring intensity for ',name))
1935                    File.write('%12s %12s\n'%('Azimuth','RMD'))
1936                    for item in RingInt[i].T:
1937                        File.write(' %12.3f %12.3f\n'%(item[0],item[1]))
1938                    File.write('\n')
1939                File.close()
1940        finally:
1941            dlg.Destroy()
1942               
1943               
1944    def OnFitStrSta(event):
1945        Controls = G2frame.PatternTree.GetItemPyData(
1946            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))
1947        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
1948        print 'Strain fitting finished'
1949        UpdateStressStrain(G2frame,data)
1950        G2plt.PlotExposedImage(G2frame,event=event)
1951        G2plt.PlotStrain(G2frame,data,newPlot=True)
1952       
1953    def OnFitAllStrSta(event):
1954        choices = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
1955        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
1956        names = []
1957        if dlg.ShowModal() == wx.ID_OK:
1958            for sel in dlg.GetSelections():
1959                names.append(choices[sel])
1960            Id =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
1961            if Id:
1962                SeqResult = G2frame.PatternTree.GetItemPyData(Id)
1963            else:
1964                SeqResult = {}
1965                Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
1966            SeqResult.update({'SeqPseudoVars':{},'SeqParFitEqList':[]})
1967        else:
1968            dlg.Destroy()
1969            return
1970        dlg.Destroy()
1971        if not names:
1972            return
1973        Reverse = False
1974        CopyForward = False
1975        choice = ['Reverse sequence','Copy from prev.',]
1976        dlg = wx.MultiChoiceDialog(G2frame.dataFrame,'Sequential controls','Select controls',choice)
1977        if dlg.ShowModal() == wx.ID_OK:
1978            for sel in dlg.GetSelections():
1979                if sel:
1980                    CopyForward = True
1981                else:
1982                    Reverse = True
1983        dlg.Destroy()
1984        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
1985            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
1986        wx.BeginBusyCursor()
1987        goodnames = []
1988        if Reverse:
1989            names.reverse()
1990        try:
1991            for i,name in enumerate(names):
1992                print ' Sequential strain fit for ',name
1993                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
1994                if not GoOn:
1995                    break
1996                sId =  G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name)
1997                G2frame.Image = sId
1998                Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,sId, 'Image Controls'))
1999                StaCtrls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,sId, 'Stress/Strain'))
2000                if not len(StaCtrls['d-zero']):
2001                    continue
2002                goodnames.append(name)
2003                Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(sId)
2004                image = GetImageZ(G2frame,Controls)
2005                sig = []
2006                varyList = []
2007                variables = []
2008                if i and CopyForward:
2009                    for j,ring in enumerate(StaCtrls['d-zero']):
2010                        ring['Emat'] = variables[4*j:4*j+3]
2011                #get results from previous & put in StaCtrls
2012                G2img.FitStrSta(image,StaCtrls,Controls)
2013                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
2014                parmDict = {'Sample load':StaCtrls['Sample load'],}
2015                varyNames = ['e11','e12','e22']
2016                coVar = np.eye(4*len(StaCtrls['d-zero']))
2017                for j,item in enumerate(StaCtrls['d-zero']):
2018                    variables += item['Emat']
2019                    sig += item['Esig']
2020                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
2021                    varyList += varylist
2022                    parmDict.update(dict(zip(varylist,item['Emat'])))
2023                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
2024                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2025                    variables.append(item['Ivar'])
2026                    varyList.append('%d;Ivar'%(j))
2027                    sig.append(None)
2028                    j4 = j*4
2029                    coVar[j4:j4+3,j4:j4+3] = item['covMat']
2030                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2031                    'covMatrix':coVar,'title':name,'parmDict':parmDict}
2032            else:
2033                SeqResult['histNames'] = goodnames
2034                dlg.Destroy()
2035                print ' ***** Sequential strain refinement successful *****'
2036        finally:
2037            wx.EndBusyCursor()   
2038        SeqResult['histNames'] = choices
2039        G2frame.PatternTree.SetItemPyData(Id,SeqResult)
2040        G2frame.PatternTree.SelectItem(Id)
2041        print 'All images fitted'
2042       
2043    def SamSizer():
2044       
2045        def OnStrainType(event):
2046            data['Type'] = strType.GetValue()
2047       
2048        samSizer = wx.BoxSizer(wx.HORIZONTAL)
2049        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Strain type: '),0,WACV)
2050        strType = wx.ComboBox(G2frame.dataDisplay,value=data['Type'],choices=['True','Conventional'],
2051            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2052        strType.SetValue(data['Type'])
2053        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2054        samSizer.Add(strType,0,WACV)
2055       
2056        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample phi: '),0,WACV)
2057        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2058        samSizer.Add(samPhi,0,WACV)
2059        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample delta-z(mm): '),0,WACV)
2060        samZ = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample z',nDig=(10,3),typeHint=float)
2061        samSizer.Add(samZ,0,WACV)
2062        samSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=' Sample load(MPa): '),0,WACV)
2063        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data,'Sample load',
2064                nDig=[8,3],typeHint=float,)
2065        samSizer.Add(samLoad,0,WACV)
2066
2067        return samSizer
2068       
2069    def DzeroSizer():
2070   
2071        def OnDzero(invalid,value,tc):
2072            data['d-zero'] = G2mth.sortArray(data['d-zero'],'Dset',reverse=True)
2073            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2074            if len(Ring):
2075                data['d-zero'][Indx[tc.GetId()]].update(R)
2076            else:
2077                G2frame.ErrorDialog('Strain peak selection','WARNING - No points found for this ring selection')
2078               
2079            wx.CallAfter(UpdateStressStrain,G2frame,data)
2080            G2plt.PlotExposedImage(G2frame,event=tc.event,newPlot=False)
2081            G2plt.PlotStrain(G2frame,data,newPlot=True)
2082           
2083        def OnDeleteDzero(event):
2084            Obj = event.GetEventObject()
2085            del(data['d-zero'][delIndx.index(Obj)])
2086            UpdateStressStrain(G2frame,data)
2087            G2plt.PlotExposedImage(G2frame,event=event,newPlot=True)
2088            G2plt.PlotStrain(G2frame,data,newPlot=True)
2089       
2090        def OnCutOff(invalid,value,tc):
2091            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[tc.GetId()]],G2frame.ImageZ,Controls)
2092            G2plt.PlotExposedImage(G2frame,event=tc.event)
2093            G2plt.PlotStrain(G2frame,data,newPlot=True)
2094       
2095        def OnPixLimit(event):
2096            Obj = event.GetEventObject()
2097            data['d-zero'][Indx[Obj.GetId()]]['pixLimit'] = int(Obj.GetValue())
2098            Ring,R = G2img.MakeStrStaRing(data['d-zero'][Indx[Obj.GetId()]],G2frame.ImageZ,Controls)
2099            G2plt.PlotExposedImage(G2frame,event=event)
2100            G2plt.PlotStrain(G2frame,data,newPlot=True)
2101           
2102        def OnFixDset(event):
2103            Obj = event.GetEventObject()
2104            data['d-zero'][Indx[Obj.GetId()]]['fixDset'] = Obj.GetValue()
2105           
2106        Indx = {}
2107        delIndx = []   
2108        dzeroSizer = wx.FlexGridSizer(0,8,5,5)
2109        for id,dzero in enumerate(data['d-zero']):
2110            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero #%d: '%(id))),0,WACV)
2111            dZero = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'Dset',
2112                min=0.25,max=20.,nDig=(10,5),typeHint=float,OnLeave=OnDzero)
2113            dzeroSizer.Add(dZero,0,WACV)
2114            Indx[dZero.GetId()] = id
2115            dfix = wx.CheckBox(G2frame.dataDisplay,label='Use Poisson mean?')
2116            dfix.SetValue(dzero.get('fixDset',False))
2117            dfix.Bind(wx.EVT_CHECKBOX,OnFixDset)
2118            Indx[dfix.GetId()] = id
2119            dzeroSizer.Add(dfix,0,WACV)
2120            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' d-zero ave: %.5f'%(dzero['Dcalc']))),0,WACV)
2121               
2122            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Min ring I/Ib '),0,WACV)
2123            cutOff = G2G.ValidatedTxtCtrl(G2frame.dataDisplay,data['d-zero'][id],'cutoff',
2124                    min=0.5,max=20.,nDig=(10,1),typeHint=float,OnLeave=OnCutOff)
2125            Indx[cutOff.GetId()] = id
2126            dzeroSizer.Add(cutOff,0,WACV)
2127       
2128            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,label=' Pixel search range '),0,WACV)
2129            pixLimit = wx.ComboBox(parent=G2frame.dataDisplay,value=str(dzero['pixLimit']),choices=['1','2','5','10','15','20'],
2130                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2131            pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
2132            Indx[pixLimit.GetId()] = id
2133            dzeroSizer.Add(pixLimit,0,WACV)               
2134               
2135            dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=(' Strain tensor:')),WACV)
2136            names = ['e11','e12','e22']
2137            for i in range(3):
2138                dzeroSizer.Add(wx.StaticText(G2frame.dataDisplay,-1,label=names[i]),0,WACV)
2139                tensorElem = wx.TextCtrl(G2frame.dataDisplay,-1,value='%.2f'%(dzero['Emat'][i]),style=wx.TE_READONLY)
2140                tensorElem.SetBackgroundColour(VERY_LIGHT_GREY)
2141                dzeroSizer.Add(tensorElem,0,WACV)
2142            dzeroDelete = wx.CheckBox(parent=G2frame.dataDisplay,label='delete?')
2143            dzeroDelete.Bind(wx.EVT_CHECKBOX,OnDeleteDzero)
2144            delIndx.append(dzeroDelete)
2145            dzeroSizer.Add(dzeroDelete,0,WACV)
2146           
2147        return dzeroSizer
2148       
2149# patches
2150    if 'Sample load' not in data:
2151        data['Sample load'] = 0.0
2152# end patches
2153   
2154    if G2frame.dataDisplay:
2155        G2frame.dataDisplay.Destroy()
2156    Controls = G2frame.PatternTree.GetItemPyData(
2157        G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls'))       
2158    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.StrStaMenu)
2159    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2gd.wxID_APPENDDZERO)
2160    G2frame.dataFrame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2gd.wxID_UPDATEDZERO)
2161    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2gd.wxID_STRSTAFIT)
2162    G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2gd.wxID_STRSTAPLOT) 
2163    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2gd.wxID_STRRINGSAVE) 
2164    G2frame.dataFrame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2gd.wxID_STRSTAALLFIT)
2165    G2frame.dataFrame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2gd.wxID_STRSTACOPY)
2166    G2frame.dataFrame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2gd.wxID_STRSTALOAD)
2167    G2frame.dataFrame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2gd.wxID_STRSTASAVE)
2168    G2frame.dataFrame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2gd.wxID_STRSTSAMPLE)       
2169    if not G2frame.dataFrame.GetStatusBar():
2170        G2frame.dataFrame.CreateStatusBar()
2171    if G2frame.StrainKey == 'a':    #probably doesn't happen
2172        G2frame.dataFrame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value')
2173    else:
2174        G2frame.dataFrame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring")
2175       
2176    G2frame.dataDisplay = wxscroll.ScrolledPanel(G2frame.dataFrame)
2177    mainSizer = wx.BoxSizer(wx.VERTICAL)
2178    mainSizer.Add((5,10),0)
2179    mainSizer.Add(SamSizer())
2180    mainSizer.Add((5,10),0)
2181    mainSizer.Add(DzeroSizer())
2182   
2183    mainSizer.Layout()   
2184    G2frame.dataDisplay.SetSizer(mainSizer)
2185    G2frame.dataDisplay.SetAutoLayout(1)
2186    G2frame.dataDisplay.SetupScrolling()
2187    Size = mainSizer.Fit(G2frame.dataFrame)
2188    Size[0] += 25
2189    G2frame.dataFrame.setSizePosLeft(Size)   
2190
2191###########################################################################
2192# Autointegration
2193###########################################################################
2194def ReadMask(filename):
2195    'Read a mask (.immask) file'
2196    File = open(filename,'r')
2197    save = {}
2198    S = File.readline()
2199    while S:
2200        if S[0] == '#':
2201            S = File.readline()
2202            continue
2203        [key,val] = S.strip().split(':',1)
2204        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2205            save[key] = eval(val)
2206        S = File.readline()
2207    File.close()
2208    CleanupMasks(save)
2209    return save
2210
2211def ReadControls(filename):
2212    'read an image controls (.imctrl) file'
2213    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2214            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2215            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2216            'PolaVal','SampleAbs','dark image','background image']
2217    File = open(filename,'r')
2218    save = {}
2219    S = File.readline()
2220    while S:
2221        if S[0] == '#':
2222            S = File.readline()
2223            continue
2224        [key,val] = S.strip().split(':',1)
2225        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2226            save[key] = val
2227        elif key in ['rotation']:
2228            save[key] = float(val)
2229        elif key in ['center',]:
2230            if ',' in val:
2231                save[key] = eval(val)
2232            else:
2233                vals = val.strip('[] ').split()
2234                save[key] = [float(vals[0]),float(vals[1])] 
2235        elif key in cntlList:
2236            save[key] = eval(val)
2237        S = File.readline()
2238    File.close()
2239    return save
2240
2241def Read_imctrl(imctrl_file):
2242    '''Read an image control file and record control parms into a dict, with some simple
2243    type conversions
2244    '''
2245    save = {'filename':imctrl_file}
2246    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2247    if os.path.exists(immask_file):
2248        save['maskfile'] = immask_file
2249    else:
2250        save['maskfile'] = '(none)'
2251    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2252                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2253                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2254                        'PolaVal','SampleAbs','dark image','background image','setdist']
2255    File = open(imctrl_file,'r')
2256    fullIntegrate = False
2257    try:
2258        S = File.readline()
2259        while S:
2260            if S[0] == '#':
2261                S = File.readline()
2262                continue
2263            [key,val] = S.strip().split(':',1)
2264            if val.find(':') != -1:
2265                #print 'rejecting ',key,val
2266                S = File.readline()
2267                continue
2268            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2269                save[key] = val
2270            elif key == 'rotation':
2271                save[key] = float(val)
2272            elif key == 'fullIntegrate':
2273                fullIntegrate = eval(val)
2274            elif key == 'LRazimuth':
2275                save['LRazimuth_min'],save['LRazimuth_max'] = eval(val)[0:2]
2276            elif key == 'IOtth':
2277                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2278            elif key == 'center':
2279                if ',' in val:
2280                    vals = eval(val)
2281                else:
2282                    vals = val.strip('[] ').split()
2283                    vals = [float(vals[0]),float(vals[1])] 
2284                save['center_x'],save['center_y'] = vals[0:2]
2285            elif key in cntlList:
2286                save[key] = eval(val)
2287            S = File.readline()
2288    finally:
2289        File.close()
2290        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,0.
2291    return save
2292   
2293class AutoIntFrame(wx.Frame):
2294    '''Creates a wx.Frame window for the Image AutoIntegration.
2295    The intent is that this will be used as a non-modal dialog window.
2296   
2297    Implements a Start button that morphs into a pause and resume button.
2298    This button starts a processing loop that is repeated every
2299    :meth:`PollTime` seconds.
2300
2301    :param wx.Frame G2frame: main GSAS-II frame
2302    :param float PollTime: frequency in seconds to repeat calling the
2303      processing loop. (Default is 30.0 seconds.)
2304    '''
2305    def __init__(self,G2frame,PollTime=30.0):
2306        def OnStart(event):
2307            '''Called when the start button is pressed. Changes button label
2308            to Pause. When Pause is pressed the label changes to Resume.
2309            When either Start or Resume is pressed, the processing loop
2310            is started. When Pause is pressed, the loop is stopped.
2311            '''
2312            # check inputs for errors before starting
2313            #err = ''
2314            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2315            #    err += '\nPlease select at least one output format\n'
2316            #if err:
2317            #    G2G.G2MessageBox(self,err)
2318            #    return
2319            self.Pause = False
2320            # change button label
2321            if self.btnstart.GetLabel() != 'Pause':
2322                self.btnstart.SetLabel('Pause')
2323                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2324                if self.timer.IsRunning(): self.timer.Stop()
2325                self.PreventReEntryTimer = False
2326                if self.StartLoop():
2327                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2328                    return
2329                self.OnTimerLoop(None) # run once immediately
2330                if not self.Pause:
2331                    # no pause, so start timer to check for new files
2332                    self.timer.Start(int(1000*PollTime),oneShot=False)
2333                    return
2334            # we will get to this point if Paused
2335            self.OnPause()
2336           
2337        def OnReset(event):
2338            '''Called when Reset button is pressed. This stops the
2339            processing loop and resets the list of integrated files so
2340            all images can be reintegrated.
2341            '''
2342            self.btnstart.SetLabel('Restart')
2343            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2344            if self.timer.IsRunning(): self.timer.Stop()
2345            self.Reset = True
2346            self.Pause = True
2347           
2348        def OnQuit(event):
2349            '''Stop the processing loop and close the Frame
2350            '''
2351            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2352            wx.CallAfter(self.Destroy)
2353           
2354        def OnBrowse(event):
2355            '''Responds when the Browse button is pressed to load a file.
2356            The routine determines which button was pressed and gets the
2357            appropriate file type and loads it into the appropriate place
2358            in the dict.
2359            '''
2360            if btn3 == event.GetEventObject():
2361                dlg = wx.DirDialog(
2362                    self, 'Select directory for output files',
2363                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2364                dlg.CenterOnParent()
2365                try:
2366                    if dlg.ShowModal() == wx.ID_OK:
2367                        self.params['outdir'] = dlg.GetPath()
2368                        fInp3.SetValue(self.params['outdir'])
2369                finally:
2370                    dlg.Destroy()
2371                return
2372            elif btn4 == event.GetEventObject():
2373                msg = ''
2374                pth = G2G.GetExportPath(G2frame)
2375                dlg = wx.FileDialog(
2376                    self, 'Select a PDF parameter file',
2377                    pth, self.params['pdfprm'], 
2378                    "PDF controls file (*.pdfprm)|*.pdfprm",
2379                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2380                dlg.CenterOnParent()
2381                try:
2382                    if dlg.ShowModal() == wx.ID_OK:
2383                        self.params['pdfprm'] = dlg.GetPath()
2384                        fInp4.SetValue(self.params['pdfprm'])
2385                        scanPDFprm()
2386                        msg = self.checkPDFprm(True)
2387                finally:
2388                    dlg.Destroy()
2389                if 'Error' in msg:
2390                    print(msg)
2391                    lbl = 'PDFPRM error'
2392                else:
2393                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2394                    lbl = 'PDFPRM information'
2395                G2G.G2MessageBox(self,msg,lbl)
2396                return
2397               
2398        def OnRadioSelect(event):
2399            '''Respond to a radiobutton selection and when in table
2400            mode, get distance-dependent parameters from user.
2401            '''
2402            self.Evaluator = None
2403            if self.useTable.GetValue():
2404                dlg = None
2405                try:
2406                    dlg = IntegParmTable(self) # create the dialog
2407                    dlg.CenterOnParent()
2408                    if dlg.ShowModal() == wx.ID_OK:
2409                        self.ImgTblParms = dlg.parms
2410                        self.IMfileList = dlg.IMfileList
2411                        self.Evaluator = DefineEvaluator(dlg)
2412                        self.params['Mode'] = 'table'
2413                        self.editTable.Enable(True)
2414                    else:
2415                        self.useActive.SetValue(True)
2416                finally:
2417                    if dlg: dlg.Destroy()
2418            elif self.useActive.GetValue():
2419                self.params['Mode'] = 'active'
2420                self.imageBase = G2frame.Image
2421                self.useActive.SetLabel("Active Image: "+
2422                        G2frame.PatternTree.GetItemText(self.imageBase))
2423                self.editTable.Enable(False)
2424            else:
2425                print('unexpected mode in OnRadioSelect')
2426
2427        def OnEditTable(event):
2428            '''Called to edit the distance-dependent parameter look-up table.
2429            Should be called only when table is defined and active.
2430            '''
2431            dlg = None
2432            try:
2433                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2434                dlg.CenterOnParent()
2435                if dlg.ShowModal() == wx.ID_OK:
2436                    self.ImgTblParms = dlg.parms
2437                    self.IMfileList = dlg.IMfileList
2438                    self.Evaluator = DefineEvaluator(dlg)
2439                    self.params['Mode'] = 'table'
2440                    self.editTable.Enable(True)
2441                else:
2442                    self.useActive.SetValue(True)
2443                    self.params['Mode'] = 'active'
2444                    self.imageBase = G2frame.Image
2445                    self.useActive.SetLabel("Active Image: "+
2446                            G2frame.PatternTree.GetItemText(self.imageBase))
2447                    self.editTable.Enable(False)
2448            finally:
2449                if dlg: dlg.Destroy()
2450               
2451        def showPDFctrls(event):
2452            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2453            sizer layout with .Show(True) before .Show(False) will work properly.
2454            '''
2455            fInp4.Enable(self.params['ComputePDF'])
2456            fInp4.Show(self.params['ComputePDF'])
2457            fInp4a.Enable(self.params['ComputePDF'])
2458            btn4.Enable(self.params['ComputePDF'])
2459            cOpt.Enable(self.params['ComputePDF'])
2460            if self.params['ComputePDF']:
2461                lbl4.SetForegroundColour("black")
2462                lbl4a.SetForegroundColour("black")
2463            else:
2464                lbl4.SetForegroundColour("gray")
2465                lbl4a.SetForegroundColour("gray")
2466                                   
2467        def scanPDFprm(**kw):
2468            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2469            fInp4._IndicateValidity()
2470           
2471        def OnAutoScale(event):
2472            self.AutoScale = autoscale.GetValue()
2473           
2474        def OnAutoScaleName(event):
2475            self.AutoScaleName = scalename.GetValue()
2476            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2477            scaleval.SetValue(self.Scale[0])
2478               
2479        ##################################################
2480        # beginning of __init__ processing
2481        ##################################################
2482        self.G2frame = G2frame
2483        self.ImgTblParms = None
2484        self.IMfileList = None
2485        self.Evaluator = None
2486        self.params = {}
2487        self.Reset = False
2488        self.Pause = False
2489        self.PreventReEntryShowMatch = False
2490        self.PreventReEntryTimer = False
2491        self.params['IMGfile'] = ''
2492        self.params['MaskFile'] = ''
2493        self.params['IgnoreMask'] = True
2494        self.fmtlist = G2IO.ExportPowderList(G2frame)
2495        self.timer = wx.Timer()
2496        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2497        self.imageBase = G2frame.Image
2498        self.params['ComputePDF'] = False
2499        self.params['pdfDmax'] = 0.0
2500        self.params['pdfprm'] = ''
2501        self.params['optPDF'] = True
2502        self.pdfControls = {}
2503        self.AutoScale = False
2504        self.Scale = [1.0,]
2505
2506        G2frame.PatternTree.GetSelection()
2507        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(self.imageBase) 
2508        self.imagedir,fileroot = os.path.split(imagefile)
2509        Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2510            G2frame,self.imageBase, 'Comments'))
2511        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2512        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2513        self.AutoScales = {}
2514        if DefaultAutoScaleNames is not None:
2515            for comment in Comments:
2516                if '=' in comment:
2517                    name,val = comment.split('=',1) 
2518                    if name in DefaultAutoScaleNames:
2519                        try:
2520                            self.AutoScales[name] = float(val)
2521                            if name == self.AutoScaleName:
2522                                self.Scale[0] = float(val)
2523                        except ValueError:
2524                            continue
2525        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2526        self.params['outdir'] = os.path.abspath(self.imagedir)
2527        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2528                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2529        self.Status = self.CreateStatusBar()
2530        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2531        mnpnl = wx.Panel(self)
2532        mnsizer = wx.BoxSizer(wx.VERTICAL)
2533        # box for integration controls & masks input
2534        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2535        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2536        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2537        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2538                            style = wx.RB_GROUP)
2539        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2540        self.useActive.SetLabel("Active Image: "+
2541                                G2frame.PatternTree.GetItemText(self.imageBase))
2542        lblsizr.Add(self.useActive,1,wx.EXPAND,1)
2543        self.useActive.SetValue(True)
2544        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2545        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2546        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2547        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2548        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2549        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2550        self.editTable.Enable(False)
2551        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2552        # bind button and deactivate be default
2553        lblsizr.Add(minisizer)
2554        mnsizer.Add(lblsizr,1,wx.EXPAND,1)
2555
2556        # file filter stuff
2557        sizer = wx.BoxSizer(wx.HORIZONTAL)
2558        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Image filter'))
2559        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',OnLeave=self.ShowMatchingFiles)
2560        sizer.Add(flterInp)
2561        mnsizer.Add(sizer,0,wx.ALIGN_RIGHT,1)
2562        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2563        mnsizer.Add(self.ListBox,0,wx.EXPAND,1)
2564        self.ShowMatchingFiles(self.params['filter'])
2565        # box for output selections
2566        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2567        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2568        sizer = wx.BoxSizer(wx.HORIZONTAL)
2569        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2570        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2571        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2572        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2573        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2574        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2575        lblsizr.Add(sizer,0,wx.EXPAND)
2576        sizer = wx.BoxSizer(wx.HORIZONTAL)
2577        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s): '))
2578        for dfmt in self.fmtlist:
2579            fmt = dfmt[1:]
2580            self.params[fmt] = False
2581            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2582            sizer.Add(btn)
2583        lblsizr.Add(sizer)
2584        sizer = wx.BoxSizer(wx.HORIZONTAL)
2585        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2586        self.params['SeparateDir'] = False
2587        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2588        lblsizr.Add(sizer)
2589        if self.AutoScales:
2590            sizer = wx.BoxSizer(wx.HORIZONTAL)
2591            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2592            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2593            sizer.Add(autoscale,0,WACV)
2594            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=self.AutoScales.keys(),
2595                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2596            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2597            sizer.Add(scalename,0,WACV)
2598            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2599            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2600            sizer.Add(scaleval,0,WACV)
2601            lblsizr.Add(sizer,0)
2602        #ToDO: Autonormalize, parm name?, scaling value?
2603        sizer = wx.BoxSizer(wx.HORIZONTAL)
2604        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2605        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2606        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2607        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2608        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2609        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2610        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2611        sizer.Add(cOpt)
2612        lblsizr.Add(sizer,0)
2613        sizer = wx.BoxSizer(wx.HORIZONTAL)
2614        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2615        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2616        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2617                                     OnLeave=scanPDFprm)
2618        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2619        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2620        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2621        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2622        lblsizr.Add(sizer,0,wx.EXPAND)
2623        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2624        # buttons on bottom
2625        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2626        sizer = wx.BoxSizer(wx.HORIZONTAL)
2627        sizer.Add((20,-1))
2628        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2629        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2630        sizer.Add(self.btnstart)
2631        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2632        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2633        sizer.Add(self.btnreset)
2634        sizer.Add((20,-1),wx.EXPAND,1)
2635        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2636        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2637        sizer.Add(self.btnclose)
2638        sizer.Add((20,-1))
2639        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2640        # finish up window
2641        mnpnl.SetSizer(mnsizer)
2642        OnRadioSelect(None) # disable widgets
2643        mnsizer.Fit(self)
2644        self.CenterOnParent()
2645        self.Show()
2646        showPDFctrls(None)
2647
2648    def checkPDFprm(self,ShowContents=False):
2649        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2650        If ShowContents is True, a formatted text version of some of the file
2651        contents is returned. If errors are found, the return string will contain
2652        the string "Error:" at least once.
2653        '''
2654        self.pdfControls = {}
2655        msg = ''
2656        File = None
2657        try:
2658            File = open(self.params['pdfprm'],'r')
2659            S = File.readline()
2660            while S:
2661                if '#' in S:
2662                    S = File.readline()
2663                    continue
2664                key,val = S.split(':',1)
2665                try:
2666                    self.pdfControls[key] = eval(val)
2667                except:
2668                    self.pdfControls[key] = val
2669                S = File.readline()
2670        except Exception as err:
2671            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2672            if GSASIIpath.GetConfigValue('debug'):
2673                print(msg)
2674                print(err)
2675            self.pdfControls = {}
2676            return msg
2677        finally:
2678            if File: File.close()
2679        formula = ''
2680        for el in self.pdfControls['ElList']:
2681            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2682            if formula: formula += ' '
2683            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2684        if not formula:
2685            msg += 'Error: no chemical formula in file'
2686        for key in ['Sample Bkg.','Container','Container Bkg.']:
2687            if key not in self.pdfControls:
2688                if msg: msg += '\n'
2689                msg += 'Error: missing key in self.pdfControls: '+key
2690                continue
2691        if msg or not ShowContents: return msg  # stop on error
2692        msg += 'Default formula: '+formula+'\n'
2693        for key in ['Sample Bkg.','Container','Container Bkg.']:
2694            name = self.pdfControls[key]['Name']
2695            mult = self.pdfControls[key].get('Mult',0.0)
2696            if not name: continue
2697            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2698            if not G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,name):
2699                msg += ' *** missing ***'
2700        return msg
2701   
2702    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2703        G2frame = self.G2frame
2704        if invalid: return
2705        msg = ''
2706        if self.PreventReEntryShowMatch: return
2707        self.PreventReEntryShowMatch = True
2708        imageFileList = []
2709        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2710            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2711            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2712            if imagefile not in imageFileList: imageFileList.append(imagefile)
2713            if img not in G2frame.IntegratedList:
2714                if msg: msg += '\n'
2715                msg += '  ' + img
2716        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
2717        msg1 = ""
2718        try:
2719            imageList = sorted(
2720                glob.glob(os.path.join(self.imagedir,value)))
2721            if not imageList:
2722                msg1 = 'Warning: No files match search string '+os.path.join(self.imagedir,value)
2723            else:
2724                for fil in imageList:
2725                    if fil not in imageFileList: msg1 += '\n  '+fil
2726                if msg1:
2727                    msg += 'Files to integrate from '+os.path.join(self.imagedir,value)+msg1
2728                else:
2729                    msg += 'No files found to read'
2730        except IndexError:
2731            msg += 'Error searching for files named '+os.path.join(self.imagedir,value)
2732        self.ListBox.Clear()
2733        self.ListBox.AppendItems(msg.split('\n'))
2734        self.PreventReEntryShowMatch = False
2735        return
2736       
2737    def OnPause(self):
2738        '''Respond to Pause, changes text on button/Status line, if needed
2739        Stops timer
2740        self.Pause should already be True
2741        '''
2742        if self.timer.IsRunning(): self.timer.Stop()
2743        if self.btnstart.GetLabel() == 'Restart':
2744            return
2745        if self.btnstart.GetLabel() != 'Resume':
2746            print('\nPausing autointegration\n')
2747            self.btnstart.SetLabel('Resume')
2748            self.Status.SetStatusText(
2749                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
2750        self.Pause = True
2751           
2752    def IntegrateImage(self,img):
2753        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
2754        are placed in G2frame.IntgOutList
2755        '''
2756        G2frame = self.G2frame
2757        imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
2758        G2frame.Image = imgId
2759        G2frame.PickId = G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Image Controls')
2760        # do integration
2761        size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
2762        if self.AutoScale:
2763            Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2764                G2frame,imgId, 'Comments'))
2765            for comment in Comments:
2766                if '=' in comment:
2767                    name,val = comment.split('=',1) 
2768                    if name == self.AutoScaleName:
2769                        val = float(val)
2770                        if val > 0.:
2771                            Scale = self.Scale[0]/val
2772                        break
2773        masks = G2frame.PatternTree.GetItemPyData(
2774            G2gd.GetPatternTreeItemId(G2frame,G2frame.Image, 'Masks'))
2775        data = G2frame.PatternTree.GetItemPyData(G2frame.PickId)
2776        # simulate a Image Controls press, since that is where the
2777        # integration is hidden
2778        UpdateImageControls(G2frame,data,masks,IntegrateOnly=True)
2779        G2frame.IntegratedList.append(img) # note this as integrated
2780        # split name and control number
2781        s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
2782        namepre = s[0]
2783        if len(s) > 1:
2784            namenum = s[1]
2785        else:
2786            namenum = ''
2787        for Id in G2frame.IntgOutList: # loop over newly created PDWR entry(ies)
2788            # save the created PWDR tree names so that a reset can delete them
2789            G2frame.Image = Id
2790            treename = G2frame.PatternTree.GetItemText(Id)
2791            G2frame.AutointPWDRnames.append(treename)
2792            # write out the images in the selected formats
2793            Sdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Sample Parameters'))
2794            if self.AutoScale:
2795                print 'Rescale by %.4f'%(Scale)
2796                y,w = G2frame.PatternTree.GetItemPyData(Id)[1][1:3]
2797                y *= Scale
2798                w /= Scale**2
2799            # determine the name for the current file
2800            fileroot = namepre
2801            if len(G2frame.IntgOutList) > 1:
2802                fileroot += "_AZM"
2803                if 'Azimuth' in Sdata:
2804                    fileroot += str(int(10*Sdata['Azimuth']))
2805                fileroot += "_" 
2806            fileroot += namenum
2807            # loop over selected formats
2808            for dfmt in self.fmtlist:
2809                if not self.params[dfmt[1:]]: continue
2810                if self.params['SeparateDir']:
2811                    subdir = dfmt[1:]
2812                else:
2813                    subdir = ''
2814                fil = os.path.join(self.params['outdir'],subdir,fileroot)
2815                print('writing file '+fil+dfmt)
2816                G2IO.ExportPowder(G2frame,treename,fil,dfmt)
2817               
2818    def EnableButtons(self,flag):
2819        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
2820        '''
2821        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
2822        # on windows
2823        if sys.platform != "win32":
2824            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
2825        self.btnstart.SetLabel('Pause')
2826        wx.Yield()
2827               
2828    def ResetFromTable(self,dist):
2829        '''Sets integration parameters based on values from
2830        the lookup table
2831        '''
2832        #dist = self.controlsDict['distance']
2833        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
2834        if GSASIIpath.GetConfigValue('debug'):
2835            print 'interpolated values: ',interpDict
2836        self.ImageControls = ReadControls(imgctrl)
2837        self.ImageControls.update(interpDict)
2838        self.ImageControls['showLines'] = True
2839        self.ImageControls['ring'] = []
2840        self.ImageControls['rings'] = []
2841        self.ImageControls['ellipses'] = []
2842        self.ImageControls['setDefault'] = False
2843        for i in 'range','size','GonioAngles':
2844            if i in self.ImageControls:
2845                del self.ImageControls[i]
2846        # load copy of Image Masks
2847        if immask:
2848            self.ImageMasks = ReadMask(immask)
2849            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
2850                del self.ImageMasks['Thresholds']
2851        else:
2852            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
2853       
2854    def StartLoop(self):
2855        '''Prepare to start autointegration timer loop.
2856        Save current Image params for use in future integrations
2857        also label the window so users understand what is being used
2858        '''
2859        print('\nStarting new autointegration\n')
2860        G2frame = self.G2frame
2861        # show current IMG base
2862        if self.params['Mode'] != 'table':
2863            self.useActive.SetLabel("Active Image: "+
2864                                    G2frame.PatternTree.GetItemText(self.imageBase))
2865            # load copy of Image Controls from current image and clean up
2866            # items that should not be copied
2867            self.ImageControls = copy.deepcopy(
2868                G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2869                    G2frame,self.imageBase, 'Image Controls')))
2870            self.ImageControls['showLines'] = True
2871            self.ImageControls['ring'] = []
2872            self.ImageControls['rings'] = []
2873            self.ImageControls['ellipses'] = []
2874            self.ImageControls['setDefault'] = False
2875            del self.ImageControls['range']
2876            del self.ImageControls['size']
2877            del self.ImageControls['GonioAngles']
2878            # load copy of Image Masks, keep thresholds
2879            self.ImageMasks = copy.deepcopy(
2880                G2frame.PatternTree.GetItemPyData(
2881                    G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks')))
2882            self.Thresholds = self.ImageMasks['Thresholds'][:]
2883            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
2884                del self.ImageMasks['Thresholds']   
2885        # make sure all output directories exist
2886        if self.params['SeparateDir']:
2887            for dfmt in self.fmtlist:
2888                if not self.params[dfmt[1:]]: continue
2889                dir = os.path.join(self.params['outdir'],dfmt[1:])
2890                if not os.path.exists(dir): os.makedirs(dir)
2891        else:
2892            if not os.path.exists(self.params['outdir']):
2893                os.makedirs(self.params['outdir'])
2894        if self.Reset: # special things to do after Reset has been pressed
2895            self.G2frame.IntegratedList = []
2896           
2897            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
2898                for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
2899                    # update controls from master
2900                    controlsDict = G2frame.PatternTree.GetItemPyData(
2901                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Image Controls'))
2902                    controlsDict.update(self.ImageControls)
2903                    # update masks from master
2904                    ImageMasks = G2frame.PatternTree.GetItemPyData(
2905                        G2gd.GetPatternTreeItemId(G2frame,self.imageBase, 'Masks'))
2906                    ImageMasks.update(self.ImageMasks)
2907            # delete all PWDR items created after last Start was pressed
2908            idlist = []
2909            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2910            while item:
2911                itemName = G2frame.PatternTree.GetItemText(item)
2912                if itemName in G2frame.AutointPWDRnames:
2913                    idlist.append(item)
2914                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2915            for item in idlist:
2916                G2frame.PatternTree.Delete(item)
2917            wx.Yield()
2918            self.Reset = False
2919        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
2920        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
2921        # check that AutoPDF input is OK, offer chance to use alternate PDWRs if referenced ones
2922        # are not present
2923        if self.params['ComputePDF']:
2924            msg = self.checkPDFprm()
2925            if 'Error:' in msg:
2926                print(msg)
2927                return True
2928            fileList = []
2929            id, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
2930            while id:
2931                name = G2frame.PatternTree.GetItemText(id)
2932                if name.startswith('PWDR '): fileList.append(name)
2933                id, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
2934            if not fileList:
2935                print(msg)
2936                print('No PWDR entries to select')
2937                return True
2938            for key in ['Sample Bkg.','Container','Container Bkg.']:
2939                name = self.pdfControls[key]['Name']
2940                if not name: continue
2941                if not G2gd.GetPatternTreeItemId(G2frame,G2frame.root,name):
2942                    indx = G2G.ItemSelector(fileList, self, header='Select PDWR item',
2943                                    title='Select a PDWR tree item for '+key+'\n(or cancel to quit)')
2944                    if indx is None:
2945                        print('No PWDR entry selected for '+key)
2946                        return True
2947                    self.pdfControls[key]['Name'] = fileList[indx]
2948        return False
2949               
2950    def OnTimerLoop(self,event):
2951        '''A method that is called every :meth:`PollTime` seconds that is
2952        used to check for new files and process them. Integrates new images.
2953        Also optionally sets up and computes PDF.
2954        This is called only after the "Start" button is pressed (then its label reads "Pause").
2955        '''
2956        def AutoIntegrateImage(imgId):
2957            '''Integrates an image that has been read into the data tree and updates the
2958            AutoInt window.
2959            '''
2960            img = G2frame.PatternTree.GetItemText(imgId)
2961            controlsDict = G2frame.PatternTree.GetItemPyData(
2962                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2963            ImageMasks = G2frame.PatternTree.GetItemPyData(
2964                G2gd.GetPatternTreeItemId(G2frame,imgId, 'Masks'))
2965            if self.params['Mode'] == 'table': # look up parameter values from table
2966                self.ResetFromTable(controlsDict['setdist'])
2967            # update controls from master
2968            controlsDict.update(self.ImageControls)
2969            # update masks from master w/o Thresholds
2970            ImageMasks.update(self.ImageMasks)
2971            self.EnableButtons(False)
2972            try:
2973                self.IntegrateImage(img)
2974            finally:
2975                self.EnableButtons(True)
2976            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
2977            wx.Yield()
2978            self.ShowMatchingFiles(self.params['filter'])
2979            wx.Yield()
2980           
2981        def AutoComputePDF(imgId):
2982            '''Computes a PDF for a PWDR data tree tree item
2983            '''
2984            for pwdr in G2frame.AutointPWDRnames[:]:
2985                if not pwdr.startswith('PWDR '): continue
2986                if pwdr in G2frame.AutointPDFnames: continue
2987                PWid = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,pwdr)
2988                controlsDict = G2frame.PatternTree.GetItemPyData(
2989                    G2gd.GetPatternTreeItemId(G2frame,imgId, 'Image Controls'))
2990                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
2991                    print('Skipping PDF for '+pwdr+' due to detector position')
2992                    continue
2993                # Setup PDF
2994                Data = G2frame.PatternTree.GetItemPyData(PWid)[1]
2995                pwdrMin = np.min(Data[1])
2996                Parms = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2997                    G2frame,PWid,'Instrument Parameters'))[0]
2998                fullLimits = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
2999                    G2frame,PWid,'Limits'))[0]
3000                if 'C' in Parms['Type'][0]:
3001                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
3002                else:
3003                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
3004                Qlimits = [0.9*qMax,qMax]
3005
3006                item = pwdr
3007                Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
3008                    G2frame,imgId, 'Comments'))
3009                ElList = {}
3010                sumnum = 1.0
3011                for item in Comments:           #grab chemical formula from Comments, if there
3012                    if 'formula' in item[:15].lower():
3013                        formula = item.split('=')[1].split()
3014                        elems = formula[::2]
3015                        nums = formula[1::2]
3016                        formula = zip(elems,nums)
3017                        sumnum = 0.
3018                        for [elem,num] in formula:
3019                            ElData = G2elem.GetElInfo(elem,Parms)
3020                            ElData['FormulaNo'] = float(num)
3021                            sumnum += float(num)
3022                            ElList[elem] = ElData
3023                PDFnames = G2gd.GetPatternTreeDataNames(G2frame,['PDF ',])
3024                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
3025                if not PDFid: continue
3026                PDFdata = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(
3027                    G2frame,PDFid, 'PDF Controls'))
3028                PDFdata.update(self.pdfControls)
3029                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
3030                PDFdata['Sample']['Name'] = pwdr
3031                # compute PDF
3032                wx.Yield()
3033                G2pdG.computePDF(G2frame,PDFdata)
3034                wx.Yield()
3035                G2frame.PatternId = PDFid
3036                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3037                if self.params['optPDF']:
3038                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3039                    wx.Yield()
3040                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3041                G2frame.AutointPDFnames.append(pwdr)
3042                # save names of PDF entry to be deleted later if needed
3043                G2frame.AutointPWDRnames.append(G2frame.PatternTree.GetItemText(PDFid))
3044           
3045        G2frame = self.G2frame
3046        try:
3047            self.currImageList = sorted(
3048                glob.glob(os.path.join(self.imagedir,self.params['filter'])))
3049            self.ShowMatchingFiles(self.params['filter'])
3050        except IndexError:
3051            self.currImageList = []
3052            return
3053
3054        if self.PreventReEntryTimer: return
3055        self.PreventReEntryTimer = True
3056        imageFileList = []
3057        # integrate the images that have already been read in, but
3058        # have not yet been processed           
3059        for img in G2gd.GetPatternTreeDataNames(G2frame,['IMG ']):
3060            imgId = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,img)
3061            size,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(imgId)
3062            # Create a list of image files that have been read in
3063            if imagefile not in imageFileList: imageFileList.append(imagefile)
3064            # skip if already integrated
3065            if img in G2frame.IntegratedList: continue
3066            AutoIntegrateImage(imgId)
3067            #self.Pause |= G2frame.PauseIntegration
3068            #if self.Pause:
3069            #    self.OnPause()
3070            #    self.PreventReEntryTimer = False
3071            #    return
3072            if self.pdfControls: AutoComputePDF(imgId)
3073            self.Pause |= G2frame.PauseIntegration
3074            if self.Pause:
3075                self.OnPause()
3076                self.PreventReEntryTimer = False
3077                return
3078
3079        # loop over image files matching glob, reading in any new ones
3080        for newImage in self.currImageList:
3081            if newImage in imageFileList or self.Pause: continue # already read?
3082            for imgId in G2IO.ReadImages(G2frame,newImage):
3083                AutoIntegrateImage(imgId)           
3084                #self.Pause |= G2frame.PauseIntegration
3085                #if self.Pause:
3086                #    self.OnPause()
3087                #    self.PreventReEntryTimer = False
3088                #    return
3089                if self.pdfControls: AutoComputePDF(imgId)
3090                self.Pause |= G2frame.PauseIntegration
3091                if self.Pause:
3092                    self.OnPause()
3093                    self.PreventReEntryTimer = False
3094                    return
3095        if GSASIIpath.GetConfigValue('debug'):
3096            import datetime
3097            print ("Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3098        self.PreventReEntryTimer = False
3099
3100def DefineEvaluator(dlg):
3101    '''Creates a function that provides interpolated values for a given distance value
3102    '''
3103    def Evaluator(dist):
3104        '''Interpolate image parameters for a supplied distance value
3105
3106        :param float dist: distance to use for interpolation
3107        :returns: a list with 3 items:
3108
3109          * a dict with parameter values,
3110          * the closest imctrl and
3111          * the closest maskfile (or None)
3112        '''           
3113        x = np.array([float(i) for i in parms[0]])
3114        closest = abs(x-dist).argmin()
3115        D = {'setdist':dist}
3116        imctfile = IMfileList[closest]
3117        if parms[-1][closest].lower() != '(none)':
3118            maskfile = parms[-1][closest]
3119        else:
3120            maskfile = None
3121        for c in range(1,cols-1):
3122            lbl = ParmList[c]
3123            if lbl in nonInterpVars:
3124                if lbl in ['outChannels',]:
3125                    D[lbl] = int(float(parms[c][closest]))
3126                else:
3127                    D[lbl] = float(parms[c][closest])
3128            else:
3129                y = np.array([float(i) for i in parms[c]])
3130                D[lbl] = np.interp(dist,x,y)
3131        # full integration when angular range is 0
3132        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3133        # conversion for paired values
3134        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3135            r = a.split('_')[0]
3136            D[r] = [D[a],D[b]]
3137            if r in ['LRazimuth',]:
3138                D[r] = [int(D[a]),int(D[b])]
3139            del D[a]
3140            del D[b]
3141        return D,imctfile,maskfile
3142    # save local copies of values needed in Evaluator
3143    parms = dlg.ReadImageParmTable()
3144    IMfileList = dlg.IMfileList
3145    cols = dlg.list.GetColumnCount()
3146    ParmList = dlg.ParmList
3147    nonInterpVars = dlg.nonInterpVars
3148    return Evaluator
3149
3150class IntegParmTable(wx.Dialog):
3151    '''Creates a dialog window with a table of integration parameters.
3152    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3153    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3154    creates a dictionary with interpolated parameter values.
3155    '''
3156    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3157            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3158            'maskfile',
3159            )
3160    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3161                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3162    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3163            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3164            'Mask File',
3165            )
3166    def __init__(self,parent,parms=None,IMfileList=None):
3167        self.G2frame = parent.G2frame
3168        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3169        if parms:
3170            self.parms = parms # list of values by column
3171            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3172        else:
3173            self.parms = [] # list of values by column
3174            self.IMfileList = [] # list of .imctrl file names for each entry in table
3175            files = []
3176            try:
3177                pth = G2G.GetImportPath(self.G2frame)
3178                if not pth: pth = '.'
3179                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3180                    style=wx.OPEN| wx.MULTIPLE,
3181                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3182                dlg.CenterOnParent()
3183                if dlg.ShowModal() == wx.ID_OK:
3184                    files = dlg.GetPaths()
3185                    self.parms,self.IMfileList = self.ReadFiles(files)
3186            finally:
3187                dlg.Destroy()
3188            if not files:
3189                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3190                return
3191        mainSizer = wx.BoxSizer(wx.VERTICAL)
3192        self.list = ImgIntLstCtrl(self, wx.ID_ANY,
3193                      style=wx.LC_REPORT
3194                          | wx.BORDER_SUNKEN
3195                         #| wx.BORDER_NONE
3196                         )
3197        mainSizer.Add(self.list,1,wx.EXPAND,1)
3198        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3199        btn = wx.Button(self, wx.ID_OK)
3200        btnsizer.Add(btn)
3201        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3202        btn.Bind(wx.EVT_BUTTON,self._onSave)
3203        btnsizer.Add(btn)
3204        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3205        btn.Bind(wx.EVT_BUTTON,self._onClose)
3206        btnsizer.Add(btn)
3207        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3208        self.SetSizer(mainSizer)
3209        self.list.FillList(self.parms)
3210        mainSizer.Layout()
3211        mainSizer.Fit(self)
3212       
3213    def ReadFiles(self,files):
3214        '''Reads a list of .imctrl files or a single .imtbl file
3215        '''
3216        tmpDict = {}
3217        if not files: return
3218        # option 1, a dump from a previous save
3219        if os.path.splitext(files[0])[1] == '.imtbl':
3220            fp = open(files[0],'r')
3221            S = fp.readline()
3222            while S:
3223                if S[0] != '#':
3224                    [key,val] = S[:-1].split(':',1)
3225                    tmpDict[key] = eval(val)
3226                S = fp.readline()
3227            fp.close()
3228            # delete entries where files do not exist
3229            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3230            if m1:
3231                print('\nimctrl file not found:')
3232                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3233            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3234            if m2:
3235                print('\nmask file not found')
3236                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3237            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3238            if m3:
3239                print('\nDropping entries due to negative distance: '+str(m3))
3240            m = sorted(set(m1 + m2 + m3))
3241            m.reverse()
3242            for c in m:
3243                for key in tmpDict:
3244                    del tmpDict[key][c]
3245            fileList = tmpDict.get('filenames','[]')
3246            parms = []
3247            if 'setdist' not in tmpDict:
3248                print(u'Old file, recreate before using: {}'.format(files[0]))
3249                return [[]],[]
3250            for key in self.ParmList:
3251                try:
3252                    float(tmpDict[key][0])
3253                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3254                except ValueError:
3255                    parms.append(tmpDict[key])
3256                except IndexError:
3257                    print('No valid image control entries read')
3258                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3259                    return [[]],[]
3260            return parms,fileList
3261        # option 2, read in a list of files
3262        for file in files: # read all files; place in dict by distance
3263            imgDict = Read_imctrl(file)
3264            dist = imgDict.get('setdist')
3265            if dist is None:
3266                print('Skipping old file, redo: {}'.format(file))
3267            tmpDict[dist] = imgDict
3268        parms = [[] for key in self.ParmList]
3269        fileList = []
3270        for d in sorted(tmpDict):
3271            fileList.append(tmpDict[d].get('filename'))
3272            if d is None: continue
3273            if d < 0: continue
3274            for i,key in enumerate(self.ParmList):
3275                val = tmpDict[d].get(key)
3276                try:
3277                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3278                except:
3279                    val = str(val)
3280                parms[i].append(val)
3281        return parms,fileList
3282   
3283    def ReadImageParmTable(self):
3284        '''Reads possibly edited values from the ListCtrl table and returns a list
3285        of values for each column.
3286        '''
3287        rows = self.list.GetItemCount()
3288        cols = self.list.GetColumnCount()
3289        parms = []
3290        for c in range(cols):
3291            parms.append([])
3292            for r in range(rows):
3293                parms[c].append(self.list.GetItem(r,c).GetText())
3294        return parms
3295
3296    def _onClose(self,event):
3297        'Called when Cancel button is pressed'
3298        self.EndModal(wx.ID_CANCEL)
3299       
3300    def _onSave(self,event):
3301        'Called when save button is pressed; creates a .imtbl file'
3302        fil = ''
3303        if self.G2frame.GSASprojectfile:
3304            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3305        dir,f = os.path.split(fil)
3306        pth = G2G.GetExportPath(self.G2frame)
3307        try:
3308            dlg = wx.FileDialog(self, 'Save table data as',
3309                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3310                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3311            dlg.CenterOnParent()
3312            if dlg.ShowModal() != wx.ID_OK: return
3313            fil = dlg.GetPath()
3314            fil = os.path.splitext(fil)[0]+'.imtbl'
3315        finally:
3316            dlg.Destroy()       
3317        parms = self.ReadImageParmTable()
3318        print('Writing image parameter table as '+fil)
3319        fp = open(fil,'w')
3320        for c in range(len(parms)-1):
3321            lbl = self.ParmList[c]
3322            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3323        lbl = self.ParmList[c+1]
3324        fp.write(lbl+': '+str(parms[c+1])+'\n')
3325        lbl = 'filenames'
3326        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3327        fp.close()
3328   
3329class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3330    '''Creates a custom ListCtrl for editing Image Integration parameters
3331    '''
3332    def __init__(self, parent, ID, pos=wx.DefaultPosition,
3333                 size=(1000,200), style=0):
3334        self.parent=parent
3335        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3336        listmix.ListCtrlAutoWidthMixin.__init__(self)
3337        listmix.TextEditMixin.__init__(self)
3338        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3339        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3340    def FillList(self,parms):
3341        'Places the current parms into the table'
3342        self.ClearAll()
3343        self.rowlen = len(self.parent.ParmList)
3344        for i,lbl in enumerate(self.parent.HeaderList):
3345            self.InsertColumn(i, lbl)
3346        for r,d in enumerate(parms[0]):
3347            if d is None: continue
3348            if float(d) < 0: continue
3349            index = self.InsertStringItem(sys.maxint, d)
3350            for j in range(1,len(parms)):
3351                self.SetStringItem(index, j, parms[j][r])
3352        for i,lbl in enumerate(self.parent.ParmList):
3353            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3354
3355    def OnDouble(self,evt):
3356        'respond to a double-click'
3357        self.CloseEditor()
3358        fil = '(none)'
3359        pth = G2G.GetImportPath(self.parent.G2frame)
3360        if not pth: pth = '.'
3361        try:
3362            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3363                                style=wx.OPEN,
3364                                wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3365            dlg.CenterOnParent()
3366            if dlg.ShowModal() == wx.ID_OK:
3367                fil = dlg.GetPath()
3368        finally:
3369            dlg.Destroy()
3370        if os.path.splitext(fil)[1] != '.imctrl':
3371            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3372            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3373        else:
3374            # insert or overwrite an instrument parameter set
3375            if not os.path.exists(fil):
3376                print('Does not exist: '+fil)
3377                return
3378            imgDict = Read_imctrl(fil)
3379            dist = imgDict['distance']
3380            parms = self.parent.ReadImageParmTable()
3381            x = np.array([float(i) for i in parms[0]])
3382            closest = abs(x-dist).argmin()
3383            closeX = x[closest]
3384            # fix IMfileList
3385            for c,lbl in enumerate(self.parent.ParmList):
3386                try:
3387                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3388                except ValueError:
3389                    vali = imgDict[lbl]
3390                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3391                    parms[c][closest] = vali
3392                elif dist > closeX: # insert after
3393                    parms[c].insert(closest+1,vali)
3394                else:
3395                    parms[c].insert(closest,vali)
3396            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3397                self.parent.IMfileList[closest] = fil
3398            elif dist > closeX: # insert after
3399                self.parent.IMfileList.insert(closest+1,fil)
3400            else:
3401                self.parent.IMfileList.insert(closest,fil)
3402            self.FillList(parms)
3403# Autointegration end
3404###########################################################################
Note: See TracBrowser for help on using the repository browser.