source: trunk/GSASIIimgGUI.py @ 4830

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

show more places in Absorb output
improve image absorption calcs for cylinders now seems to work OK
reorganize Image controls GUI & add option for vertical samples
hide printing of LS cycles if Rrint=False in HessianLSQ
implement (& not use) Klein-Nishina PDF correction
Fix bug in if importer

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