source: trunk/GSASIIimgGUI.py @ 4793

Last change on this file since 4793 was 4712, checked in by vondreele, 4 years ago

fix plotting of image limit ellipses - wrong call (GetDetectorXY) used. Should be GetDetectorXY2

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