source: trunk/GSASIIimgGUI.py @ 4570

Last change on this file since 4570 was 4570, checked in by vondreele, 3 years ago

Implement arc mask method for determining polarization with scipy minimize_scalar method to find minimum difference between arc mask on/mask off integrations

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