source: trunk/GSASIIimgGUI.py @ 4345

Last change on this file since 4345 was 4345, checked in by toby, 23 months ago

Add more image settings to preferences; prevent save of config.py after "no save"

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