source: trunk/GSASIIimgGUI.py @ 4698

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

forgot the 'color' change for gain map

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