source: trunk/GSASIIimgGUI.py @ 4692

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

improve gain map calc - more steps for integration step

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