source: trunk/GSASIIimgGUI.py @ 4303

Last change on this file since 4303 was 4303, checked in by toby, 3 years ago

fix scriptable for multiple image files and xfer correction in @4286

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