source: trunk/GSASIIimgGUI.py @ 4697

Last change on this file since 4697 was 4697, checked in by vondreele, 2 years ago

move superminusone definition to header block in G2pwdrGUI
replace deprecated GetLabel? with GetItemLabelText? in G2restrGUI
set default gain map color to 'gray' & force all gain map > 1200 or < 800 toequal 1000 - gets rid of zingers & other flaws

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