source: trunk/GSASIIimgGUI.py @ 4333

Last change on this file since 4333 was 4333, checked in by vondreele, 22 months ago

work on spot masking; change some text, menu names, etc.
Add menu item for Auto search for selected images.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 189.7 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2020-02-28 10:19:30 +0000 (Fri, 28 Feb 2020) $
5# $Author: vondreele $
6# $Revision: 4333 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 4333 2020-02-28 10:19:30Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIimgGUI: Image GUI*
12-------------------------
13
14Control image display and processing
15
16'''
17from __future__ import division, print_function
18import os
19import copy
20import glob
21import time
22import re
23import math
24import sys
25import wx
26import wx.lib.mixins.listctrl  as  listmix
27import wx.grid as wg
28import matplotlib as mpl
29import numpy as np
30import numpy.ma as ma
31import GSASIIpath
32GSASIIpath.SetVersionNumber("$Revision: 4333 $")
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['SpotMask']['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        calSel = wx.ComboBox(parent=G2frame.dataWindow,value=data['calibrant'],choices=calList,
1340            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1341        calSel.Bind(wx.EVT_COMBOBOX, OnNewCalibrant)
1342        comboSizer.Add(calSel,0,WACV)
1343        calibSizer.Add(comboSizer,0)
1344       
1345        comboSizer = wx.BoxSizer(wx.HORIZONTAL)   
1346        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Calib lines to skip   '),0,WACV)
1347        calibSkip  = wx.ComboBox(parent=G2frame.dataWindow,value=str(data['calibskip']),choices=[str(i) for i in range(25)],
1348            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1349        calibSkip.Bind(wx.EVT_COMBOBOX, OnCalibSkip)
1350        comboSizer.Add(calibSkip,0,WACV)
1351        calibSizer.Add(comboSizer,0)
1352       
1353        comboSizer = wx.BoxSizer(wx.HORIZONTAL)       
1354        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min calib d-spacing '),0,WACV)
1355        G2frame.calibDmin = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'calibdmin',nDig=(10,2),typeHint=float,min=0.25)
1356        comboSizer.Add(G2frame.calibDmin,0,WACV)
1357        calibSizer.Add(comboSizer,0)
1358       
1359        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1360        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min ring I/Ib '),0,WACV)
1361        cutOff = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'cutoff',nDig=(10,2),min=0.1)
1362        comboSizer.Add(cutOff,0,WACV)
1363        calibSizer.Add(comboSizer,0)
1364       
1365        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1366        comboSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Pixel search range '),0,WACV)
1367        pixLimit = wx.ComboBox(parent=G2frame.dataWindow,value=str(data['pixLimit']),choices=['1','2','5','10','15','20'],
1368            style=wx.CB_READONLY|wx.CB_DROPDOWN)
1369        pixLimit.Bind(wx.EVT_COMBOBOX, OnPixLimit)
1370        comboSizer.Add(pixLimit,0,WACV)
1371        calibSizer.Add(comboSizer,0)
1372       
1373        comboSizer = wx.BoxSizer(wx.HORIZONTAL)
1374        setRings = wx.CheckBox(parent=G2frame.dataWindow,label='Show ring picks?')
1375        comboSizer.Add(setRings,0)
1376        setRings.Bind(wx.EVT_CHECKBOX, OnSetRings)
1377        setRings.SetValue(data['setRings'])
1378        calibSizer.Add(comboSizer,0)
1379        return calibSizer
1380       
1381    def GonioSizer():
1382       
1383        def OnGlobalEdit(event):
1384            Names = []
1385            Items = []
1386            if G2frame.GPXtree.GetCount():
1387                Id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
1388                while Id:
1389                    name = G2frame.GPXtree.GetItemText(Id)
1390                    if 'IMG' in name:
1391                        ctrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
1392                        Names.append(name)
1393                        Items.append(ctrls['GonioAngles'])
1394                    Id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
1395                if len(Names) == 1:
1396                    G2frame.ErrorDialog('Nothing for global editing','There must be more than one "IMG" pattern')
1397                    return
1398                dlg = G2G.G2HistoDataDialog(G2frame,' Edit sample goniometer data:',
1399                    'Edit data',['Omega','Chi','Phi'],['%.2f','%.2f','%.2f'],Names,Items)
1400                try:
1401                    if dlg.ShowModal() == wx.ID_OK:
1402                        Id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
1403                        while Id:
1404                            name = G2frame.GPXtree.GetItemText(Id)
1405                            if 'IMG' in name:
1406                                ctrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Image Controls'))
1407                                vals = Items[Names.index(name)]
1408                                ctrls['GonioAngles'] = vals
1409                            Id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
1410                finally:
1411                    dlg.Destroy()
1412                    G2frame.GPXtree.SelectItem(G2frame.PickId)
1413       
1414        gonioSizer = wx.BoxSizer(wx.HORIZONTAL)
1415        names = ['Omega','Chi','Phi']
1416        gonioSizer.Add(wx.StaticText(G2frame.dataWindow,-1,'Sample goniometer angles: '),0,WACV)
1417        for i,name in enumerate(names):
1418            gonioSizer.Add(wx.StaticText(G2frame.dataWindow,-1,name),0,WACV)
1419            angle = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data['GonioAngles'],i,nDig=(8,2),typeHint=float)
1420            gonioSizer.Add(angle,0,WACV)
1421        globEdit = wx.Button(G2frame.dataWindow,-1,'Global edit')
1422        globEdit.Bind(wx.EVT_BUTTON,OnGlobalEdit)
1423        gonioSizer.Add(globEdit,0,WACV)
1424        return gonioSizer
1425       
1426    # UpdateImageControls: Image Controls main code             
1427                           
1428    #fix for old files:
1429    if 'azmthOff' not in data:
1430        data['azmthOff'] = 0.0
1431    if 'background image' not in data:
1432        data['background image'] = ['',-1.0]
1433    if 'dark image' not in data:
1434        data['dark image'] = ['',-1.0]
1435    if 'centerAzm' not in data:
1436        data['centerAzm'] = False
1437    if 'Oblique' not in data:
1438        data['Oblique'] = [0.5,False]
1439    if 'PolaVal' not in data:
1440        data['PolaVal'] = [0.99,False]
1441    #end fix
1442   
1443    if IntegrateOnly:
1444        Masks = G2frame.GPXtree.GetItemPyData(
1445            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Masks'))
1446        # Mhash = hash(str(Masks))  # TODO: implement this to save integration time (?)
1447        # if  Mhash != oldMhash:
1448        #     t0 = time.time()
1449        useMask = G2img.MakeUseMask(data,Masks,blkSize)
1450        #     print(' Use new mask; make mask time: %.3f'%(time.time()-t0))
1451        #     oldMhash = Mhash
1452        OnIntegrate(None,useTA=useTA,useMask=useMask)
1453        return
1454   
1455    G2frame.GetStatusBar().SetStatusText('* Global parameters in Multi-dist recalib.',1)
1456    colorList = sorted([m for m in mpl.cm.datad.keys() ],key=lambda s: s.lower())   #if not m.endswith("_r")
1457    calList = sorted([m for m in calFile.Calibrants.keys()],key=lambda s: s.lower())
1458    typeList = ['PWDR - powder diffraction data','SASD - small angle scattering data',]
1459    if not data.get('type'):                        #patch for old project files
1460        data['type'] = 'PWDR'
1461    typeDict = {'PWDR':typeList[0],'SASD':typeList[1],}
1462    G2frame.dataWindow.ClearData()
1463    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ImageMenu)
1464    G2frame.Bind(wx.EVT_MENU, OnCalibrate, id=G2G.wxID_IMCALIBRATE)
1465    G2frame.Bind(wx.EVT_MENU, OnRecalibrate, id=G2G.wxID_IMRECALIBRATE)
1466    G2frame.Bind(wx.EVT_MENU, OnRecalibAll, id=G2G.wxID_IMRECALIBALL)
1467    G2frame.Bind(wx.EVT_MENU, OnDistRecalib, id=G2G.wxID_IMDISTRECALIB)
1468    G2frame.Bind(wx.EVT_MENU, OnClearCalib, id=G2G.wxID_IMCLEARCALIB)
1469#    if data.get('calibrant'):
1470#        mode = True
1471#    else:
1472#        mode = False
1473#    G2frame.Enable(id=G2G.wxID_IMRECALIBRATE,enable=mode)
1474#    G2frame.Enable(id=G2G.wxID_IMCALIBRATE,enable=mode)
1475#    G2frame.Enable(id=G2G.wxID_IMRECALIBALL,enable=mode)
1476    G2frame.Bind(wx.EVT_MENU, OnIntegrate, id=G2G.wxID_IMINTEGRATE)
1477    G2frame.Bind(wx.EVT_MENU, OnIntegrateAll, id=G2G.wxID_INTEGRATEALL)
1478    G2frame.Bind(wx.EVT_MENU, OnCopyControls, id=G2G.wxID_IMCOPYCONTROLS)
1479    G2frame.Bind(wx.EVT_MENU, OnCopySelected, id=G2G.wxID_IMCOPYSELECTED)
1480    G2frame.Bind(wx.EVT_MENU, OnSaveControls, id=G2G.wxID_IMSAVECONTROLS)
1481    G2frame.Bind(wx.EVT_MENU, OnSaveMultiControls, id=G2G.wxID_SAVESELECTEDCONTROLS)
1482    G2frame.Bind(wx.EVT_MENU, OnLoadControls, id=G2G.wxID_IMLOADCONTROLS)
1483    G2frame.Bind(wx.EVT_MENU, OnLoadMultiControls, id=G2G.wxID_LOADELECTEDCONTROLS)
1484    G2frame.Bind(wx.EVT_MENU, OnTransferAngles, id=G2G.wxID_IMXFERCONTROLS)
1485    G2frame.Bind(wx.EVT_MENU, OnResetDist, id=G2G.wxID_IMRESETDIST)
1486    def OnDestroy(event):
1487        G2frame.autoIntFrame = None
1488    def OnAutoInt(event):
1489        if G2frame.autoIntFrame: # ensure only one open at a time
1490            G2frame.autoIntFrame.Raise()
1491            return
1492        PollTime = GSASIIpath.GetConfigValue('Autoint_PollTime',30.)
1493        G2frame.autoIntFrame = AutoIntFrame(G2frame,PollTime=PollTime)
1494        # debug code to reload code for window on each use
1495        #import GSASIIimgGUI
1496        #reload(GSASIIimgGUI)
1497        #G2frame.autoIntFrame = GSASIIimgGUI.AutoIntFrame(G2frame,PollTime=PollTime)
1498
1499        G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
1500    G2frame.Bind(wx.EVT_MENU, OnAutoInt, id=G2G.wxID_IMAUTOINTEG)
1501    def OnIntPDFtool(event):
1502        import subprocess
1503        ex = sys.executable
1504        if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1505            if os.path.exists(ex+'w'): ex += 'w'
1506        if G2frame.GSASprojectfile:
1507            project = os.path.abspath(G2frame.GSASprojectfile)
1508        else:
1509            project = ''
1510        subprocess.Popen([ex,os.path.join(GSASIIpath.path2GSAS2,'GSASIIIntPDFtool.py'),project])
1511    G2frame.Bind(wx.EVT_MENU, OnIntPDFtool, id=G2G.wxID_IMINTEGPDFTOOL)
1512
1513    mainSizer = G2frame.dataWindow.GetSizer()
1514    mainSizer.Add((5,10),0)   
1515    mainSizer.Add(ComboSizer(),0,wx.ALIGN_LEFT)
1516    mainSizer.Add((5,5),0)
1517    Range = data['range'] # allows code to be same in Masks
1518    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1519    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1520   
1521    mainSizer.Add((5,5),0)
1522    DataSizer = wx.FlexGridSizer(0,2,5,0)
1523    DataSizer.Add(CalibCoeffSizer(),0)
1524    DataSizer.Add(IntegrateSizer(),0)       
1525    mainSizer.Add(DataSizer,0)
1526    mainSizer.Add((5,5),0)           
1527    mainSizer.Add(BackSizer(),0)
1528    mainSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Calibration controls:'),0,WACV)
1529    mainSizer.Add((5,5),0)
1530    mainSizer.Add(CalibSizer(),0,WACV)
1531    mainSizer.Add((5,5),0)
1532    mainSizer.Add(GonioSizer(),0,WACV)   
1533    G2frame.dataWindow.SetDataSize()
1534   
1535################################################################################
1536##### Masks
1537################################################################################
1538def CleanupMasks(data):
1539    '''If a mask creation is not completed, an empty mask entry is created in the
1540    masks array. This cleans them out. It is called when the masks page is first loaded
1541    and before saving them or after reading them in. This should also probably be done
1542    before they are used for integration.
1543    '''
1544    for key in ['Points','Rings','Arcs','Polygons',]:
1545        data[key] = data.get(key,[])
1546        l1 = len(data[key])
1547        data[key] = [i for i in data[key] if len(i)]
1548        l2 = len(data[key])
1549        if GSASIIpath.GetConfigValue('debug') and l1 != l2:
1550            print ('DBG_Mask Cleanup: %s was %d entries, now %d'%(key,l1,l2))
1551   
1552def UpdateMasks(G2frame,data):
1553    '''Shows and handles the controls on the "Masks" data tree entry
1554    '''
1555   
1556    def OnTextMsg(event):
1557        Obj = event.GetEventObject()
1558        Obj.SetToolTipString('Drag this mask on 2D Powder Image with mouse to change ')
1559
1560    def Replot(*args,**kwargs):
1561        wx.CallAfter(G2plt.PlotExposedImage,G2frame)
1562
1563    def newReplot(*args,**kwargs):
1564        wx.CallAfter(G2plt.PlotExposedImage,G2frame,newPlot=True)
1565
1566    def onDeleteMask(event):
1567        Obj = event.GetEventObject()
1568        typ = Obj.locationcode.split('+')[1]
1569        num = int(Obj.locationcode.split('+')[2])
1570        del(data[typ][num])
1571        wx.CallAfter(UpdateMasks,G2frame,data)
1572        G2plt.PlotExposedImage(G2frame,event=event)
1573       
1574    def OnSpotChange(event):
1575        r,c = event.GetRow(),event.GetCol()
1576        if c == 2:
1577            del Spots[r]
1578            SpotTable.DeleteRow(r)
1579        else:
1580            Spots[r][2] = float(SpotGrid.GetCellValue(r,c))
1581        SpotGrid.ForceRefresh()
1582        wx.CallAfter(UpdateMasks,G2frame,data)
1583        G2plt.PlotExposedImage(G2frame,event=event)
1584        event.Skip()
1585
1586    def onDeleteFrame(event):
1587        data['Frames'] = []
1588        wx.CallAfter(UpdateMasks,G2frame,data)
1589        G2plt.PlotExposedImage(G2frame,event=event)
1590
1591    def OnCopyMask(event):
1592        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1593        if len(Names) == 1:
1594            G2frame.ErrorDialog('Nothing to copy masks to','There must be more than one "IMG" pattern')
1595            return
1596        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1597        Names.pop(Names.index(Source))
1598        Data = copy.deepcopy(data)
1599        Thresh = Data.pop('Thresholds')     # & remove it as well
1600        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy mask data','Copy masks from '+Source+' to:',Names)
1601        try:
1602            if dlg.ShowModal() == wx.ID_OK:
1603                items = dlg.GetSelections()
1604                for item in items:
1605                    name = Names[item]
1606                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1607                    MId = G2gd.GetGPXtreeItemId(G2frame,Id,'Masks')
1608                    Mask = G2frame.GPXtree.GetItemPyData(MId)
1609                    Mask.update(copy.deepcopy(Data))
1610                    Mask['Thresholds'][1][0] = Thresh[1][0]  #copy only lower threshold
1611                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Masks'),Mask)
1612        finally:
1613            dlg.Destroy()
1614
1615    def OnSaveMask(event):
1616        CleanupMasks(data)
1617        pth = G2G.GetExportPath(G2frame)
1618        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1619            'image mask files (*.immask)|*.immask',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1620        try:
1621            if dlg.ShowModal() == wx.ID_OK:
1622                filename = dlg.GetPath()
1623                filename = os.path.splitext(filename)[0]+'.immask'
1624                File = open(filename,'w')
1625                keys = ['Points','Rings','Arcs','Polygons','Xlines','Ylines','Frames','Thresholds']
1626                for key in keys:
1627                    File.write(key+':'+str(data[key])+'\n')
1628                File.close()
1629        finally:
1630            dlg.Destroy()
1631       
1632    def OnLoadMask(event):
1633        if event.Id == G2G.wxID_MASKLOADNOT:
1634            ignoreThreshold = True
1635        else:
1636            ignoreThreshold = False
1637        pth = G2G.GetImportPath(G2frame)
1638        if not pth: pth = '.'
1639        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1640            'image mask files (*.immask)|*.immask',wx.FD_OPEN)
1641        try:
1642            if dlg.ShowModal() == wx.ID_OK:
1643                filename = dlg.GetPath()
1644               
1645                G2fil.readMasks(filename,data,ignoreThreshold)
1646                wx.CallAfter(UpdateMasks,G2frame,data)
1647                G2plt.PlotExposedImage(G2frame,event=event)               
1648        finally:
1649            dlg.Destroy()
1650           
1651    def OnFindSpotMask(event):
1652        'Do auto search for spot masks'
1653        if wx.MessageDialog(G2frame.dataWindow,'NB: This is slow (5-10min)',
1654            'Spot mask search', wx.OK|wx.CANCEL).ShowModal() == wx.ID_OK:
1655            Controls = G2frame.GPXtree.GetItemPyData( 
1656                G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls'))
1657            wave = Controls['wavelength']
1658            LUtth = np.array(Controls['IOtth'])
1659            dsp0 = wave/(2.0*sind(LUtth[0]/2.0))
1660            dsp1 = wave/(2.0*sind(LUtth[1]/2.0))
1661            x0 = G2img.GetDetectorXY(dsp0,0.0,Controls)[0]
1662            x1 = G2img.GetDetectorXY(dsp1,0.0,Controls)[0]   
1663            nChans = int(1000*(x1-x0)/Controls['pixelSize'][0])//2
1664            dlg = wx.ProgressDialog("Auto spot masking for %d bins"%nChans,"Processed 2-theta rings = ",nChans+3,
1665                style = wx.PD_ELAPSED_TIME|wx.PD_CAN_ABORT)
1666            time0 = time.time()
1667            data['SpotMask']['spotMask'] = G2img.AutoSpotMask(G2frame.ImageZ,data,Controls,nChans,dlg)
1668            print(' Spot masksearch time: %.2f m'%((time.time()-time0)/60.))
1669            wx.CallAfter(UpdateMasks,G2frame,data)
1670            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
1671        else:
1672            print(' Spot mask search not done')
1673           
1674    def OnAutoFindSpotMask(event):
1675        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1676        dlg = G2G.G2MultiChoiceDialog(G2frame,'Automatic spot mask search','Select images to spot mask:',Names)
1677        try:
1678            if dlg.ShowModal() == wx.ID_OK:
1679                items = dlg.GetSelections()
1680                G2frame.EnablePlot = False
1681                for item in items:
1682                    name = Names[item]
1683                    print ('Spot search for'+name)
1684                    G2frame.Image = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1685                    Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls'))
1686                    Mask = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Masks'))
1687                    G2frame.ImageZ = GetImageZ(G2frame,Controls)
1688                    wave = Controls['wavelength']
1689                    LUtth = np.array(Controls['IOtth'])
1690                    dsp0 = wave/(2.0*sind(LUtth[0]/2.0))
1691                    dsp1 = wave/(2.0*sind(LUtth[1]/2.0))
1692                    x0 = G2img.GetDetectorXY(dsp0,0.0,Controls)[0]
1693                    x1 = G2img.GetDetectorXY(dsp1,0.0,Controls)[0]   
1694                    nChans = int(1000*(x1-x0)/Controls['pixelSize'][0])//2
1695                    dlg = wx.ProgressDialog("Spot mask search for %d bins"%nChans,"Processed 2-theta rings = ",nChans+3,
1696                        style = wx.PD_ELAPSED_TIME|wx.PD_CAN_ABORT)
1697                    time0 = time.time()
1698                    Mask['SpotMask']['spotMask'] = G2img.AutoSpotMask(G2frame.ImageZ,Mask,Controls,nChans,dlg)
1699                    print(' Spot mask search time: %.2f m'%((time.time()-time0)/60.))
1700        finally:
1701            dlg.Destroy()
1702        G2plt.PlotExposedImage(G2frame,event=None)
1703       
1704
1705    def OnDeleteSpotMask(event):
1706        data['Points'] = []
1707        wx.CallAfter(UpdateMasks,G2frame,data)
1708        G2plt.PlotExposedImage(G2frame,newPlot=True,event=event)         
1709           
1710    def ToggleSpotMaskMode(event):
1711        G2plt.ToggleMultiSpotMask(G2frame)
1712       
1713    def OnNewArcMask(event):
1714        'Start a new arc mask'
1715        G2frame.MaskKey = 'a'
1716        G2plt.OnStartMask(G2frame)
1717       
1718    def OnNewRingMask(event):
1719        'Start a new ring mask'
1720        G2frame.MaskKey = 'r'
1721        G2plt.OnStartMask(G2frame)
1722       
1723    def OnNewXlineMask(event):
1724        'Start a new x-line mask'
1725        print('x')
1726        G2frame.MaskKey = 'x'
1727        G2plt.OnStartMask(G2frame)
1728       
1729    def OnNewYlineMask(event):
1730        'Start a new y-line mask'
1731        G2frame.MaskKey = 'y'
1732        G2plt.OnStartMask(G2frame)
1733       
1734    def OnNewPolyMask(event):
1735        'Start a new polygon mask'
1736        G2frame.MaskKey = 'p'
1737        G2plt.OnStartMask(G2frame)
1738       
1739    def OnNewFrameMask(event):
1740        'Start a new Frame mask'
1741        G2frame.MaskKey = 'f'
1742        G2plt.OnStartMask(G2frame)
1743       
1744    def MaxSizer():
1745        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1746        for the image, as well as callback routines.
1747        '''
1748        def OnNewVal(invalid,value,tc):
1749            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1750            the value into the data['range'] nested list).
1751            This adjusts the slider positions to match the current values
1752            '''
1753            scaleSel.SetSelection(len(scaleChoices)-1)
1754            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1755            if r11 != Range[1][1]:
1756                Range[1][1] = r11
1757                maxVal.SetValue(int(Range[1][1]))
1758            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1759            if r10 != Range[1][0]:
1760                Range[1][0] = r10
1761                minVal.SetValue(int(Range[1][0]))
1762            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1763            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1764            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1765            maxSel.SetValue(sv1)
1766            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1767            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1768            minSel.SetValue(sv0)
1769            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1770            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1771            if mplOld:
1772                Page.canvas.draw()
1773            else:
1774                Page.canvas.draw_idle()
1775           
1776        G2frame.prevMaxValue = None   
1777        def OnMaxSlider(event):
1778            val = maxSel.GetValue()
1779            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1780            scaleSel.SetSelection(len(scaleChoices)-1)
1781            G2frame.prevMaxValue = val
1782            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1783            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1784            maxVal.SetValue(int(0.5+Range[1][1]))
1785            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1786            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1787            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1788            minSel.SetValue(sv0)
1789            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1790            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1791            if mplOld:
1792                Page.canvas.draw()
1793            else:
1794                Page.canvas.draw_idle()
1795           
1796        G2frame.prevMinValue = None   
1797        def OnMinSlider(event):
1798            val = minSel.GetValue()
1799            scaleSel.SetSelection(len(scaleChoices)-1)
1800            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1801            G2frame.prevMinValue = val
1802            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1803            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1804            minVal.SetValue(int(Range[1][0]))
1805            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1806            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1807            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1808            maxSel.SetValue(sv1)
1809            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1810            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1811            if mplOld:
1812                Page.canvas.draw()
1813            else:
1814                Page.canvas.draw_idle()
1815           
1816        def OnAutoSet(event):
1817            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1818            for the image so that 95% (etc.) of pixels are inside the color map limits.
1819            An equal number of pixels are dropped at the minimum and maximum levels.
1820            '''
1821            try:
1822                val = int(event.GetEventObject().GetStringSelection()[:-1])
1823                margin = (100-val)/2.
1824            except:
1825                margin = 0
1826                event.GetEventObject().SetSelection(0)
1827            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1828            if margin == 0:
1829                Range[1] = list(Range[0])
1830            else:
1831                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1832                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1833            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1834            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1835            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1836            maxSel.SetValue(sv1)
1837            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1838            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1839            minSel.SetValue(sv0)
1840            minVal.SetValue(int(Range[1][0]))
1841            maxVal.SetValue(int(Range[1][1]))
1842            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1843            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1844            if mplOld:
1845                Page.canvas.draw()
1846            else:
1847                Page.canvas.draw_idle()
1848
1849        mplv = mpl.__version__.split('.')
1850        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1851        # Plot color scaling uses limits as below:
1852        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1853        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1854       
1855        maxSizer = wx.BoxSizer(wx.VERTICAL)
1856        slideSizer = wx.FlexGridSizer(2,3,5,5)
1857        slideSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Max intensity'),0,WACV)
1858        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1859        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1860        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1861        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1862        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1863        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1864        maxSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv1)
1865        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],1,min=Range[0][0]+1,
1866            max=Range[0][1],OnLeave=OnNewVal)
1867        slideSizer.Add(maxVal,0,WACV)
1868        slideSizer.Add(maxSel,flag=wx.EXPAND|wx.ALL)
1869        slideSizer.AddGrowableCol(2)
1870        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1871        slideSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min intensity'),0,WACV)
1872        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1873        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1874        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1875        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1876        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1877        minVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],0,
1878            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1879        slideSizer.Add(minVal,0,WACV)
1880        minSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv0)
1881        slideSizer.Add(minSel,flag=wx.EXPAND|wx.ALL)
1882        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1883        maxSizer.Add(slideSizer,flag=wx.EXPAND|wx.ALL)
1884        autoSizer = wx.BoxSizer(wx.HORIZONTAL)
1885        autoSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Auto scaler '),0,WACV)
1886        scaleChoices = ("100%","99%","95%","90%","80%","?")
1887        scaleSel = wx.Choice(G2frame.dataWindow,choices=scaleChoices,size=(-1,-1))
1888        if (Range[1][0] == Range[0][0] and
1889            Range[1][1] == Range[0][1]):
1890            scaleSel.SetSelection(0)
1891        else:
1892            scaleSel.SetSelection(len(scaleChoices)-1)
1893        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1894        autoSizer.Add(scaleSel,0,WACV)
1895        maxSizer.Add(autoSizer)
1896        return maxSizer
1897   
1898    def OnDelBtn(event):
1899        data['SpotMask'] = {'esdMul':3.,'spotMask':None}
1900        wx.CallAfter(UpdateMasks,G2frame,data)
1901               
1902   
1903    G2frame.dataWindow.ClearData()
1904    startScroll = None
1905    if G2frame.dataWindow:
1906        startScroll = G2frame.dataWindow.GetScrollPos(wx.VERTICAL) # save scroll position
1907    else:
1908        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1909    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.MaskMenu)
1910    G2frame.Bind(wx.EVT_MENU, OnCopyMask, id=G2G.wxID_MASKCOPY)
1911    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOAD)
1912    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOADNOT)
1913    G2frame.Bind(wx.EVT_MENU, OnSaveMask, id=G2G.wxID_MASKSAVE)
1914    G2frame.Bind(wx.EVT_MENU, OnFindSpotMask, id=G2G.wxID_FINDSPOTS)
1915    G2frame.Bind(wx.EVT_MENU, OnAutoFindSpotMask, id=G2G.wxID_AUTOFINDSPOTS)
1916    G2frame.Bind(wx.EVT_MENU, OnDeleteSpotMask, id=G2G.wxID_DELETESPOTS)
1917    G2frame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2G.wxID_NEWMASKSPOT)
1918    G2frame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2G.wxID_NEWMASKARC)
1919    G2frame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2G.wxID_NEWMASKRING)
1920    G2frame.Bind(wx.EVT_MENU, OnNewXlineMask, id=G2G.wxID_NEWMASKXLINE)
1921    G2frame.Bind(wx.EVT_MENU, OnNewYlineMask, id=G2G.wxID_NEWMASKYLINE)
1922    G2frame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2G.wxID_NEWMASKPOLY)
1923    G2frame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2G.wxID_NEWMASKFRAME)
1924    if G2frame.MaskKey == 'f':
1925        G2frame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon',1)
1926    elif G2frame.MaskKey == 'p':
1927        G2frame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon',1)
1928    elif G2frame.MaskKey == 'a':
1929        G2frame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location',1)
1930    elif G2frame.MaskKey == 'r':
1931        G2frame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location',1)
1932    elif G2frame.MaskKey == 'x':
1933        G2frame.GetStatusBar().SetStatusText('X-line mask active - LB pick x line of pixels',1)
1934    elif G2frame.MaskKey == 'y':
1935        G2frame.GetStatusBar().SetStatusText('Y-line mask active - LB pick y line of pixels',1)
1936    else:
1937        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)
1938    mainSizer = G2frame.dataWindow.GetSizer()
1939    mainSizer.Add((5,10),0)
1940
1941    thresh = data['Thresholds']         #min/max intensity range
1942    Spots = data['Points']               #x,y,radius in mm
1943    Rings = data['Rings']               #radius, thickness
1944    Polygons = data['Polygons']         #3+ x,y pairs
1945    if 'Xlines' not in data:            #single rows/columns of bad pixels
1946        data['Xlines'] = []
1947        data['Ylines'] = []
1948    Xlines = data['Xlines']
1949    Ylines = data['Ylines']
1950    # not a good place for patch -- this not always called
1951    if 'Frames' not in data:
1952        data['Frames'] = []
1953    if 'SpotMask' not in data:
1954        data['SpotMask'] = {'esdMul':3.,'spotMask':None}
1955    frame = data['Frames']             #3+ x,y pairs
1956    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1957
1958    ######################################################################
1959    CId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')
1960    controlData = G2frame.GPXtree.GetItemPyData(CId)
1961    Range = controlData['range']
1962    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1963    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1964
1965    littleSizer = wx.FlexGridSizer(0,3,0,5)
1966    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper limits '),0,WACV)
1967    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][0]),style=wx.TE_READONLY)
1968    littleSizer.Add(Text,0,WACV)
1969    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1970    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][1]),style=wx.TE_READONLY)
1971    littleSizer.Add(Text,0,WACV)
1972    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1973    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper thresholds '),0,WACV)
1974    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=0,
1975        min=thresh[0][0],OnLeave=newReplot,typeHint=int)
1976    littleSizer.Add(lowerThreshold,0,WACV)
1977    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=1,
1978        max=thresh[0][1],OnLeave=newReplot,typeHint=int)
1979    littleSizer.Add(upperThreshold,0,WACV)
1980    mainSizer.Add(littleSizer,0,)
1981    spotSizer = wx.BoxSizer(wx.HORIZONTAL)
1982    data['SpotMask']['esdMul'] = float(data['SpotMask']['esdMul'])
1983    spotSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Select n*sigma spot rejection (n=1-10): '),0,WACV)
1984    spotSizer.Add(G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=data['SpotMask'],
1985        key='esdMul',min=1.,max=10.,size=(40,25)),0,WACV)
1986    numPix = 0
1987    if data['SpotMask']['spotMask'] is not None:
1988        numPix = np.count_nonzero(data['SpotMask']['spotMask'])
1989    spotSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Number of pixels in spot mask: %d  '%numPix),0,WACV)
1990    delbtn = wx.Button(G2frame.dataWindow,label='Clear spot mask')
1991    delbtn.Bind(wx.EVT_BUTTON,OnDelBtn)
1992    spotSizer.Add(delbtn,0,WACV)
1993    mainSizer.Add(spotSizer,0,WACV)
1994    if len(Spots):
1995        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Spot masks(on plot LB drag to move, shift-LB drag to resize, RB to delete)')
1996        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1997        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1998        colTypes = [wg.GRID_VALUE_STRING,wg.GRID_VALUE_FLOAT+':10,2',wg.GRID_VALUE_BOOL]
1999        colIds = ['position, mm','diameter, mm','Delete?']
2000        rowIds = [str(i) for i in range(len(Spots))]
2001        table = [['%.2f,%.2f'%(item[0],item[1]),item[2],False] for item in Spots]
2002        SpotTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
2003        SpotGrid = G2G.GSGrid(G2frame.dataWindow)
2004        SpotGrid.SetTable(SpotTable,True)
2005        SpotGrid.AutoSizeColumns(True)
2006        SpotGrid.SetColSize(1,80)
2007        for r in range(len(Spots)):
2008            SpotGrid.SetCellStyle(r,0,VERY_LIGHT_GREY,True)
2009        if 'phoenix' in wx.version():
2010            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnSpotChange)
2011        else:
2012            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnSpotChange)
2013        mainSizer.Add(SpotGrid,0,)
2014    if Rings:
2015        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Ring masks')
2016        lbl.SetBackgroundColour(wx.Colour(200,200,210))
2017        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
2018        littleSizer = wx.FlexGridSizer(0,3,0,5)
2019        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
2020        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
2021        littleSizer.Add((5,0),0)
2022        for i in range(len(Rings)):
2023            if Rings[i]:
2024                ringText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (Rings[i][0])),
2025                    style=wx.TE_READONLY)
2026                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
2027                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
2028                littleSizer.Add(ringText,0,WACV)
2029                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Rings[i],key=1,
2030                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
2031                littleSizer.Add(ringThick,0,WACV)
2032                ringDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
2033                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
2034                littleSizer.Add(ringDelete,0,WACV)
2035        mainSizer.Add(littleSizer,0,)
2036    if Arcs:
2037        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Arc masks')
2038        lbl.SetBackgroundColour(wx.Colour(200,200,210))
2039        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
2040        littleSizer = wx.FlexGridSizer(0,4,0,5)
2041        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
2042        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' azimuth, deg'),0,WACV)
2043        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
2044        littleSizer.Add((5,0),0)
2045        for i in range(len(Arcs)):
2046            if Arcs[i]:
2047                tth,azimuth,thick = Arcs[i]
2048                arcText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (tth)),
2049                    style=wx.TE_READONLY)
2050                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
2051                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
2052                littleSizer.Add(arcText,0,WACV)
2053                azmText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%d,%d" % (azimuth[0],azimuth[1])),
2054                    style=wx.TE_READONLY)
2055                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
2056                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
2057                littleSizer.Add(azmText,0,WACV)
2058                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Arcs[i],key=2,
2059                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
2060                littleSizer.Add(arcThick,0,WACV)
2061                arcDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
2062                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
2063                littleSizer.Add(arcDelete,0,WACV)
2064        mainSizer.Add(littleSizer,0,)
2065       
2066    if Xlines:
2067        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' X line masks')
2068        lbl.SetBackgroundColour(wx.Colour(200,200,210))
2069        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
2070        littleSizer = wx.FlexGridSizer(0,2,0,5)
2071        for i in range(len(Xlines)):
2072            if Xlines[i]:
2073                littleSizer.Add(wx.StaticText(G2frame.dataWindow,label='at Y-pixel: %d'%(Xlines[i])),0,WACV)
2074                xlineDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
2075                    locationcode='Delete+Xlines+'+str(i),handler=onDeleteMask)
2076                littleSizer.Add(xlineDelete,0,WACV)
2077        mainSizer.Add(littleSizer,0,)
2078       
2079    if Ylines:
2080        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Y line masks')
2081        lbl.SetBackgroundColour(wx.Colour(200,200,210))
2082        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
2083        littleSizer = wx.FlexGridSizer(0,2,0,5)
2084        for i in range(len(Ylines)):
2085            if Ylines[i]:
2086                littleSizer.Add(wx.StaticText(G2frame.dataWindow,label='at X-pixel: %d'%(Ylines[i])),0,WACV)
2087                ylineDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
2088                    locationcode='Delete+Ylines+'+str(i),handler=onDeleteMask)
2089                littleSizer.Add(ylineDelete,0,WACV)
2090        mainSizer.Add(littleSizer,0,)
2091       
2092    if Polygons:
2093        lbl = wx.StaticText(parent=G2frame.dataWindow,
2094            label=' Polygon masks (on plot LB vertex drag to move, RB vertex drag to insert)')
2095        lbl.SetBackgroundColour(wx.Colour(200,200,210))
2096        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
2097        littleSizer = wx.FlexGridSizer(0,2,0,5)
2098        for i in range(len(Polygons)):
2099            if Polygons[i]:
2100                polyList = []
2101                for x,y in Polygons[i]:
2102                    polyList.append("%.2f, %.2f"%(x,y))
2103                polyText = wx.ComboBox(G2frame.dataWindow,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
2104                littleSizer.Add(polyText,0,WACV)
2105                polyDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
2106                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
2107                littleSizer.Add(polyDelete,0,WACV)
2108        mainSizer.Add(littleSizer,0,)
2109    if frame:
2110        lbl = wx.StaticText(parent=G2frame.dataWindow,
2111            label=' Frame mask (on plot LB vertex drag to move, RB vertex drag to insert)')
2112        lbl.SetBackgroundColour(wx.Colour(200,200,210))
2113        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
2114        littleSizer = wx.FlexGridSizer(0,2,0,5)
2115        frameList = []
2116        for x,y in frame:
2117            frameList.append("%.2f, %.2f"%(x,y))
2118        frameText = wx.ComboBox(G2frame.dataWindow,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
2119        littleSizer.Add(frameText,0,WACV)
2120        frameDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
2121            locationcode='Delete+Frame',handler=onDeleteFrame)
2122        littleSizer.Add(frameDelete,0,WACV)
2123        mainSizer.Add(littleSizer,0,)
2124    G2frame.dataWindow.SetDataSize()
2125    if startScroll: # reset scroll to saved position
2126        G2frame.dataWindow.Scroll(0,startScroll) # set to saved scroll position
2127        wx.Yield()
2128
2129################################################################################
2130##### Stress/Strain
2131################################################################################
2132
2133def UpdateStressStrain(G2frame,data):
2134    '''Shows and handles the controls on the "Stress/Strain"
2135    data tree entry
2136    '''
2137   
2138    def OnAppendDzero(event):
2139        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
2140            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0],'fixDset':False})
2141        UpdateStressStrain(G2frame,data)
2142       
2143    def OnUpdateDzero(event):
2144        for item in data['d-zero']:
2145            if item['Dcalc']:   #skip unrefined ones
2146                item['Dset'] = item['Dcalc']
2147        UpdateStressStrain(G2frame,data)
2148           
2149    def OnCopyStrSta(event):
2150        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
2151        if len(Names) == 1:
2152            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
2153            return
2154        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
2155        Names.pop(Names.index(Source))
2156        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
2157        try:
2158            if dlg.ShowModal() == wx.ID_OK:
2159                items = dlg.GetSelections()
2160                for item in items:
2161                    name = Names[item]
2162                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
2163                    CId = G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain')
2164                    oldData = G2frame.GPXtree.GetItemPyData(CId)
2165                    load = oldData.get('Sample load',0.0)
2166                    Data = copy.deepcopy(data)
2167                    Data['Sample load'] = load
2168                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'),Data)
2169        finally:
2170            dlg.Destroy()
2171            G2frame.GPXtree.SelectItem(G2frame.PickId)
2172
2173    def OnLoadStrSta(event):
2174        pth = G2G.GetImportPath(G2frame)
2175        if not pth: pth = '.'
2176        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
2177            'image control files (*.strsta)|*.strsta',wx.FD_OPEN)
2178        try:
2179            if dlg.ShowModal() == wx.ID_OK:
2180                filename = dlg.GetPath()
2181                File = open(filename,'r')
2182                S = File.read()
2183                data = eval(S)
2184                Controls = G2frame.GPXtree.GetItemPyData(
2185                    G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2186                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
2187                UpdateStressStrain(G2frame,data)
2188                G2plt.PlotExposedImage(G2frame,event=event)
2189                G2plt.PlotStrain(G2frame,data,newPlot=True)
2190                File.close()
2191        finally:
2192            dlg.Destroy()
2193
2194    def OnSaveStrSta(event):
2195        pth = G2G.GetExportPath(G2frame)
2196        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
2197            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2198        try:
2199            if dlg.ShowModal() == wx.ID_OK:
2200                filename = dlg.GetPath()
2201                File = open(filename,'w')
2202                keys = ['Type','Sample phi','Sample z','Sample load']
2203                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat','fixDset']
2204                File.write('{\n\t')
2205                for key in keys:
2206                    if key in 'Type':
2207                        File.write("'"+key+"':'"+data[key]+"',")
2208                    else:
2209                        File.write("'"+key+"':"+str(data[key])+',')
2210                File.write('\n\t'+"'d-zero':[\n")
2211                for data2 in data['d-zero']:
2212                    File.write('\t\t{')
2213                    for key in keys2:
2214                        File.write("'"+key+"':"+str(data2[key])+',')
2215                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
2216                File.write('\t]\n}')
2217                File.close()
2218        finally:
2219            dlg.Destroy()
2220           
2221    def OnStrStaSample(event):
2222        filename = ''
2223        pth = G2G.GetImportPath(G2frame)
2224        if not pth: pth = '.'
2225        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
2226            'metadata file (*.*)|*.*',wx.FD_OPEN)
2227        try:
2228            if dlg.ShowModal() == wx.ID_OK:
2229                filename = dlg.GetPath()
2230                File = open(filename,'r')
2231                S = File.readline()
2232                newItems = []
2233                itemNames = []
2234                Comments = []
2235                while S:
2236                    if S[0] == '#':
2237                        Comments.append(S)
2238                        S = File.readline()
2239                        continue
2240                    S = S.replace(',',' ').replace('\t',' ')
2241                    Stuff = S[:-1].split()
2242                    itemNames.append(Stuff[0])
2243                    newItems.append(Stuff[1:])
2244                    S = File.readline()               
2245                File.close()
2246        finally:
2247            dlg.Destroy()
2248        if not filename:
2249            G2frame.ErrorDialog('Nothing to do','No file selected')
2250            return
2251        dataDict = dict(zip(itemNames,newItems))
2252        ifany = False
2253        Names = [' ','Sample phi','Sample z','Sample load']
2254        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
2255            'Select columns',Comments,Names,np.array(newItems).T)
2256        try:
2257            if dlg.ShowModal() == wx.ID_OK:
2258                colNames,newData = dlg.GetSelection()
2259                dataDict = dict(zip(itemNames,newData.T))
2260                for item in colNames:
2261                    if item != ' ':
2262                        ifany = True
2263        finally:
2264            dlg.Destroy()
2265        if not ifany:
2266            G2frame.ErrorDialog('Nothing to do','No columns identified')
2267            return
2268        histList = []
2269        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)       
2270        while item:
2271            name = G2frame.GPXtree.GetItemText(item)
2272            if name.startswith('IMG'):
2273                histList.append(name)
2274            item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2275        colIds = {}
2276        for i,name in enumerate(colNames):
2277            if name != ' ':
2278                colIds[name] = i
2279        for hist in histList:
2280            name = hist.split()[1]  #this is file name
2281            if name in dataDict:
2282                newItems = {}
2283                for item in colIds:
2284                    newItems[item] = float(dataDict[name][colIds[item]])
2285                Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,hist)
2286                stsrData = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'))
2287                stsrData.update(newItems)       
2288        UpdateStressStrain(G2frame,data)       
2289   
2290    def OnPlotStrSta(event):
2291        Controls = G2frame.GPXtree.GetItemPyData(
2292            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2293        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
2294        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
2295        G2plt.PlotExposedImage(G2frame,event=event)
2296        G2frame.G2plotNB.Delete('Ring Intensities')
2297        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
2298            labelY='MRD',newPlot=True,Title='Ring Intensities',
2299            names=Names,lines=True)
2300       
2301    def OnSaveStrRing(event):
2302        Controls = G2frame.GPXtree.GetItemPyData(
2303            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2304        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
2305        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
2306        pth = G2G.GetExportPath(G2frame)
2307        dlg = wx.FileDialog(G2frame, 'Choose strain ring intensity file', pth, '', 
2308            'ring intensity file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2309        try:
2310            if dlg.ShowModal() == wx.ID_OK:
2311                filename = dlg.GetPath()
2312                File = open(filename,'w')
2313                for i,name in enumerate(Names):
2314                    File.write('%s%s\n'%(' Ring intensity for ',name))
2315                    File.write('%12s %12s\n'%('Azimuth','RMD'))
2316                    for item in RingInt[i].T:
2317                        File.write(' %12.3f %12.3f\n'%(item[0],item[1]))
2318                    File.write('\n')
2319                File.close()
2320        finally:
2321            dlg.Destroy()
2322               
2323               
2324    def OnFitStrSta(event):
2325        Controls = G2frame.GPXtree.GetItemPyData(
2326            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2327        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
2328        print ('Strain fitting finished')
2329        UpdateStressStrain(G2frame,data)
2330        G2plt.PlotExposedImage(G2frame,event=event)
2331        G2plt.PlotStrain(G2frame,data,newPlot=True)
2332       
2333    def OnFitAllStrSta(event):
2334        choices = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
2335        od = {'label_1':'Copy to next','value_1':False,'label_2':'Reverse order','value_2':False}
2336        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices,extraOpts=od)
2337        names = []
2338        if dlg.ShowModal() == wx.ID_OK:
2339            for sel in dlg.GetSelections():
2340                names.append(choices[sel])
2341            Id =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
2342            if Id:
2343                SeqResult = G2frame.GPXtree.GetItemPyData(Id)
2344            else:
2345                SeqResult = {}
2346                Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
2347            SeqResult.update({'SeqPseudoVars':{},'SeqParFitEqList':[]})
2348        else:
2349            dlg.Destroy()
2350            return
2351        dlg.Destroy()
2352        if not names:
2353            return
2354        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
2355            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
2356        wx.BeginBusyCursor()
2357        goodnames = []
2358        if od['value_2']:
2359            names.reverse()
2360        try:
2361            varyList = []
2362            variables = []
2363            for i,name in enumerate(names):
2364                print (' Sequential strain fit for '+name)
2365                dlg.Raise()
2366                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
2367                if not GoOn:
2368                    break
2369                sId =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
2370                G2frame.Image = sId
2371                Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Image Controls'))
2372                StaCtrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Stress/Strain'))
2373                if not len(StaCtrls['d-zero']):
2374                    continue
2375                goodnames.append(name)
2376                Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(sId)
2377                image = GetImageZ(G2frame,Controls)
2378                sig = []
2379                if i and od['value_1']:
2380                    for j,ring in enumerate(StaCtrls['d-zero']):
2381                        ring['Emat'] = copy.copy(variables[4*j:4*j+3])
2382                varyList = []
2383                variables = []
2384                #get results from previous & put in StaCtrls
2385                G2img.FitStrSta(image,StaCtrls,Controls)
2386                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
2387                parmDict = {'Sample load':StaCtrls['Sample load'],}
2388                varyNames = ['e11','e12','e22']
2389                Nvar = 5*len(StaCtrls['d-zero'])
2390                coVar = np.zeros((Nvar,Nvar))
2391                for j,item in enumerate(StaCtrls['d-zero']):
2392                    variables += item['Emat']
2393                    sig += item['Esig']
2394                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
2395                    varyList += varylist
2396                    parmDict.update(dict(zip(varylist,item['Emat'])))
2397                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
2398                    variables.append(1.e6*(item['Dcalc']/item['Dset']-1.))
2399                    varyList.append('%d;h-mstrain'%(j))
2400                    sig.append(0)
2401                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2402                    variables.append(item['Ivar'])
2403                    varyList.append('%d;Ivar'%(j))
2404                    sig.append(0)
2405                    j4 = j*5
2406                    coVar[j4:j4+3,j4:j4+3] = item['covMat']
2407                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2408                    'covMatrix':coVar,'title':name,'parmDict':parmDict}
2409            else:
2410                SeqResult['histNames'] = goodnames
2411                dlg.Destroy()
2412                print (' ***** Sequential strain refinement successful *****')
2413        finally:
2414            wx.EndBusyCursor()   
2415        SeqResult['histNames'] = choices
2416        G2frame.GPXtree.SetItemPyData(Id,SeqResult)
2417        G2frame.GPXtree.SelectItem(Id)
2418        print ('All images fitted')
2419       
2420    def SamSizer():
2421       
2422        def OnStrainType(event):
2423            data['Type'] = strType.GetValue()
2424       
2425        samSizer = wx.FlexGridSizer(0,4,0,5)
2426        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Strain type: '),0,WACV)
2427        strType = wx.ComboBox(G2frame.dataWindow,value=data['Type'],choices=['True','Conventional'],
2428            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2429        strType.SetValue(data['Type'])
2430        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2431        samSizer.Add(strType,0,WACV)
2432        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample phi: '),0,WACV)
2433        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2434        samSizer.Add(samPhi,0,WACV)
2435        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample delta-z(mm): '),0,WACV)
2436        samZ = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample z',nDig=(10,3),typeHint=float)
2437        samSizer.Add(samZ,0,WACV)
2438        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample load(MPa): '),0,WACV)
2439        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample load',
2440                nDig=[8,3],typeHint=float,)
2441        samSizer.Add(samLoad,0,WACV)
2442
2443        return samSizer
2444       
2445    def DzeroSizer():
2446               
2447        def OnStrainChange(event):
2448#            print (event)
2449            r,c = event.GetRow(),event.GetCol()
2450            if c == 0:
2451                data['d-zero'][r]['Dset'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.25),20.)
2452            elif c == 1:
2453                data['d-zero'][r]['fixDset'] = bool(StrainGrid.GetCellValue(r,c))
2454            elif c == 3:
2455                data['d-zero'][r]['cutoff'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.5),20.)
2456            elif c == 4:
2457                data['d-zero'][r]['pixLimit'] = int(StrainGrid.GetCellValue(r,c))
2458            elif c == 8:
2459                del data['d-zero'][r]
2460                StrainTable.DeleteRow(r)
2461                wx.CallAfter(UpdateStressStrain,G2frame,data)
2462            G2plt.PlotExposedImage(G2frame,event=event)
2463            G2plt.PlotStrain(G2frame,data,newPlot=True)
2464           
2465        def OnSetCol(event):
2466            c = event.GetCol()
2467            if c == 1:
2468                StrainGrid.ClearSelection()
2469                StrainGrid.SelectCol(c,True)
2470                choice = ['Y - set all','N - set none',]
2471                dlg = wx.SingleChoiceDialog(G2frame,'Select option for '+StrainGrid.GetColLabelValue(c-1),
2472                    'Strain controls',choice)
2473                dlg.CenterOnParent()
2474                if dlg.ShowModal() == wx.ID_OK:
2475                    sel = dlg.GetSelection()
2476                    if sel == 0:
2477                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=True
2478                    else:
2479                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=False
2480                wx.CallAfter(UpdateStressStrain,G2frame,data)
2481           
2482        colTypes = [wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_BOOL,wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_FLOAT+':10,1',
2483            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',]
2484        colIds = ['d-zero','Poisson\n mean?','d-zero ave','I/Ib','nPix','e11','e12','e22','Delete?','h-mustrain','Ivar']
2485        rowIds = [str(i) for i in range(len(data['d-zero']))]
2486        table = [[item['Dset'],item.get('fixDset',False),item['Dcalc'],item['cutoff'],item['pixLimit'], 
2487            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']]
2488        StrainTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
2489        StrainGrid = G2G.GSGrid(G2frame.dataWindow)
2490        StrainGrid.SetTable(StrainTable,True)
2491        StrainGrid.AutoSizeColumns(True)
2492        for r in range(len(data['d-zero'])):
2493            StrainGrid.SetCellStyle(r,2,VERY_LIGHT_GREY,True)
2494            StrainGrid.SetCellStyle(r,5,VERY_LIGHT_GREY,True)
2495            StrainGrid.SetCellStyle(r,6,VERY_LIGHT_GREY,True)
2496            StrainGrid.SetCellStyle(r,7,VERY_LIGHT_GREY,True)
2497            StrainGrid.SetCellStyle(r,9,VERY_LIGHT_GREY,True)
2498            StrainGrid.SetCellStyle(r,10,VERY_LIGHT_GREY,True)
2499        if 'phoenix' in wx.version():
2500            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnStrainChange)
2501        else:
2502            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnStrainChange)
2503        StrainGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnSetCol)
2504        return StrainGrid
2505# patches
2506    if 'Sample load' not in data:
2507        data['Sample load'] = 0.0
2508# end patches
2509   
2510    G2frame.dataWindow.ClearData()
2511    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.StrStaMenu)
2512    G2frame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2G.wxID_APPENDDZERO)
2513    G2frame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2G.wxID_UPDATEDZERO)
2514    G2frame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2G.wxID_STRSTAFIT)
2515    G2frame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2G.wxID_STRSTAPLOT) 
2516    G2frame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2G.wxID_STRRINGSAVE) 
2517    G2frame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2G.wxID_STRSTAALLFIT)
2518    G2frame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2G.wxID_STRSTACOPY)
2519    G2frame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2G.wxID_STRSTALOAD)
2520    G2frame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2G.wxID_STRSTASAVE)
2521    G2frame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2G.wxID_STRSTSAMPLE)       
2522    if G2frame.StrainKey == 'a':    #probably doesn't happen
2523        G2frame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value',1)
2524    else:
2525        G2frame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring",1)
2526       
2527    mainSizer = G2frame.dataWindow.GetSizer()
2528    mainSizer.Add((5,10),0)
2529    mainSizer.Add(SamSizer())
2530    mainSizer.Add((5,10),0)
2531    mainSizer.Add(DzeroSizer())
2532    G2frame.dataWindow.SetDataSize()
2533   
2534###########################################################################
2535# Autointegration
2536###########################################################################
2537def ReadMask(filename):
2538    'Read a mask (.immask) file'
2539    File = open(filename,'r')
2540    save = {}
2541    S = File.readline()
2542    while S:
2543        if S[0] == '#':
2544            S = File.readline()
2545            continue
2546        [key,val] = S.strip().split(':',1)
2547        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2548            save[key] = eval(val)
2549        S = File.readline()
2550    File.close()
2551    CleanupMasks(save)
2552    return save
2553
2554def ReadControls(filename):
2555    'read an image controls (.imctrl) file'
2556    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2557            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2558            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2559            'PolaVal','SampleAbs','dark image','background image']
2560    File = open(filename,'r')
2561    save = {}
2562    S = File.readline()
2563    while S:
2564        if S[0] == '#':
2565            S = File.readline()
2566            continue
2567        [key,val] = S.strip().split(':',1)
2568        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2569            save[key] = val
2570        elif key in ['rotation']:
2571            save[key] = float(val)
2572        elif key in ['center',]:
2573            if ',' in val:
2574                save[key] = eval(val)
2575            else:
2576                vals = val.strip('[] ').split()
2577                save[key] = [float(vals[0]),float(vals[1])] 
2578        elif key in cntlList:
2579            save[key] = eval(val)
2580        S = File.readline()
2581    File.close()
2582    return save
2583
2584def Read_imctrl(imctrl_file):
2585    '''Read an image control file and record control parms into a dict, with some simple
2586    type conversions
2587    '''
2588    save = {'filename':imctrl_file}
2589    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2590    if os.path.exists(immask_file):
2591        save['maskfile'] = immask_file
2592    else:
2593        save['maskfile'] = '(none)'
2594    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2595                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2596                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2597                        'PolaVal','SampleAbs','dark image','background image','setdist']
2598    File = open(imctrl_file,'r')
2599    fullIntegrate = False
2600    try:
2601        S = File.readline()
2602        while S:
2603            if S[0] == '#':
2604                S = File.readline()
2605                continue
2606            [key,val] = S.strip().split(':',1)
2607            if val.find(':') != -1:
2608                #print 'rejecting ',key,val
2609                S = File.readline()
2610                continue
2611            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2612                save[key] = val
2613            elif key == 'rotation':
2614                save[key] = float(val)
2615            elif key == 'fullIntegrate':
2616                fullIntegrate = eval(val)
2617            elif key == 'LRazimuth':
2618                vals = eval(val)
2619                save['LRazimuth_min'] = float(vals[0])
2620                save['LRazimuth_max'] = float(vals[1])
2621            elif key == 'IOtth':
2622                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2623            elif key == 'center':
2624                if ',' in val:
2625                    vals = eval(val)
2626                else:
2627                    vals = val.strip('[] ').split()
2628                    vals = [float(vals[0]),float(vals[1])] 
2629                save['center_x'],save['center_y'] = vals[0:2]
2630            elif key in cntlList:
2631                save[key] = eval(val)
2632            S = File.readline()
2633    finally:
2634        File.close()
2635        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,360.
2636    return save
2637   
2638class AutoIntFrame(wx.Frame):
2639    '''Creates a wx.Frame window for the Image AutoIntegration.
2640    The intent is that this will be used as a non-modal dialog window.
2641   
2642    Implements a Start button that morphs into a pause and resume button.
2643    This button starts a processing loop that is repeated every
2644    :meth:`PollTime` seconds.
2645
2646    :param wx.Frame G2frame: main GSAS-II frame
2647    :param float PollTime: frequency in seconds to repeat calling the
2648      processing loop. (Default is 30.0 seconds.)
2649    '''
2650    def __init__(self,G2frame,PollTime=30.0):
2651        def OnStart(event):
2652            '''Called when the start button is pressed. Changes button label
2653            to Pause. When Pause is pressed the label changes to Resume.
2654            When either Start or Resume is pressed, the processing loop
2655            is started. When Pause is pressed, the loop is stopped.
2656            '''
2657            # check inputs for errors before starting
2658            #err = ''
2659            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2660            #    err += '\nPlease select at least one output format\n'
2661            #if err:
2662            #    G2G.G2MessageBox(self,err)
2663            #    return
2664            self.Pause = False
2665            # change button label
2666            if self.btnstart.GetLabel() != 'Pause':
2667                self.btnstart.SetLabel('Pause')
2668                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2669                if self.timer.IsRunning(): self.timer.Stop()
2670                self.PreventReEntryTimer = False
2671                if self.StartLoop():
2672                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2673                    return
2674                self.OnTimerLoop(None) # run once immediately
2675                if not self.Pause:
2676                    # no pause, so start timer to check for new files
2677                    self.timer.Start(int(1000*PollTime),oneShot=False)
2678                    return
2679            # we will get to this point if Paused
2680            self.OnPause()
2681           
2682        def OnReset(event):
2683            '''Called when Reset button is pressed. This stops the
2684            processing loop and resets the list of integrated files so
2685            all images can be reintegrated.
2686            '''
2687            self.btnstart.SetLabel('Restart')
2688            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2689            if self.timer.IsRunning(): self.timer.Stop()
2690            self.Reset = True
2691            self.Pause = True
2692           
2693        def OnQuit(event):
2694            '''Stop the processing loop and close the Frame
2695            '''
2696            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2697            wx.CallAfter(self.Destroy)
2698           
2699        def OnBrowse(event):
2700            '''Responds when the Browse button is pressed to load a file.
2701            The routine determines which button was pressed and gets the
2702            appropriate file type and loads it into the appropriate place
2703            in the dict.
2704            '''
2705            if btn3 == event.GetEventObject():
2706                dlg = wx.DirDialog(
2707                    self, 'Select directory for output files',
2708                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2709                dlg.CenterOnParent()
2710                try:
2711                    if dlg.ShowModal() == wx.ID_OK:
2712                        self.params['outdir'] = dlg.GetPath()
2713                        fInp3.SetValue(self.params['outdir'])
2714                finally:
2715                    dlg.Destroy()
2716                return
2717            elif btn4 == event.GetEventObject():
2718                msg = ''
2719                pth = G2G.GetExportPath(G2frame)
2720                dlg = wx.FileDialog(
2721                    self, 'Select a PDF parameter file',
2722                    pth, self.params['pdfprm'], 
2723                    "PDF controls file (*.pdfprm)|*.pdfprm",
2724                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2725                dlg.CenterOnParent()
2726                try:
2727                    if dlg.ShowModal() == wx.ID_OK:
2728                        self.params['pdfprm'] = dlg.GetPath()
2729                        fInp4.SetValue(self.params['pdfprm'])
2730                        scanPDFprm()
2731                        msg = self.checkPDFprm(True)
2732                finally:
2733                    dlg.Destroy()
2734                if 'Error' in msg:
2735                    print(msg)
2736                    lbl = 'PDFPRM error'
2737                else:
2738                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2739                    lbl = 'PDFPRM information'
2740                G2G.G2MessageBox(self,msg,lbl)
2741                return
2742               
2743        def OnRadioSelect(event):
2744            '''Respond to a radiobutton selection and when in table
2745            mode, get distance-dependent parameters from user.
2746            '''
2747            self.Evaluator = None
2748            if self.useTable.GetValue():
2749                dlg = None
2750                try:
2751                    dlg = IntegParmTable(self) # create the dialog
2752                    dlg.CenterOnParent()
2753                    if dlg.ShowModal() == wx.ID_OK:
2754                        self.ImgTblParms = dlg.parms
2755                        self.IMfileList = dlg.IMfileList
2756                        self.Evaluator = DefineEvaluator(dlg)
2757                        self.params['Mode'] = 'table'
2758                        self.editTable.Enable(True)
2759                    else:
2760                        self.useActive.SetValue(True)
2761                finally:
2762                    if dlg: dlg.Destroy()
2763            elif self.useActive.GetValue():
2764                self.params['Mode'] = 'active'
2765                self.imageBase = G2frame.Image
2766                self.useActive.SetLabel("Active Image: "+
2767                        G2frame.GPXtree.GetItemText(self.imageBase))
2768                self.editTable.Enable(False)
2769            else:
2770                print('unexpected mode in OnRadioSelect')
2771
2772        def OnEditTable(event):
2773            '''Called to edit the distance-dependent parameter look-up table.
2774            Should be called only when table is defined and active.
2775            '''
2776            dlg = None
2777            try:
2778                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2779                dlg.CenterOnParent()
2780                if dlg.ShowModal() == wx.ID_OK:
2781                    self.ImgTblParms = dlg.parms
2782                    self.IMfileList = dlg.IMfileList
2783                    self.Evaluator = DefineEvaluator(dlg)
2784                    self.params['Mode'] = 'table'
2785                    self.editTable.Enable(True)
2786                else:
2787                    self.useActive.SetValue(True)
2788                    self.params['Mode'] = 'active'
2789                    self.imageBase = G2frame.Image
2790                    self.useActive.SetLabel("Active Image: "+
2791                            G2frame.GPXtree.GetItemText(self.imageBase))
2792                    self.editTable.Enable(False)
2793            finally:
2794                if dlg: dlg.Destroy()
2795               
2796        def showPDFctrls(event):
2797            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2798            sizer layout with .Show(True) before .Show(False) will work properly.
2799            '''
2800            fInp4.Enable(self.params['ComputePDF'])
2801            fInp4.Show(self.params['ComputePDF'])
2802            fInp4a.Enable(self.params['ComputePDF'])
2803            btn4.Enable(self.params['ComputePDF'])
2804            cOpt.Enable(self.params['ComputePDF'])
2805            if self.params['ComputePDF']:
2806                lbl4.SetForegroundColour("black")
2807                lbl4a.SetForegroundColour("black")
2808            else:
2809                lbl4.SetForegroundColour("gray")
2810                lbl4a.SetForegroundColour("gray")
2811                                   
2812        def scanPDFprm(**kw):
2813            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2814            fInp4._IndicateValidity()
2815           
2816        def OnAutoScale(event):
2817            self.AutoScale = autoscale.GetValue()
2818           
2819        def OnAutoScaleName(event):
2820            self.AutoScaleName = scalename.GetValue()
2821            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2822            scaleval.SetValue(self.Scale[0])
2823               
2824        ##################################################
2825        # beginning of __init__ processing
2826        ##################################################
2827        self.G2frame = G2frame
2828        self.ImgTblParms = None
2829        self.IMfileList = None
2830        self.Evaluator = None
2831        self.params = {}
2832        self.Reset = False
2833        self.Pause = False
2834        self.PreventReEntryShowMatch = False
2835        self.PreventReEntryTimer = False
2836        self.params['IMGfile'] = ''
2837        self.params['MaskFile'] = ''
2838        self.params['IgnoreMask'] = True
2839        self.fmtlist = G2IO.ExportPowderList(G2frame)
2840        self.timer = wx.Timer()
2841        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2842        self.imageBase = G2frame.Image
2843        self.params['ComputePDF'] = False
2844        self.params['pdfDmax'] = 0.0
2845        self.params['pdfprm'] = ''
2846        self.params['optPDF'] = True
2847        self.pdfControls = {}
2848        self.AutoScale = False
2849        self.Scale = [1.0,]
2850
2851        G2frame.GPXtree.GetSelection()
2852        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(self.imageBase) 
2853        self.params['readdir'],fileroot = os.path.split(imagefile)
2854        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2855        self.params['outdir'] = os.path.abspath(self.params['readdir'])
2856        Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2857            G2frame,self.imageBase, 'Comments'))
2858        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2859        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2860        self.AutoScales = {}
2861        if DefaultAutoScaleNames is not None:
2862            for comment in Comments:
2863                if '=' in comment:
2864                    name,val = comment.split('=',1) 
2865                    if name in DefaultAutoScaleNames:
2866                        try:
2867                            self.AutoScales[name] = float(val)
2868                            if name == self.AutoScaleName:
2869                                self.Scale[0] = float(val)
2870                        except ValueError:
2871                            continue
2872        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2873                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2874        self.Status = self.CreateStatusBar()
2875        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2876        mnpnl = wx.Panel(self)
2877        mnsizer = wx.BoxSizer(wx.VERTICAL)
2878        # box for integration controls & masks input
2879        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2880        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2881        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2882        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2883                            style = wx.RB_GROUP)
2884        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2885        self.useActive.SetLabel("Active Image: "+
2886                                G2frame.GPXtree.GetItemText(self.imageBase))
2887        lblsizr.Add(self.useActive,0,wx.EXPAND,1)
2888        self.useActive.SetValue(True)
2889        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2890        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2891        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2892        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2893        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2894        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2895        self.editTable.Enable(False)
2896        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2897        # bind button and deactivate be default
2898        lblsizr.Add(minisizer)
2899        mnsizer.Add(lblsizr,0,wx.EXPAND,0)
2900
2901        # file filter stuff
2902        sizer = wx.BoxSizer(wx.HORIZONTAL)
2903        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Read images from '))
2904        self.readDir = G2G.ValidatedTxtCtrl(mnpnl,self.params,'readdir',
2905                            OnLeave=self.ShowMatchingFiles,size=(200,-1))
2906        sizer.Add(self.readDir,1,wx.EXPAND,1)
2907        btn3 = wx.Button(mnpnl, wx.ID_ANY, "Browse")
2908        btn3.Bind(wx.EVT_BUTTON, self.SetSourceDir)
2909        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2910        mnsizer.Add(sizer,0,wx.EXPAND,0)
2911        # not yet implemented
2912        sizer = wx.BoxSizer(wx.HORIZONTAL)
2913        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Keep read images in tree '))
2914        self.params['keepReadImage'] = True
2915        keepImage = G2G.G2CheckBox(mnpnl,'',self.params,'keepReadImage')
2916        sizer.Add(keepImage)
2917        keepImage.Enable(False)
2918        sizer.Add((-1,-1),1,wx.EXPAND,1)
2919        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'  Image filter'))
2920        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',
2921                                        OnLeave=self.ShowMatchingFiles)
2922        sizer.Add(flterInp)
2923        mnsizer.Add(sizer,0,wx.EXPAND,0)
2924       
2925        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2926        mnsizer.Add(self.ListBox,1,wx.EXPAND,1)
2927        self.ShowMatchingFiles(self.params['filter'])
2928
2929        # box for output selections
2930        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2931        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2932        sizer = wx.BoxSizer(wx.HORIZONTAL)
2933        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2934        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2935        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2936        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2937        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2938        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2939        lblsizr.Add(sizer,0,wx.EXPAND)
2940        sizer = wx.BoxSizer(wx.HORIZONTAL)
2941        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s):'))
2942        for dfmt in self.fmtlist:
2943            sizer.Add((6,2)) # add a bit of extra space
2944            fmt = dfmt[1:]
2945            self.params[fmt] = False
2946            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2947            sizer.Add(btn)
2948        lblsizr.Add(sizer)
2949        sizer = wx.BoxSizer(wx.HORIZONTAL)
2950        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2951        self.params['SeparateDir'] = False
2952        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2953        lblsizr.Add(sizer)
2954        if self.AutoScales:
2955            sizer = wx.BoxSizer(wx.HORIZONTAL)
2956            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2957            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2958            sizer.Add(autoscale,0,WACV)
2959            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=list(self.AutoScales.keys()),
2960                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2961            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2962            sizer.Add(scalename,0,WACV)
2963            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2964            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2965            sizer.Add(scaleval,0,WACV)
2966            lblsizr.Add(sizer,0)
2967        #ToDO: Autonormalize, parm name?, scaling value?
2968        sizer = wx.BoxSizer(wx.HORIZONTAL)
2969        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2970        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2971        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2972        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2973        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2974        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2975        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2976        sizer.Add(cOpt)
2977        lblsizr.Add(sizer,0)
2978        sizer = wx.BoxSizer(wx.HORIZONTAL)
2979        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2980        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2981        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2982                                     OnLeave=scanPDFprm)
2983        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2984        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2985        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2986        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2987        lblsizr.Add(sizer,0,wx.EXPAND)
2988        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2989        # buttons on bottom
2990        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2991        sizer = wx.BoxSizer(wx.HORIZONTAL)
2992        sizer.Add((20,-1))
2993        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2994        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2995        sizer.Add(self.btnstart)
2996        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2997        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2998        sizer.Add(self.btnreset)
2999        sizer.Add((20,-1),wx.EXPAND,1)
3000        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
3001        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
3002        sizer.Add(self.btnclose)
3003        sizer.Add((20,-1))
3004        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
3005        # finish up window
3006        mnpnl.SetSizer(mnsizer)
3007        OnRadioSelect(None) # disable widgets
3008        mnsizer.Fit(self)
3009        self.CenterOnParent()
3010        self.Show()
3011        showPDFctrls(None)
3012
3013    def checkPDFprm(self,ShowContents=False):
3014        '''Read in the PDF (.pdfprm) parameter file and check for problems.
3015        If ShowContents is True, a formatted text version of some of the file
3016        contents is returned. If errors are found, the return string will contain
3017        the string "Error:" at least once.
3018        '''
3019        self.pdfControls = {}
3020        msg = ''
3021        File = None
3022        try:
3023            File = open(self.params['pdfprm'],'r')
3024            S = File.readline()
3025            while S:
3026                if '#' in S:
3027                    S = File.readline()
3028                    continue
3029                key,val = S.split(':',1)
3030                try:
3031                    self.pdfControls[key] = eval(val)
3032                except:
3033                    self.pdfControls[key] = val
3034                S = File.readline()
3035        except Exception as err:
3036            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
3037            if GSASIIpath.GetConfigValue('debug'):
3038                print('DBG_'+msg)
3039                print('DBG_'+err)
3040            self.pdfControls = {}
3041            return msg
3042        finally:
3043            if File: File.close()
3044        formula = ''
3045        for el in self.pdfControls['ElList']:
3046            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
3047            if formula: formula += ' '
3048            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
3049        if not formula:
3050            msg += 'Error: no chemical formula in file'
3051        for key in ['Sample Bkg.','Container','Container Bkg.']:
3052            if key not in self.pdfControls:
3053                if msg: msg += '\n'
3054                msg += 'Error: missing key in self.pdfControls: '+key
3055                continue
3056        if msg or not ShowContents: return msg  # stop on error
3057        msg += 'Default formula: '+formula+'\n'
3058        for key in ['Sample Bkg.','Container','Container Bkg.']:
3059            name = self.pdfControls[key]['Name']
3060            mult = self.pdfControls[key].get('Mult',0.0)
3061            if not name: continue
3062            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
3063            if not G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,name):
3064                msg += ' *** missing ***'
3065        return msg
3066   
3067    def SetSourceDir(self,event):
3068        '''Use a dialog to get a directory for image files
3069        '''
3070        dlg = wx.DirDialog(self, 'Select directory for image files',
3071                        self.params['readdir'],wx.DD_DEFAULT_STYLE)
3072        dlg.CenterOnParent()
3073        try:
3074            if dlg.ShowModal() == wx.ID_OK:
3075                self.params['readdir'] = dlg.GetPath()
3076            self.readDir.SetValue(self.params['readdir'])
3077            self.ShowMatchingFiles(None)
3078        finally:
3079            dlg.Destroy()
3080        return
3081       
3082    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
3083        '''Find and show images in the tree and the image files matching the image
3084        file directory (self.params['readdir']) and the image file filter
3085        (self.params['filter']) and add this information to the GUI list box
3086        '''
3087        G2frame = self.G2frame
3088        if invalid: return
3089        msg = ''
3090        if self.PreventReEntryShowMatch: return
3091        self.PreventReEntryShowMatch = True
3092        imageFileList = []
3093        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3094            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3095            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3096            if imagefile not in imageFileList: imageFileList.append(imagefile)
3097            if img not in G2frame.IntegratedList:
3098                if msg: msg += '\n'
3099                msg += '  ' + img
3100        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
3101        msg1 = ""
3102        try:
3103            if os.path.exists(self.params['readdir']): 
3104                imageList = sorted(
3105                    glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
3106                if not imageList:
3107                    msg1 = 'Warning: No files match search string '+os.path.join(self.params['readdir'],self.params['filter'])
3108                else:
3109                    for fil in imageList:
3110                        if fil not in imageFileList: msg1 += '\n  '+fil
3111                    if msg1:
3112                        msg += 'Files to integrate from '+os.path.join(self.params['readdir'],self.params['filter'])+msg1
3113                    else:
3114                        msg += 'No files found to read in '+self.params['readdir']
3115            else:
3116                msg += 'Warning: does not exist: '+self.params['readdir']
3117        except IndexError:
3118            msg += 'Error searching for files named '+os.path.join(self.params['readdir'],self.params['filter'])
3119        self.ListBox.Clear()
3120        self.ListBox.AppendItems(msg.split('\n'))
3121        self.PreventReEntryShowMatch = False
3122        return
3123       
3124    def OnPause(self):
3125        '''Respond to Pause, changes text on button/Status line, if needed
3126        Stops timer
3127        self.Pause should already be True
3128        '''
3129        if self.timer.IsRunning(): self.timer.Stop()
3130        if self.btnstart.GetLabel() == 'Restart':
3131            return
3132        if self.btnstart.GetLabel() != 'Resume':
3133            print('\nPausing autointegration\n')
3134            self.btnstart.SetLabel('Resume')
3135            self.Status.SetStatusText(
3136                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
3137        self.Pause = True
3138           
3139    def IntegrateImage(self,img,useTA=None,useMask=None):
3140        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
3141        are placed in G2frame.IntgOutList
3142        '''
3143        G2frame = self.G2frame
3144        imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3145        G2frame.Image = imgId
3146        G2frame.PickId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls')
3147        # do integration
3148        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3149        if self.AutoScale:
3150            Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3151                G2frame,imgId, 'Comments'))
3152            for comment in Comments:
3153                if '=' in comment:
3154                    name,val = comment.split('=',1) 
3155                    if name == self.AutoScaleName:
3156                        val = float(val)
3157                        if val > 0.:
3158                            Scale = self.Scale[0]/val
3159                        break
3160        masks = G2frame.GPXtree.GetItemPyData(
3161            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Masks'))
3162        data = G2frame.GPXtree.GetItemPyData(G2frame.PickId)
3163        # simulate a Image Controls press, since that is where the
3164        # integration is hidden
3165        UpdateImageControls(G2frame,data,masks,useTA=useTA,useMask=useMask,IntegrateOnly=True)
3166        G2frame.IntegratedList.append(img) # note this as integrated
3167        # split name and control number
3168        try:
3169            s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
3170        except AttributeError: # not sure why, but sometimes imagefile is a list here (should not be)!
3171            s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile[0])[0])[1])
3172        namepre = s[0]
3173        if len(s) > 1:
3174            namenum = s[1]
3175        else:
3176            namenum = ''
3177        for Id in G2frame.IntgOutList: # loop over newly created PWDR entry(ies)
3178            # save the created PWDR tree names so that a reset can delete them
3179            G2frame.Image = Id
3180            treename = G2frame.GPXtree.GetItemText(Id)
3181            G2frame.AutointPWDRnames.append(treename)
3182            # write out the images in the selected formats
3183            Sdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Sample Parameters'))
3184            if self.AutoScale:
3185                print ('Rescale by %.4f'%(Scale))
3186                y,w = G2frame.GPXtree.GetItemPyData(Id)[1][1:3]
3187                y *= Scale
3188                w /= Scale**2
3189            # determine the name for the current file
3190            fileroot = namepre
3191            if len(G2frame.IntgOutList) > 1:
3192                fileroot += "_AZM"
3193                if 'Azimuth' in Sdata:
3194                    fileroot += str(int(10*Sdata['Azimuth']))
3195                fileroot += "_" 
3196            fileroot += namenum
3197            # loop over selected formats
3198            for dfmt in self.fmtlist:
3199                if not self.params[dfmt[1:]]: continue
3200                if self.params['SeparateDir']:
3201                    subdir = dfmt[1:]
3202                else:
3203                    subdir = ''
3204                fil = os.path.join(self.params['outdir'],subdir,fileroot)
3205                G2IO.ExportPowder(G2frame,treename,fil+'.x',dfmt) # dummy extension (.x) is replaced before write)
3206               
3207    def EnableButtons(self,flag):
3208        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
3209        '''
3210        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
3211        # on windows
3212        if sys.platform != "win32":
3213            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
3214        self.btnstart.SetLabel('Pause')
3215        wx.Yield()
3216               
3217    def ResetFromTable(self,dist):
3218        '''Sets integration parameters based on values from
3219        the lookup table
3220        '''
3221        #dist = self.controlsDict['distance']
3222        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
3223        if GSASIIpath.GetConfigValue('debug'):
3224            print ('DBG_interpolated values: ',interpDict)
3225        self.ImageControls = ReadControls(imgctrl)
3226        self.ImageControls.update(interpDict)
3227        self.ImageControls['showLines'] = True
3228        self.ImageControls['ring'] = []
3229        self.ImageControls['rings'] = []
3230        self.ImageControls['ellipses'] = []
3231        self.ImageControls['setDefault'] = False
3232        for i in 'range','size','GonioAngles':
3233            if i in self.ImageControls:
3234                del self.ImageControls[i]
3235        # load copy of Image Masks
3236        if immask:
3237            self.ImageMasks = ReadMask(immask)
3238            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
3239                del self.ImageMasks['Thresholds']
3240        else:
3241            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],
3242                'SpotMask':{'esdMul':3.,'spotMask':None},}
3243       
3244    def StartLoop(self):
3245        '''Prepare to start autointegration timer loop.
3246        Save current Image params for use in future integrations
3247        also label the window so users understand what is being used
3248        '''
3249        print('\nStarting new autointegration\n')
3250        G2frame = self.G2frame
3251        # show current IMG base
3252        if self.params['Mode'] != 'table':
3253            self.useActive.SetLabel("Active Image: "+
3254                                    G2frame.GPXtree.GetItemText(self.imageBase))
3255            # load copy of Image Controls from current image and clean up
3256            # items that should not be copied
3257            self.ImageControls = copy.deepcopy(
3258                G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3259                    G2frame,self.imageBase, 'Image Controls')))
3260            self.ImageControls['showLines'] = True
3261            self.ImageControls['ring'] = []
3262            self.ImageControls['rings'] = []
3263            self.ImageControls['ellipses'] = []
3264            self.ImageControls['setDefault'] = False
3265            del self.ImageControls['range']
3266            del self.ImageControls['size']
3267            del self.ImageControls['GonioAngles']
3268            # load copy of Image Masks, keep thresholds
3269            self.ImageMasks = copy.deepcopy(
3270                G2frame.GPXtree.GetItemPyData(
3271                    G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks')))
3272            self.Thresholds = self.ImageMasks['Thresholds'][:]
3273            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
3274                del self.ImageMasks['Thresholds']   
3275        # make sure all output directories exist
3276        if self.params['SeparateDir']:
3277            for dfmt in self.fmtlist:
3278                if not self.params[dfmt[1:]]: continue
3279                dir = os.path.join(self.params['outdir'],dfmt[1:])
3280                if not os.path.exists(dir): os.makedirs(dir)
3281        else:
3282            if not os.path.exists(self.params['outdir']):
3283                os.makedirs(self.params['outdir'])
3284        if self.Reset: # special things to do after Reset has been pressed
3285            self.G2frame.IntegratedList = []
3286           
3287            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
3288                for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3289                    # update controls from master
3290                    controlsDict = G2frame.GPXtree.GetItemPyData(
3291                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Image Controls'))
3292                    controlsDict.update(self.ImageControls)
3293                    # update masks from master
3294                    ImageMasks = G2frame.GPXtree.GetItemPyData(
3295                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks'))
3296                    ImageMasks.update(self.ImageMasks)
3297            # delete all PWDR items created after last Start was pressed
3298            idlist = []
3299            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
3300            while item:
3301                itemName = G2frame.GPXtree.GetItemText(item)
3302                if itemName in G2frame.AutointPWDRnames:
3303                    idlist.append(item)
3304                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
3305            for item in idlist:
3306                G2frame.GPXtree.Delete(item)
3307            wx.Yield()
3308            self.Reset = False
3309        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
3310        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
3311        # check that AutoPDF input is OK, offer chance to use alternate PWDRs if referenced ones
3312        # are not present
3313        if self.params['ComputePDF']:
3314            msg = self.checkPDFprm()
3315            if 'Error:' in msg:
3316                print(msg)
3317                return True
3318            fileList = []
3319            Id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
3320            while Id:
3321                name = G2frame.GPXtree.GetItemText(Id)
3322                if name.startswith('PWDR '): fileList.append(name)
3323                Id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
3324            if not fileList:
3325                print(msg)
3326                print('No PWDR entries to select')
3327                return True
3328            for key in ['Sample Bkg.','Container','Container Bkg.']:
3329                name = self.pdfControls[key]['Name']
3330                if not name: continue
3331                if not G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name):
3332                    indx = G2G.ItemSelector(fileList, self, header='Select PWDR item',
3333                                    title='Select a PWDR tree item for '+key+'\n(or cancel to quit)')
3334                    if indx is None:
3335                        print('No PWDR entry selected for '+key)
3336                        return True
3337                    self.pdfControls[key]['Name'] = fileList[indx]
3338        return False
3339               
3340    def OnTimerLoop(self,event):
3341        '''A method that is called every :meth:`PollTime` seconds that is
3342        used to check for new files and process them. Integrates new images.
3343        Also optionally sets up and computes PDF.
3344        This is called only after the "Start" button is pressed (then its label reads "Pause").
3345        '''
3346        def AutoIntegrateImage(imgId,useTA=None,useMask=None):
3347            '''Integrates an image that has been read into the data tree and updates the
3348            AutoInt window.
3349            '''
3350            img = G2frame.GPXtree.GetItemText(imgId)
3351            controlsDict = G2frame.GPXtree.GetItemPyData(
3352                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3353            ImageMasks = G2frame.GPXtree.GetItemPyData(
3354                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
3355            if self.params['Mode'] == 'table': # look up parameter values from table
3356                useTA = None        #force remake of x,y-->2th,azm map
3357                self.ResetFromTable(controlsDict['setdist'])
3358            # update controls from master
3359            controlsDict.update(self.ImageControls)
3360            # update masks from master w/o Thresholds
3361            ImageMasks.update(self.ImageMasks)
3362            self.EnableButtons(False)
3363            try:
3364                self.IntegrateImage(img,useTA=useTA,useMask=useMask)
3365            finally:
3366                self.EnableButtons(True)
3367            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
3368            wx.Yield()
3369            self.ShowMatchingFiles(self.params['filter'])
3370            wx.Yield()
3371           
3372        def AutoComputePDF(imgId):
3373            '''Computes a PDF for a PWDR data tree tree item
3374            '''
3375            for pwdr in G2frame.AutointPWDRnames[:]:
3376                if not pwdr.startswith('PWDR '): continue
3377                if pwdr in G2frame.AutointPDFnames: continue
3378                PWid = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,pwdr)
3379                controlsDict = G2frame.GPXtree.GetItemPyData(
3380                    G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3381                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
3382                    print('Skipping PDF for '+pwdr+' due to detector position')
3383                    continue
3384                # Setup PDF
3385                Data = G2frame.GPXtree.GetItemPyData(PWid)[1]
3386                pwdrMin = np.min(Data[1])
3387                Parms = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3388                    G2frame,PWid,'Instrument Parameters'))[0]
3389                fullLimits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3390                    G2frame,PWid,'Limits'))[0]
3391                if 'C' in Parms['Type'][0]:
3392                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
3393                else:
3394                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
3395                Qlimits = [0.9*qMax,qMax]
3396
3397                item = pwdr
3398                Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3399                    G2frame,imgId, 'Comments'))
3400                ElList = {}
3401                sumnum = 1.0
3402                for item in Comments:           #grab chemical formula from Comments, if there
3403                    if 'formula' in item[:15].lower():
3404                        formula = item.split('=')[1].split()
3405                        elems = formula[::2]
3406                        nums = formula[1::2]
3407                        formula = zip(elems,nums)
3408                        sumnum = 0.
3409                        for [elem,num] in formula:
3410                            ElData = G2elem.GetElInfo(elem,Parms)
3411                            ElData['FormulaNo'] = float(num)
3412                            sumnum += float(num)
3413                            ElList[elem] = ElData
3414                PDFnames = G2gd.GetGPXtreeDataNames(G2frame,['PDF ',])
3415                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
3416                if not PDFid: continue
3417                PDFdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3418                    G2frame,PDFid, 'PDF Controls'))
3419                PDFdata.update(copy.deepcopy(self.pdfControls))
3420                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
3421                PDFdata['Sample']['Name'] = pwdr
3422                # compute PDF
3423                wx.Yield()
3424                G2pdG.computePDF(G2frame,PDFdata)
3425                wx.Yield()
3426                G2frame.PatternId = PDFid
3427                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3428                if self.params['optPDF']:
3429                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3430                    wx.Yield()
3431                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3432                G2frame.AutointPDFnames.append(pwdr)
3433                # save names of PDF entry to be deleted later if needed
3434                G2frame.AutointPWDRnames.append(G2frame.GPXtree.GetItemText(PDFid))
3435           
3436        G2frame = self.G2frame
3437        try:
3438            self.currImageList = sorted(
3439                glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
3440            self.ShowMatchingFiles(self.params['filter'])
3441        except IndexError:
3442            self.currImageList = []
3443            return
3444
3445        if self.PreventReEntryTimer: return
3446        self.PreventReEntryTimer = True
3447        imageFileList = []
3448        # integrate the images that have already been read in, but
3449        # have not yet been processed           
3450        oldData = {'tilt':0.,'distance':0.,'rotation':0.,'center':[0.,0.],'DetDepth':0.,'azmthOff':0.}
3451        oldMhash = 0
3452        if 'useTA' not in dir(self):    #initial definition; reuse if after Resume
3453            self.useTA = None
3454            self.useMask = None
3455        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3456            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3457            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3458            # Create a list of image files that have been read in
3459            if imagefile not in imageFileList: imageFileList.append(imagefile)
3460            # skip if already integrated
3461            if img in G2frame.IntegratedList: continue
3462            Data = G2frame.GPXtree.GetItemPyData(
3463                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3464            sameTA = True
3465            for item in ['tilt','distance','rotation','center','DetDepth','azmthOff']:
3466                if Data[item] != oldData[item]:
3467                    sameTA = False
3468            if not sameTA:
3469                t0 = time.time()
3470                self.useTA = G2img.MakeUseTA(Data,blkSize)
3471                print(' Use new image controls; xy->th,azm mtime: %.3f'%(time.time()-t0))
3472            Mask = G2frame.GPXtree.GetItemPyData(
3473                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
3474            Mhash = copy.deepcopy(Mask)
3475            Mhash.pop('Thresholds')
3476            Mhash = hash(str(Mhash))
3477            if  Mhash != oldMhash:
3478                t0 = time.time()
3479                self.useMask = G2img.MakeUseMask(Data,Mask,blkSize)
3480                print(' Use new mask; make mask time: %.3f'%(time.time()-t0))
3481                oldMhash = Mhash
3482            AutoIntegrateImage(imgId,self.useTA,self.useMask)
3483            oldData = Data
3484            if self.pdfControls: AutoComputePDF(imgId)
3485            self.Pause |= G2frame.PauseIntegration
3486            if self.Pause:
3487                self.OnPause()
3488                self.PreventReEntryTimer = False
3489                self.Raise()
3490                return
3491
3492        # loop over image files matching glob, reading in any new ones
3493        if self.useTA is None or self.useMask is None:
3494            print('Integration will not be fast; there is no beginning image controls')     #TODO: work this out??
3495        for newImage in self.currImageList:
3496            if newImage in imageFileList or self.Pause: continue # already read?
3497            for imgId in G2IO.ReadImages(G2frame,newImage):
3498                AutoIntegrateImage(imgId,self.useTA,self.useMask)
3499                if self.pdfControls: AutoComputePDF(imgId)
3500                self.Pause |= G2frame.PauseIntegration
3501                if self.Pause:
3502                    self.OnPause()
3503                    self.PreventReEntryTimer = False
3504                    self.Raise()
3505                    return
3506        if GSASIIpath.GetConfigValue('debug'):
3507            import datetime
3508            print ("DBG_Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3509        self.PreventReEntryTimer = False
3510        self.Raise()
3511
3512def DefineEvaluator(dlg):
3513    '''Creates a function that provides interpolated values for a given distance value
3514    '''
3515    def Evaluator(dist):
3516        '''Interpolate image parameters for a supplied distance value
3517
3518        :param float dist: distance to use for interpolation
3519        :returns: a list with 3 items:
3520
3521          * a dict with interpolated parameter values,
3522          * the closest imctrl and
3523          * the closest maskfile (or None)
3524        '''           
3525        x = np.array([float(i) for i in parms[0]])
3526        closest = abs(x-dist).argmin()
3527        D = {'setdist':dist}
3528        imctfile = IMfileList[closest]
3529        if parms[-1][closest].lower() != '(none)':
3530            maskfile = parms[-1][closest]
3531        else:
3532            maskfile = None
3533        for c in range(1,cols-1):
3534            lbl = ParmList[c]
3535            if lbl in nonInterpVars:
3536                if lbl in ['outChannels',]:
3537                    D[lbl] = int(float(parms[c][closest]))
3538                else:
3539                    D[lbl] = float(parms[c][closest])
3540            else:
3541                y = np.array([float(i) for i in parms[c]])
3542                D[lbl] = np.interp(dist,x,y)
3543        # full integration when angular range is 0
3544        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3545        # conversion for paired values
3546        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3547            r = a.split('_')[0]
3548            D[r] = [D[a],D[b]]
3549            if r in ['LRazimuth',]:
3550                D[r] = [int(D[a]),int(D[b])]
3551            del D[a]
3552            del D[b]
3553        return D,imctfile,maskfile
3554    # save local copies of values needed in Evaluator
3555    parms = dlg.ReadImageParmTable()
3556    IMfileList = dlg.IMfileList
3557    cols = dlg.list.GetColumnCount()
3558    ParmList = dlg.ParmList
3559    nonInterpVars = dlg.nonInterpVars
3560    return Evaluator
3561
3562class IntegParmTable(wx.Dialog):
3563    '''Creates a dialog window with a table of integration parameters.
3564    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3565    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3566    creates a dictionary with interpolated parameter values.
3567    '''
3568    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3569            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3570            'maskfile',
3571            )
3572    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3573                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3574    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3575            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3576            'Mask File',
3577            )
3578    def __init__(self,parent,parms=None,IMfileList=None,readFileList=None):
3579        self.G2frame = parent.G2frame
3580        dlg = None
3581        pth = ''
3582        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3583        if readFileList:
3584            self.parms,self.IMfileList = self.ReadFiles(readFileList)
3585        elif parms:
3586            self.parms = parms # list of values by column
3587            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3588        else:
3589            self.parms = [] # list of values by column
3590            self.IMfileList = [] # list of .imctrl file names for each entry in table
3591            files = []
3592            try:
3593                pth = G2G.GetImportPath(self.G2frame)
3594                if not pth: pth = '.'
3595                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3596                    style=wx.FD_OPEN| wx.FD_MULTIPLE,
3597                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3598                dlg.CenterOnParent()
3599                if dlg.ShowModal() == wx.ID_OK:
3600                    files = dlg.GetPaths()
3601                    self.parms,self.IMfileList = self.ReadFiles(files)
3602            finally:
3603                if dlg: dlg.Destroy()
3604            if not files:
3605                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3606                return
3607        mainSizer = wx.BoxSizer(wx.VERTICAL)
3608        self.list = ImgIntLstCtrl(self, wx.ID_ANY,style=wx.LC_REPORT| wx.BORDER_SUNKEN)
3609        mainSizer.Add(self.list,1,wx.EXPAND,1)
3610        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3611        btn = wx.Button(self, wx.ID_OK)
3612        btnsizer.Add(btn)
3613        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3614        btn.Bind(wx.EVT_BUTTON,self._onSave)
3615        btnsizer.Add(btn)
3616        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3617        btn.Bind(wx.EVT_BUTTON,self._onClose)
3618        btnsizer.Add(btn)
3619        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3620        self.SetSizer(mainSizer)
3621        self.list.FillList(self.parms)
3622       
3623    def ReadFiles(self,files):
3624        '''Reads a list of .imctrl files or a single .imtbl file
3625        '''
3626        tmpDict = {}
3627        if not files: return
3628        # option 1, a dump from a previous save
3629        if os.path.splitext(files[0])[1] == '.imtbl':
3630            fp = open(files[0],'r')
3631            S = fp.readline()
3632            while S:
3633                if S[0] != '#':
3634                    [key,val] = S[:-1].split(':',1)
3635                    tmpDict[key] = eval(val)
3636                S = fp.readline()
3637            fp.close()
3638            # delete entries where files do not exist
3639            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3640            if m1:
3641                print('\nimctrl file not found:')
3642                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3643            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3644            if m2:
3645                print('\nmask file not found')
3646                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3647            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3648            if m3:
3649                print('\nDropping entries due to negative distance: '+str(m3))
3650            m = sorted(set(m1 + m2 + m3))
3651            m.reverse()
3652            for c in m:
3653                for key in tmpDict:
3654                    del tmpDict[key][c]
3655            fileList = tmpDict.get('filenames','[]')
3656            parms = []
3657            if 'setdist' not in tmpDict:
3658                print(u'Old file, recreate before using: {}'.format(files[0]))
3659                return [[]],[]
3660            for key in self.ParmList:
3661                try:
3662                    float(tmpDict[key][0])
3663                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3664                except ValueError:
3665                    parms.append(tmpDict[key])
3666                except IndexError:
3667                    print('No valid image control entries read')
3668                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3669                    return [[]],[]
3670            return parms,fileList
3671        # option 2, read in a list of files
3672        for file in files: # read all files; place in dict by distance
3673            imgDict = Read_imctrl(file)
3674            dist = imgDict.get('setdist',imgDict['distance'])
3675            if dist is None:
3676                print('Skipping old file, redo: {}'.format(file))
3677            tmpDict[dist] = imgDict
3678        parms = [[] for key in self.ParmList]
3679        fileList = []
3680        for d in sorted(tmpDict):
3681            fileList.append(tmpDict[d].get('filename'))
3682            if d is None: continue
3683            if d < 0: continue
3684            for i,key in enumerate(self.ParmList):
3685                val = tmpDict[d].get(key)
3686                try:
3687                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3688                except:
3689                    val = str(val)
3690                parms[i].append(val)
3691        return parms,fileList
3692   
3693    def ReadImageParmTable(self):
3694        '''Reads possibly edited values from the ListCtrl table and returns a list
3695        of values for each column.
3696        '''
3697        rows = self.list.GetItemCount()
3698        cols = self.list.GetColumnCount()
3699        parms = []
3700        for c in range(cols):
3701            parms.append([])
3702            for r in range(rows):
3703                parms[c].append(self.list.GetItem(r,c).GetText())
3704        return parms
3705
3706    def _onClose(self,event):
3707        'Called when Cancel button is pressed'
3708        self.EndModal(wx.ID_CANCEL)
3709       
3710    def _onSave(self,event):
3711        'Called when save button is pressed; creates a .imtbl file'
3712        fil = ''
3713        if self.G2frame.GSASprojectfile:
3714            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3715        dir,f = os.path.split(fil)
3716        pth = G2G.GetExportPath(self.G2frame)
3717        try:
3718            dlg = wx.FileDialog(self, 'Save table data as',
3719                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3720                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3721            dlg.CenterOnParent()
3722            if dlg.ShowModal() != wx.ID_OK: return
3723            fil = dlg.GetPath()
3724            fil = os.path.splitext(fil)[0]+'.imtbl'
3725        finally:
3726            dlg.Destroy()       
3727        parms = self.ReadImageParmTable()
3728        print('Writing image parameter table as '+fil)
3729        fp = open(fil,'w')
3730        for c in range(len(parms)-1):
3731            lbl = self.ParmList[c]
3732            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3733        lbl = self.ParmList[c+1]
3734        fp.write(lbl+': '+str(parms[c+1])+'\n')
3735        lbl = 'filenames'
3736        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3737        fp.close()
3738
3739class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3740    '''Creates a custom ListCtrl for editing Image Integration parameters
3741    '''
3742    def __init__(self, parent, ID, pos=wx.DefaultPosition,size=(1000,200),style=0):
3743        self.parent=parent
3744        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3745        listmix.ListCtrlAutoWidthMixin.__init__(self)
3746        listmix.TextEditMixin.__init__(self)
3747        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3748        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3749       
3750    def FillList(self,parms):
3751        'Places the current parms into the table'
3752        # the use of InsertStringItem and SetStringItem are depricated in 4.0 but
3753        # I am not quite sure how to replace them with InsertItem and SetItem yet.
3754        # Perhaps switch to  ULC.UltimateListCtrl?
3755        #
3756        maxint = 2**31-1
3757        self.ClearAll()
3758        self.rowlen = len(self.parent.ParmList)
3759        for i,lbl in enumerate(self.parent.HeaderList):
3760            self.InsertColumn(i, lbl)
3761        for r,d in enumerate(parms[0]):
3762            if d is None: continue
3763            if d == 'None': continue
3764            if float(d) < 0: continue
3765            index = self.InsertStringItem(maxint, d)
3766            for j in range(1,len(parms)):
3767                self.SetStringItem(index, j, parms[j][r])
3768        for i,lbl in enumerate(self.parent.ParmList):
3769            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3770
3771    def OnDouble(self,evt):
3772        'respond to a double-click'
3773        self.CloseEditor()
3774        fil = '(none)'
3775        pth = G2G.GetImportPath(self.parent.G2frame)
3776        if not pth: pth = '.'
3777        try:
3778            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3779                style=wx.FD_OPEN,wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3780            dlg.CenterOnParent()
3781            if dlg.ShowModal() == wx.ID_OK:
3782                fil = dlg.GetPath()
3783        finally:
3784            dlg.Destroy()
3785        if os.path.splitext(fil)[1] != '.imctrl':
3786            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3787            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3788        else:
3789            # insert or overwrite an instrument parameter set
3790            if not os.path.exists(fil):
3791                print('Does not exist: '+fil)
3792                return
3793            imgDict = Read_imctrl(fil)
3794            dist = imgDict['distance']
3795            parms = self.parent.ReadImageParmTable()
3796            x = np.array([float(i) for i in parms[0]])
3797            closest = abs(x-dist).argmin()
3798            closeX = x[closest]
3799            # fix IMfileList
3800            for c,lbl in enumerate(self.parent.ParmList):
3801                try:
3802                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3803                except ValueError:
3804                    vali = imgDict[lbl]
3805                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3806                    parms[c][closest] = vali
3807                elif dist > closeX: # insert after
3808                    parms[c].insert(closest+1,vali)
3809                else:
3810                    parms[c].insert(closest,vali)
3811            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3812                self.parent.IMfileList[closest] = fil
3813            elif dist > closeX: # insert after
3814                self.parent.IMfileList.insert(closest+1,fil)
3815            else:
3816                self.parent.IMfileList.insert(closest,fil)
3817            self.FillList(parms)
3818# Autointegration end
3819###########################################################################
3820
3821def testColumnMetadata(G2frame):
3822    '''Test the column-oriented metadata parsing, as implemented at 1-ID, by showing results
3823    when using a .par and .lbls pair.
3824   
3825     * Select a .par file, if more than one in selected dir.
3826     * Select the .*lbls file, if more than one matching .par file.
3827     * Parse the .lbls file, showing errors if encountered; loop until errors are fixed.
3828     * Search for an image or a line in the .par file and show the results when interpreted
3829     
3830    See :func:`GSASIIfiles.readColMetadata` for more details.
3831    '''
3832    if not GSASIIpath.GetConfigValue('Column_Metadata_directory'):
3833        G2G.G2MessageBox(G2frame,'The configuration option for I-ID Metadata is not set.\n'+
3834                         'Please use the File/Preferences menu to set Column_Metadata_directory',
3835                         'Warning')
3836        return
3837    parFiles = glob.glob(os.path.join(GSASIIpath.GetConfigValue('Column_Metadata_directory'),'*.par'))
3838    if not parFiles: 
3839        G2G.G2MessageBox(G2frame,'No .par files found in directory {}. '
3840                         .format(GSASIIpath.GetConfigValue('Column_Metadata_directory'))+
3841                         '\nThis is set by config variable Column_Metadata_directory '+
3842                         '(Set in File/Preferences menu).',
3843                         'Warning')
3844        return
3845    parList = []
3846    for parFile in parFiles:
3847        lblList = []
3848        parRoot = os.path.splitext(parFile)[0]
3849        for f in glob.glob(parRoot+'.*lbls'):
3850            if os.path.exists(f): lblList.append(f)
3851        if not len(lblList):
3852            continue
3853        parList.append(parFile)
3854    if len(parList) == 0:
3855        G2G.G2MessageBox(G2frame,'No .lbls or .EXT_lbls file found for .par file(s) in directory {}. '
3856                         .format(GSASIIpath.GetConfigValue('Column_Metadata_directory'))+
3857                         '\nThis is set by config variable Column_Metadata_directory '+
3858                         '(Set in File/Preferences menu).',
3859                         'Warning')
3860        return
3861    elif len(parList) == 1:
3862        parFile = parList[0]
3863    else:
3864        dlg = G2G.G2SingleChoiceDialog(G2frame,
3865                'More than 1 .par file found. (Better if only 1!). Choose the one to test in '+
3866                GSASIIpath.GetConfigValue('Column_Metadata_directory'),
3867                'Choose .par file', [os.path.split(i)[1] for i in parList])
3868        if dlg.ShowModal() == wx.ID_OK:
3869            parFile = parList[dlg.GetSelection()]
3870            dlg.Destroy()
3871        else:
3872            dlg.Destroy()
3873            return
3874    # got .par file; now work on .*lbls file
3875    lblList = []
3876    parRoot = os.path.splitext(parFile)[0]
3877    for f in glob.glob(parRoot+'.*lbls'):
3878        if os.path.exists(f): lblList.append(f)
3879    if not len(lblList):
3880        raise Exception('How did this happen! No .*lbls files for '+parFile)
3881    elif len(lblList) == 1:
3882        lblFile = lblList[0]
3883    else:
3884        dlg = G2G.G2SingleChoiceDialog(G2frame,
3885                'Select label file for .par file '+parFile,
3886                'Choose label file', [os.path.split(i)[1] for i in lblList])
3887        if dlg.ShowModal() == wx.ID_OK:
3888            lblFile = lblList[dlg.GetSelection()]
3889            dlg.Destroy()
3890        else:
3891            dlg.Destroy()
3892            return
3893    # parse the labels file
3894    errors = True
3895    while errors:
3896        labels,lbldict,keyCols,keyExp,errors = G2fil.readColMetadataLabels(lblFile)
3897        if errors:
3898            t = "Error reading file "+lblFile
3899            for l in errors:
3900                t += '\n'
3901                t += l
3902            t += "\n\nPlease edit the file and press OK (or Cancel to quit)"
3903            dlg = wx.MessageDialog(G2frame,message=t,
3904                caption="Read Error",style=wx.ICON_ERROR| wx.OK|wx.STAY_ON_TOP|wx.CANCEL)
3905            if dlg.ShowModal() != wx.ID_OK: return           
3906    # request a line number, read that line
3907    dlg = G2G.SingleStringDialog(G2frame,'Read what',
3908                                 'Enter a line number or an image file name (-1=last line)',
3909                                 '-1',size=(400,-1))
3910    if dlg.Show():
3911        fileorline = dlg.GetValue()
3912        dlg.Destroy()
3913    else:
3914        dlg.Destroy()
3915        return
3916    # and report the generated key pairs in metadata dict
3917    linenum = None
3918    try:
3919        linenum = int(fileorline)
3920    except:
3921        imageName = os.path.splitext(os.path.split(fileorline)[1])[0]
3922
3923    fp = open(parFile,'Ur')
3924    for iline,line in enumerate(fp):
3925        if linenum is not None:
3926            if iline == linenum:
3927                items = line.strip().split(' ')
3928                n = "Line {}".format(iline)
3929                break
3930            else:
3931                continue
3932        else:
3933            items = line.strip().split(' ')
3934            nameList = keyExp['filename'](*[items[j] for j in keyCols['filename']])
3935            if type(nameList) is str:
3936                if nameList != imageName: continue
3937                name = nameList
3938                break
3939            else:
3940                for name in nameList:
3941                    print (name,name == imageName)
3942                    if name == imageName:
3943                        n = "Image {} found in line {}".format(imageName,iline)
3944                        break # got a match
3945                else:
3946                    continue
3947                break
3948    else:
3949        if linenum is not None:
3950            n = "Line {}".format(iline)
3951        else:
3952            n = "Image {} not found. Reporting line {}".format(imageName,iline)
3953        items = line.strip().split(' ')
3954    fp.close()
3955    metadata = G2fil.evalColMetadataDicts(items,labels,lbldict,keyCols,keyExp,True)
3956    title = "Results: ("+n+")"
3957    t = ['Files: '+parFile,lblFile,' ']
3958    n = ["Named parameters:"]
3959    l = ['',"Labeled columns:"]
3960    for key in sorted(metadata):
3961        if key == "filename" or key.startswith('label_prm'): continue
3962        if key in keyCols:
3963            n += [{} = {}".format(key,metadata[key])]
3964        elif key in lbldict.values():
3965            l += [{} = {}".format(key,metadata[key])]
3966        else:
3967            t += ["** Unexpected:  {}".format(key,metadata[key])]
3968    if type(metadata['filename']) is str:
3969        l += ["","Filename: "+ metadata['filename']]
3970    else:
3971        l += ["","Filename(s): "]
3972        for i,j in enumerate(metadata['filename']):
3973            if i: l[-1] += ', '
3974            l[-1] += j
3975    t += n + l + ['','Unused columns:']
3976    usedCols = list(lbldict.keys())
3977    for i in keyCols.values(): usedCols += i
3978    for i in range(len(items)):
3979        if i in usedCols: continue
3980        t += [{}: {}".format(i,items[i])]
3981    dlg = G2G.G2SingleChoiceDialog(None,title,'Column metadata parse results',t,
3982                                   monoFont=True,filterBox=False,size=(400,600),
3983                                   style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE|wx.OK)
3984    dlg.ShowModal()
Note: See TracBrowser for help on using the repository browser.