source: trunk/GSASIIimgGUI.py @ 4299

Last change on this file since 4299 was 4299, checked in by vondreele, 3 years ago

new spot mask algorithm - it is slow but works

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