source: trunk/GSASIIimgGUI.py @ 4672

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

remove all wx.ALIGN from wx Add with wx.EXPAND - invalid combination ignored in older wx; now flagged in current wx 4.1.x
make ax as self.ax in Absorb.py - fix matplotlib warning about reusing a subaxis
put floors on pink beam alp & bet. Fix typo in GetPinkAlpBet? function - now works better.

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