source: trunk/GSASIIimgGUI.py @ 4361

Last change on this file since 4361 was 4361, checked in by toby, 23 months ago

fix hist CIF export, remove dev. ver of IntPDFtool

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