source: trunk/GSASIIimgGUI.py @ 4108

Last change on this file since 4108 was 4108, checked in by toby, 2 years ago

add plotting of rings & ring picks; load parms into each image

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