source: trunk/GSASIIimgGUI.py @ 4820

Last change on this file since 4820 was 4820, checked in by vondreele, 10 months ago

remove masks from argument list for MakeGainMap? - not used
Tweak gain map limits
fix to OnOrigX & OnOrien? - now set atom site fractions to the max found for atoms in RB
"fix" (with a try except) for the occasional crash on finding a nonexistent Pattern. Now just skips & tries again.

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