source: trunk/GSASIIimgGUI.py

Last change on this file was 5008, checked in by vondreele, 7 weeks ago

fix bug in G2plot for missing g(R) plot
fix SingleFloatDialog? "format" argument; should be 'fmt'

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