source: trunk/GSASIIimgGUI.py @ 4571

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

Improvements to polarization stuff

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