source: trunk/GSASIIimgGUI.py

Last change on this file was 5320, checked in by toby, 5 weeks ago

more fullrmc work (all but swaps done?); misc docs fixes

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