source: trunk/GSASIIimgGUI.py @ 4197

Last change on this file since 4197 was 4180, checked in by vondreele, 6 years ago

reoptimize integration block size; now 128 seems fastest (was 256 but added an array)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 185.1 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASII - image data display routines
3########### SVN repository information ###################
4# $Date: 2019-10-16 20:46:52 +0000 (Wed, 16 Oct 2019) $
5# $Author: vondreele $
6# $Revision: 4180 $
7# $URL: trunk/GSASIIimgGUI.py $
8# $Id: GSASIIimgGUI.py 4180 2019-10-16 20:46:52Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIimgGUI: Image GUI*
12-------------------------
13
14Control image display and processing
15
16'''
17from __future__ import division, print_function
18import os
19import copy
20import glob
21import time
22import re
23import math
24import sys
25import wx
26import wx.lib.mixins.listctrl  as  listmix
27import wx.grid as wg
28import matplotlib as mpl
29import numpy as np
30import GSASIIpath
31GSASIIpath.SetVersionNumber("$Revision: 4180 $")
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 = 128   #128 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    G2frame.GetStatusBar().SetStatusText('* Global parameters in Multi-dist recalib.',1)
1436    colorList = sorted([m for m in mpl.cm.datad.keys() ],key=lambda s: s.lower())   #if not m.endswith("_r")
1437    calList = sorted([m for m in calFile.Calibrants.keys()],key=lambda s: s.lower())
1438    typeList = ['PWDR - powder diffraction data','SASD - small angle scattering data',]
1439    if not data.get('type'):                        #patch for old project files
1440        data['type'] = 'PWDR'
1441    typeDict = {'PWDR':typeList[0],'SASD':typeList[1],}
1442    G2frame.dataWindow.ClearData()
1443    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ImageMenu)
1444    G2frame.Bind(wx.EVT_MENU, OnCalibrate, id=G2G.wxID_IMCALIBRATE)
1445    G2frame.Bind(wx.EVT_MENU, OnRecalibrate, id=G2G.wxID_IMRECALIBRATE)
1446    G2frame.Bind(wx.EVT_MENU, OnRecalibAll, id=G2G.wxID_IMRECALIBALL)
1447    G2frame.Bind(wx.EVT_MENU, OnDistRecalib, id=G2G.wxID_IMDISTRECALIB)
1448    G2frame.Bind(wx.EVT_MENU, OnClearCalib, id=G2G.wxID_IMCLEARCALIB)
1449#    if data.get('calibrant'):
1450#        mode = True
1451#    else:
1452#        mode = False
1453#    G2frame.Enable(id=G2G.wxID_IMRECALIBRATE,enable=mode)
1454#    G2frame.Enable(id=G2G.wxID_IMCALIBRATE,enable=mode)
1455#    G2frame.Enable(id=G2G.wxID_IMRECALIBALL,enable=mode)
1456    G2frame.Bind(wx.EVT_MENU, OnIntegrate, id=G2G.wxID_IMINTEGRATE)
1457    G2frame.Bind(wx.EVT_MENU, OnIntegrateAll, id=G2G.wxID_INTEGRATEALL)
1458    G2frame.Bind(wx.EVT_MENU, OnCopyControls, id=G2G.wxID_IMCOPYCONTROLS)
1459    G2frame.Bind(wx.EVT_MENU, OnCopySelected, id=G2G.wxID_IMCOPYSELECTED)
1460    G2frame.Bind(wx.EVT_MENU, OnSaveControls, id=G2G.wxID_IMSAVECONTROLS)
1461    G2frame.Bind(wx.EVT_MENU, OnSaveMultiControls, id=G2G.wxID_SAVESELECTEDCONTROLS)
1462    G2frame.Bind(wx.EVT_MENU, OnLoadControls, id=G2G.wxID_IMLOADCONTROLS)
1463    G2frame.Bind(wx.EVT_MENU, OnLoadMultiControls, id=G2G.wxID_LOADELECTEDCONTROLS)
1464    G2frame.Bind(wx.EVT_MENU, OnTransferAngles, id=G2G.wxID_IMXFERCONTROLS)
1465    G2frame.Bind(wx.EVT_MENU, OnResetDist, id=G2G.wxID_IMRESETDIST)
1466    def OnDestroy(event):
1467        G2frame.autoIntFrame = None
1468    def OnAutoInt(event):
1469        if G2frame.autoIntFrame: # ensure only one open at a time
1470            G2frame.autoIntFrame.Raise()
1471            return
1472        PollTime = GSASIIpath.GetConfigValue('Autoint_PollTime',30.)
1473        G2frame.autoIntFrame = AutoIntFrame(G2frame,PollTime=PollTime)
1474        # debug code to reload code for window on each use
1475        #import GSASIIimgGUI
1476        #reload(GSASIIimgGUI)
1477        #G2frame.autoIntFrame = GSASIIimgGUI.AutoIntFrame(G2frame,PollTime=PollTime)
1478
1479        G2frame.autoIntFrame.Bind(wx.EVT_WINDOW_DESTROY,OnDestroy) # clean up name on window close
1480    G2frame.Bind(wx.EVT_MENU, OnAutoInt, id=G2G.wxID_IMAUTOINTEG)
1481    def OnIntPDFtool(event):
1482        import subprocess
1483        ex = sys.executable
1484        if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1485            if os.path.exists(ex+'w'): ex += 'w'
1486        if G2frame.GSASprojectfile:
1487            project = os.path.abspath(G2frame.GSASprojectfile)
1488        else:
1489            project = ''
1490        subprocess.Popen([ex,os.path.join(GSASIIpath.path2GSAS2,'GSASIIIntPDFtool.py'),project])
1491    G2frame.Bind(wx.EVT_MENU, OnIntPDFtool, id=G2G.wxID_IMINTEGPDFTOOL)
1492
1493    mainSizer = G2frame.dataWindow.GetSizer()
1494    mainSizer.Add((5,10),0)   
1495    mainSizer.Add(ComboSizer(),0,wx.ALIGN_LEFT)
1496    mainSizer.Add((5,5),0)
1497    Range = data['range'] # allows code to be same in Masks
1498    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1499    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1500   
1501    mainSizer.Add((5,5),0)
1502    DataSizer = wx.FlexGridSizer(0,2,5,0)
1503    DataSizer.Add(CalibCoeffSizer(),0)
1504    DataSizer.Add(IntegrateSizer(),0)       
1505    mainSizer.Add(DataSizer,0)
1506    mainSizer.Add((5,5),0)           
1507    mainSizer.Add(BackSizer(),0)
1508    mainSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Calibration controls:'),0,WACV)
1509    mainSizer.Add((5,5),0)
1510    mainSizer.Add(CalibSizer(),0,WACV)
1511    mainSizer.Add((5,5),0)
1512    mainSizer.Add(GonioSizer(),0,WACV)   
1513    G2frame.dataWindow.SetDataSize()
1514   
1515################################################################################
1516##### Masks
1517################################################################################
1518def CleanupMasks(data):
1519    '''If a mask creation is not completed, an empty mask entry is created in the
1520    masks array. This cleans them out. It is called when the masks page is first loaded
1521    and before saving them or after reading them in. This should also probably be done
1522    before they are used for integration.
1523    '''
1524    for key in ['Points','Rings','Arcs','Polygons',]:
1525        data[key] = data.get(key,[])
1526        l1 = len(data[key])
1527        data[key] = [i for i in data[key] if len(i)]
1528        l2 = len(data[key])
1529        if GSASIIpath.GetConfigValue('debug') and l1 != l2:
1530            print ('DBG_Mask Cleanup: %s was %d entries, now %d'%(key,l1,l2))
1531   
1532def UpdateMasks(G2frame,data):
1533    '''Shows and handles the controls on the "Masks" data tree entry
1534    '''
1535   
1536    def OnTextMsg(event):
1537        Obj = event.GetEventObject()
1538        Obj.SetToolTipString('Drag this mask on 2D Powder Image with mouse to change ')
1539
1540    def Replot(*args,**kwargs):
1541        wx.CallAfter(G2plt.PlotExposedImage,G2frame)
1542
1543    def newReplot(*args,**kwargs):
1544        wx.CallAfter(G2plt.PlotExposedImage,G2frame,newPlot=True)
1545
1546    def onDeleteMask(event):
1547        Obj = event.GetEventObject()
1548        typ = Obj.locationcode.split('+')[1]
1549        num = int(Obj.locationcode.split('+')[2])
1550        del(data[typ][num])
1551        wx.CallAfter(UpdateMasks,G2frame,data)
1552        G2plt.PlotExposedImage(G2frame,event=event)
1553       
1554    def OnSpotChange(event):
1555        r,c = event.GetRow(),event.GetCol()
1556        if c == 2:
1557            del Spots[r]
1558            SpotTable.DeleteRow(r)
1559        else:
1560            Spots[r][2] = float(SpotGrid.GetCellValue(r,c))
1561        SpotGrid.ForceRefresh()
1562        wx.CallAfter(UpdateMasks,G2frame,data)
1563        G2plt.PlotExposedImage(G2frame,event=event)
1564        event.Skip()
1565
1566    def onDeleteFrame(event):
1567        data['Frames'] = []
1568        wx.CallAfter(UpdateMasks,G2frame,data)
1569        G2plt.PlotExposedImage(G2frame,event=event)
1570
1571    def OnCopyMask(event):
1572        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
1573        if len(Names) == 1:
1574            G2frame.ErrorDialog('Nothing to copy masks to','There must be more than one "IMG" pattern')
1575            return
1576        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
1577        Names.pop(Names.index(Source))
1578        Data = copy.deepcopy(data)
1579        Thresh = Data.pop('Thresholds')     # & remove it as well
1580        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy mask data','Copy masks from '+Source+' to:',Names)
1581        try:
1582            if dlg.ShowModal() == wx.ID_OK:
1583                items = dlg.GetSelections()
1584                for item in items:
1585                    name = Names[item]
1586                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
1587                    MId = G2gd.GetGPXtreeItemId(G2frame,Id,'Masks')
1588                    Mask = G2frame.GPXtree.GetItemPyData(MId)
1589                    Mask.update(copy.deepcopy(Data))
1590                    Mask['Thresholds'][1][0] = Thresh[1][0]  #copy only lower threshold
1591                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Masks'),Mask)
1592        finally:
1593            dlg.Destroy()
1594
1595    def OnSaveMask(event):
1596        CleanupMasks(data)
1597        pth = G2G.GetExportPath(G2frame)
1598        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1599            'image mask files (*.immask)|*.immask',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
1600        try:
1601            if dlg.ShowModal() == wx.ID_OK:
1602                filename = dlg.GetPath()
1603                filename = os.path.splitext(filename)[0]+'.immask'
1604                File = open(filename,'w')
1605                keys = ['Points','Rings','Arcs','Polygons','Xlines','Ylines','Frames','Thresholds']
1606                for key in keys:
1607                    File.write(key+':'+str(data[key])+'\n')
1608                File.close()
1609        finally:
1610            dlg.Destroy()
1611       
1612    def OnLoadMask(event):
1613        if event.Id == G2G.wxID_MASKLOADNOT:
1614            ignoreThreshold = True
1615        else:
1616            ignoreThreshold = False
1617        pth = G2G.GetImportPath(G2frame)
1618        if not pth: pth = '.'
1619        dlg = wx.FileDialog(G2frame, 'Choose image mask file', pth, '', 
1620            'image mask files (*.immask)|*.immask',wx.FD_OPEN)
1621        try:
1622            if dlg.ShowModal() == wx.ID_OK:
1623                filename = dlg.GetPath()
1624                G2fil.readMasks(filename,data,ignoreThreshold)
1625                wx.CallAfter(UpdateMasks,G2frame,data)
1626                G2plt.PlotExposedImage(G2frame,event=event)               
1627        finally:
1628            dlg.Destroy()
1629           
1630    def OnAutoSpotMask(event):
1631        'Do auto search for spot masks'
1632        if wx.MessageDialog(G2frame.dataWindow,'NB: This will clear any old spot masks',
1633            'Auto Spot Masks', wx.OK|wx.CANCEL).ShowModal() == wx.ID_OK:
1634            Controls = copy.deepcopy(G2frame.GPXtree.GetItemPyData( 
1635                G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')))
1636            Error = G2img.AutoSpotMasks(G2frame.ImageZ,data,Controls)
1637            if not Error is None:
1638                G2frame.ErrorDialog('Auto spot search error',Error)
1639            wx.CallAfter(UpdateMasks,G2frame,data)
1640            wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)
1641
1642    def OnDeleteSpotMask(event):
1643        data['Points'] = []
1644        wx.CallAfter(UpdateMasks,G2frame,data)
1645        wx.CallAfter(G2plt.PlotExposedImage,G2frame,event=event)         
1646           
1647    def ToggleSpotMaskMode(event):
1648        G2plt.ToggleMultiSpotMask(G2frame)
1649       
1650    def OnNewArcMask(event):
1651        'Start a new arc mask'
1652        G2frame.MaskKey = 'a'
1653        G2plt.OnStartMask(G2frame)
1654       
1655    def OnNewRingMask(event):
1656        'Start a new ring mask'
1657        G2frame.MaskKey = 'r'
1658        G2plt.OnStartMask(G2frame)
1659       
1660    def OnNewXlineMask(event):
1661        'Start a new x-line mask'
1662        print('x')
1663        G2frame.MaskKey = 'x'
1664        G2plt.OnStartMask(G2frame)
1665       
1666    def OnNewYlineMask(event):
1667        'Start a new y-line mask'
1668        G2frame.MaskKey = 'y'
1669        G2plt.OnStartMask(G2frame)
1670       
1671    def OnNewPolyMask(event):
1672        'Start a new polygon mask'
1673        G2frame.MaskKey = 'p'
1674        G2plt.OnStartMask(G2frame)
1675       
1676    def OnNewFrameMask(event):
1677        'Start a new Frame mask'
1678        G2frame.MaskKey = 'f'
1679        G2plt.OnStartMask(G2frame)
1680       
1681    def MaxSizer():
1682        '''Defines a sizer with sliders and TextCtrl widgets for controlling the colormap
1683        for the image, as well as callback routines.
1684        '''
1685        def OnNewVal(invalid,value,tc):
1686            '''Called when a Imax or Imin value is typed into a Validated TextCrtl (which puts
1687            the value into the data['range'] nested list).
1688            This adjusts the slider positions to match the current values
1689            '''
1690            scaleSel.SetSelection(len(scaleChoices)-1)
1691            r11 = min(max(Range[1][1],Range[1][0]+1),Range[0][1]) # keep values in range
1692            if r11 != Range[1][1]:
1693                Range[1][1] = r11
1694                maxVal.SetValue(int(Range[1][1]))
1695            r10 = max(min(Range[1][0],Range[1][1]-1),Range[0][0])
1696            if r10 != Range[1][0]:
1697                Range[1][0] = r10
1698                minVal.SetValue(int(Range[1][0]))
1699            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1700            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1701            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1702            maxSel.SetValue(sv1)
1703            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1704            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1705            minSel.SetValue(sv0)
1706            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1707            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1708            if mplOld:
1709                Page.canvas.draw()
1710            else:
1711                Page.canvas.draw_idle()
1712           
1713        G2frame.prevMaxValue = None   
1714        def OnMaxSlider(event):
1715            val = maxSel.GetValue()
1716            if G2frame.prevMaxValue == val: return # if this val has been processed, no need to repeat
1717            scaleSel.SetSelection(len(scaleChoices)-1)
1718            G2frame.prevMaxValue = val
1719            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1720            Range[1][1] = int(0.5 + (val * sqrtDeltZero / 100.)**2 + Range[1][0] + 1)
1721            maxVal.SetValue(int(0.5+Range[1][1]))
1722            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1723            minSel.SetValue(int(0.5 + 100*(Range[1][0]/DeltOne)))
1724            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1725            minSel.SetValue(sv0)
1726            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1727            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1728            if mplOld:
1729                Page.canvas.draw()
1730            else:
1731                Page.canvas.draw_idle()
1732           
1733        G2frame.prevMinValue = None   
1734        def OnMinSlider(event):
1735            val = minSel.GetValue()
1736            scaleSel.SetSelection(len(scaleChoices)-1)
1737            if G2frame.prevMinValue == val: return # if this val has been processed, no need to repeat
1738            G2frame.prevMinValue = val
1739            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1740            Range[1][0] = max(0,int(0.5 + val * DeltOne / 100 + Range[0][0]))
1741            minVal.SetValue(int(Range[1][0]))
1742            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1743            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1744            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1745            maxSel.SetValue(sv1)
1746            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1747            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1748            if mplOld:
1749                Page.canvas.draw()
1750            else:
1751                Page.canvas.draw_idle()
1752           
1753        def OnAutoSet(event):
1754            '''Responds to a button labeled 95%, etc; Sets the Imax and Imin values
1755            for the image so that 95% (etc.) of pixels are inside the color map limits.
1756            An equal number of pixels are dropped at the minimum and maximum levels.
1757            '''
1758            try:
1759                val = int(event.GetEventObject().GetStringSelection()[:-1])
1760                margin = (100-val)/2.
1761            except:
1762                margin = 0
1763                event.GetEventObject().SetSelection(0)
1764            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1765            if margin == 0:
1766                Range[1] = list(Range[0])
1767            else:
1768                Range[1][0] = int(np.percentile(Page.ImgObj.get_array().compressed(),margin))
1769                Range[1][1] = int(np.percentile(Page.ImgObj.get_array().compressed(),100-margin))
1770            sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1771            sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1772            sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1773            maxSel.SetValue(sv1)
1774            DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1)
1775            sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1776            minSel.SetValue(sv0)
1777            minVal.SetValue(int(Range[1][0]))
1778            maxVal.SetValue(int(Range[1][1]))
1779            new,plotNum,Page,Plot,lim = G2frame.G2plotNB.FindPlotTab('2D Powder Image','mpl',newImage=False)
1780            Page.ImgObj.set_clim([Range[1][0],Range[1][1]])
1781            if mplOld:
1782                Page.canvas.draw()
1783            else:
1784                Page.canvas.draw_idle()
1785
1786        mplv = mpl.__version__.split('.')
1787        mplOld = mplv[0] == '1' and int(mplv[1]) < 4 # use draw_idle for newer matplotlib versions
1788        # Plot color scaling uses limits as below:
1789        #   (Imin0, Imax0) => Range[0] = data['range'][0] # lowest to highest pixel intensity
1790        #   [Imin, Imax] => Range[1] = data['range'][1] #   lowest to highest pixel intensity on cmap scale
1791       
1792        maxSizer = wx.BoxSizer(wx.VERTICAL)
1793        slideSizer = wx.FlexGridSizer(2,3,5,5)
1794        slideSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Max intensity'),0,WACV)
1795        # maxSel is a slider with 101 steps scaled from Imin+1 to Imax0 with sqrt scaling
1796        # slider value = sv = 100 * sqrt((Imax-Imin-1)/(Imax0-Imin-1))
1797        # Imax = (sv * sqrt(Imax0-Imin-1) / 100)**2 + Imin + 1
1798        sqrtDeltZero = math.sqrt(max(1.0,Range[0][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax0-Imin-1)
1799        sqrtDeltOne  = math.sqrt(max(1.0,Range[1][1]-max(0.0,Range[1][0])-1)) # sqrt(Imax-Imin-1)
1800        sv1 = min(100,max(0,int(0.5+100.*sqrtDeltOne/sqrtDeltZero)))
1801        maxSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv1)
1802        maxVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],1,min=Range[0][0]+1,
1803            max=Range[0][1],OnLeave=OnNewVal)
1804        slideSizer.Add(maxVal,0,WACV)
1805        slideSizer.Add(maxSel,flag=wx.EXPAND|wx.ALL)
1806        slideSizer.AddGrowableCol(2)
1807        maxSel.Bind(wx.EVT_SLIDER, OnMaxSlider)
1808        slideSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Min intensity'),0,WACV)
1809        # minSel is a slider with 101 steps scaled from Imin0 to Imax-1 with linear scaling
1810        # slider value = sv0 = 100 * (Imin-Imin0)/(Imax-Imin0-1)
1811        # Imin = sv0 * (Imax-Imin0-1) / 100 + Imin0
1812        DeltOne  = max(1.0,Range[1][1]-max(0.0,Range[0][0])-1) # Imax-Imin0-1
1813        sv0 = min(100,max(0,int(0.5+100.*(Range[1][0]-Range[0][0])/DeltOne)))
1814        minVal = G2G.ValidatedTxtCtrl(G2frame.dataWindow,Range[1],0,
1815            max=Range[0][1],typeHint=int,OnLeave=OnNewVal)
1816        slideSizer.Add(minVal,0,WACV)
1817        minSel = wx.Slider(parent=G2frame.dataWindow,style=wx.SL_HORIZONTAL,value=sv0)
1818        slideSizer.Add(minSel,flag=wx.EXPAND|wx.ALL)
1819        minSel.Bind(wx.EVT_SLIDER, OnMinSlider)
1820        maxSizer.Add(slideSizer,flag=wx.EXPAND|wx.ALL)
1821        autoSizer = wx.BoxSizer(wx.HORIZONTAL)
1822        autoSizer.Add(wx.StaticText(G2frame.dataWindow,label=' Auto scaler '),0,WACV)
1823        scaleChoices = ("100%","99%","95%","90%","80%","?")
1824        scaleSel = wx.Choice(G2frame.dataWindow,choices=scaleChoices,size=(-1,-1))
1825        if (Range[1][0] == Range[0][0] and
1826            Range[1][1] == Range[0][1]):
1827            scaleSel.SetSelection(0)
1828        else:
1829            scaleSel.SetSelection(len(scaleChoices)-1)
1830        scaleSel.Bind(wx.EVT_CHOICE,OnAutoSet)
1831        autoSizer.Add(scaleSel,0,WACV)
1832        maxSizer.Add(autoSizer)
1833        return maxSizer
1834
1835    G2frame.dataWindow.ClearData()
1836    startScroll = None
1837    if G2frame.dataWindow:
1838        startScroll = G2frame.dataWindow.GetScrollPos(wx.VERTICAL) # save scroll position
1839    else:
1840        CleanupMasks(data) # posting page for 1st time; clean out anything unfinished
1841    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.MaskMenu)
1842    G2frame.Bind(wx.EVT_MENU, OnCopyMask, id=G2G.wxID_MASKCOPY)
1843    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOAD)
1844    G2frame.Bind(wx.EVT_MENU, OnLoadMask, id=G2G.wxID_MASKLOADNOT)
1845    G2frame.Bind(wx.EVT_MENU, OnSaveMask, id=G2G.wxID_MASKSAVE)
1846    G2frame.Bind(wx.EVT_MENU, OnAutoSpotMask, id=G2G.wxID_FINDSPOTS)
1847    G2frame.Bind(wx.EVT_MENU, OnDeleteSpotMask, id=G2G.wxID_DELETESPOTS)
1848    G2frame.Bind(wx.EVT_MENU, ToggleSpotMaskMode, id=G2G.wxID_NEWMASKSPOT)
1849    G2frame.Bind(wx.EVT_MENU, OnNewArcMask, id=G2G.wxID_NEWMASKARC)
1850    G2frame.Bind(wx.EVT_MENU, OnNewRingMask, id=G2G.wxID_NEWMASKRING)
1851    G2frame.Bind(wx.EVT_MENU, OnNewXlineMask, id=G2G.wxID_NEWMASKXLINE)
1852    G2frame.Bind(wx.EVT_MENU, OnNewYlineMask, id=G2G.wxID_NEWMASKYLINE)
1853    G2frame.Bind(wx.EVT_MENU, OnNewPolyMask, id=G2G.wxID_NEWMASKPOLY)
1854    G2frame.Bind(wx.EVT_MENU, OnNewFrameMask, id=G2G.wxID_NEWMASKFRAME)
1855    if G2frame.MaskKey == 'f':
1856        G2frame.GetStatusBar().SetStatusText('Frame mask active - LB pick next point, RB close polygon',1)
1857    elif G2frame.MaskKey == 'p':
1858        G2frame.GetStatusBar().SetStatusText('Polygon mask active - LB pick next point, RB close polygon',1)
1859    elif G2frame.MaskKey == 'a':
1860        G2frame.GetStatusBar().SetStatusText('Arc mask active - LB pick arc location',1)
1861    elif G2frame.MaskKey == 'r':
1862        G2frame.GetStatusBar().SetStatusText('Ring mask active - LB pick ring location',1)
1863    elif G2frame.MaskKey == 'x':
1864        G2frame.GetStatusBar().SetStatusText('X-line mask active - LB pick x line of pixels',1)
1865    elif G2frame.MaskKey == 'y':
1866        G2frame.GetStatusBar().SetStatusText('Y-line mask active - LB pick y line of pixels',1)
1867    else:
1868        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)
1869    mainSizer = G2frame.dataWindow.GetSizer()
1870    mainSizer.Add((5,10),0)
1871
1872    thresh = data['Thresholds']         #min/max intensity range
1873    Spots = data['Points']               #x,y,radius in mm
1874    Rings = data['Rings']               #radius, thickness
1875    Polygons = data['Polygons']         #3+ x,y pairs
1876    if 'Xlines' not in data:            #single rows/columns of bad pixels
1877        data['Xlines'] = []
1878        data['Ylines'] = []
1879    Xlines = data['Xlines']
1880    Ylines = data['Ylines']
1881    if 'Frames' not in data:
1882        data['Frames'] = []
1883    frame = data['Frames']             #3+ x,y pairs
1884    Arcs = data['Arcs']                 #radius, start/end azimuth, thickness
1885
1886    ######################################################################
1887    CId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image,'Image Controls')
1888    controlData = G2frame.GPXtree.GetItemPyData(CId)
1889    Range = controlData['range']
1890    MaxSizer = MaxSizer()               #keep this so it can be changed in BackSizer   
1891    mainSizer.Add(MaxSizer,0,wx.ALIGN_LEFT|wx.EXPAND|wx.ALL)
1892
1893    littleSizer = wx.FlexGridSizer(0,3,0,5)
1894    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper limits '),0,WACV)
1895    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][0]),style=wx.TE_READONLY)
1896    littleSizer.Add(Text,0,WACV)
1897    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1898    Text = wx.TextCtrl(G2frame.dataWindow,value=str(thresh[0][1]),style=wx.TE_READONLY)
1899    littleSizer.Add(Text,0,WACV)
1900    Text.SetBackgroundColour(VERY_LIGHT_GREY)
1901    littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' Lower/Upper thresholds '),0,WACV)
1902    lowerThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=0,
1903        min=thresh[0][0],OnLeave=newReplot,typeHint=int)
1904    littleSizer.Add(lowerThreshold,0,WACV)
1905    upperThreshold = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=thresh[1],key=1,
1906        max=thresh[0][1],OnLeave=newReplot,typeHint=int)
1907    littleSizer.Add(upperThreshold,0,WACV)
1908    mainSizer.Add(littleSizer,0,)
1909    if len(Spots):
1910        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Spot masks(on plot LB drag to move, shift-LB drag to resize, RB to delete)')
1911        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1912        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1913        colTypes = [wg.GRID_VALUE_STRING,wg.GRID_VALUE_FLOAT+':10,2',wg.GRID_VALUE_BOOL]
1914        colIds = ['position, mm','diameter, mm','Delete?']
1915        rowIds = [str(i) for i in range(len(Spots))]
1916        table = [['%.2f,%.2f'%(item[0],item[1]),item[2],False] for item in Spots]
1917        SpotTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
1918        SpotGrid = G2G.GSGrid(G2frame.dataWindow)
1919        SpotGrid.SetTable(SpotTable,True)
1920        SpotGrid.AutoSizeColumns(True)
1921        SpotGrid.SetColSize(1,80)
1922        for r in range(len(Spots)):
1923            SpotGrid.SetCellStyle(r,0,VERY_LIGHT_GREY,True)
1924        if 'phoenix' in wx.version():
1925            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnSpotChange)
1926        else:
1927            SpotGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnSpotChange)
1928        mainSizer.Add(SpotGrid,0,)
1929    if Rings:
1930        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Ring masks')
1931        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1932        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1933        littleSizer = wx.FlexGridSizer(0,3,0,5)
1934        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
1935        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1936        littleSizer.Add((5,0),0)
1937        for i in range(len(Rings)):
1938            if Rings[i]:
1939                ringText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (Rings[i][0])),
1940                    style=wx.TE_READONLY)
1941                ringText.SetBackgroundColour(VERY_LIGHT_GREY)
1942                ringText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1943                littleSizer.Add(ringText,0,WACV)
1944                ringThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Rings[i],key=1,
1945                    min=0.001,max=1.,OnLeave=Replot,nDig=[8,3])
1946                littleSizer.Add(ringThick,0,WACV)
1947                ringDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1948                    locationcode='Delete+Rings+'+str(i),handler=onDeleteMask)
1949                littleSizer.Add(ringDelete,0,WACV)
1950        mainSizer.Add(littleSizer,0,)
1951    if Arcs:
1952        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Arc masks')
1953        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1954        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1955        littleSizer = wx.FlexGridSizer(0,4,0,5)
1956        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' 2-theta,deg'),0,WACV)
1957        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' azimuth, deg'),0,WACV)
1958        littleSizer.Add(wx.StaticText(parent=G2frame.dataWindow,label=' thickness, deg'),0,WACV)
1959        littleSizer.Add((5,0),0)
1960        for i in range(len(Arcs)):
1961            if Arcs[i]:
1962                tth,azimuth,thick = Arcs[i]
1963                arcText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%.3f" % (tth)),
1964                    style=wx.TE_READONLY)
1965                arcText.SetBackgroundColour(VERY_LIGHT_GREY)
1966                arcText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1967                littleSizer.Add(arcText,0,WACV)
1968                azmText = wx.TextCtrl(parent=G2frame.dataWindow,value=("%d,%d" % (azimuth[0],azimuth[1])),
1969                    style=wx.TE_READONLY)
1970                azmText.SetBackgroundColour(VERY_LIGHT_GREY)
1971                azmText.Bind(wx.EVT_ENTER_WINDOW,OnTextMsg)
1972                littleSizer.Add(azmText,0,WACV)
1973                arcThick = G2G.ValidatedTxtCtrl(G2frame.dataWindow,loc=Arcs[i],key=2,
1974                    min=0.001,max=20.,OnLeave=Replot,nDig=[8,3])
1975                littleSizer.Add(arcThick,0,WACV)
1976                arcDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1977                    locationcode='Delete+Arcs+'+str(i),handler=onDeleteMask)
1978                littleSizer.Add(arcDelete,0,WACV)
1979        mainSizer.Add(littleSizer,0,)
1980       
1981    if Xlines:
1982        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' X line masks')
1983        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1984        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1985        littleSizer = wx.FlexGridSizer(0,2,0,5)
1986        for i in range(len(Xlines)):
1987            if Xlines[i]:
1988                littleSizer.Add(wx.StaticText(G2frame.dataWindow,label='at Y-pixel: %d'%(Xlines[i])),0,WACV)
1989                xlineDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
1990                    locationcode='Delete+Xlines+'+str(i),handler=onDeleteMask)
1991                littleSizer.Add(xlineDelete,0,WACV)
1992        mainSizer.Add(littleSizer,0,)
1993       
1994    if Ylines:
1995        lbl = wx.StaticText(parent=G2frame.dataWindow,label=' Y line masks')
1996        lbl.SetBackgroundColour(wx.Colour(200,200,210))
1997        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
1998        littleSizer = wx.FlexGridSizer(0,2,0,5)
1999        for i in range(len(Ylines)):
2000            if Ylines[i]:
2001                littleSizer.Add(wx.StaticText(G2frame.dataWindow,label='at X-pixel: %d'%(Ylines[i])),0,WACV)
2002                ylineDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
2003                    locationcode='Delete+Ylines+'+str(i),handler=onDeleteMask)
2004                littleSizer.Add(ylineDelete,0,WACV)
2005        mainSizer.Add(littleSizer,0,)
2006       
2007    if Polygons:
2008        lbl = wx.StaticText(parent=G2frame.dataWindow,
2009            label=' Polygon masks (on plot LB vertex drag to move, RB vertex drag to insert)')
2010        lbl.SetBackgroundColour(wx.Colour(200,200,210))
2011        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
2012        littleSizer = wx.FlexGridSizer(0,2,0,5)
2013        for i in range(len(Polygons)):
2014            if Polygons[i]:
2015                polyList = []
2016                for x,y in Polygons[i]:
2017                    polyList.append("%.2f, %.2f"%(x,y))
2018                polyText = wx.ComboBox(G2frame.dataWindow,value=polyList[0],choices=polyList,style=wx.CB_READONLY)
2019                littleSizer.Add(polyText,0,WACV)
2020                polyDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
2021                    locationcode='Delete+Polygons+'+str(i),handler=onDeleteMask)
2022                littleSizer.Add(polyDelete,0,WACV)
2023        mainSizer.Add(littleSizer,0,)
2024    if frame:
2025        lbl = wx.StaticText(parent=G2frame.dataWindow,
2026            label=' Frame mask (on plot LB vertex drag to move, RB vertex drag to insert)')
2027        lbl.SetBackgroundColour(wx.Colour(200,200,210))
2028        mainSizer.Add(lbl,0,wx.EXPAND|wx.ALIGN_CENTER,0)
2029        littleSizer = wx.FlexGridSizer(0,2,0,5)
2030        frameList = []
2031        for x,y in frame:
2032            frameList.append("%.2f, %.2f"%(x,y))
2033        frameText = wx.ComboBox(G2frame.dataWindow,value=frameList[0],choices=frameList,style=wx.CB_READONLY)
2034        littleSizer.Add(frameText,0,WACV)
2035        frameDelete = G2G.G2LoggedButton(G2frame.dataWindow,label='delete?',
2036            locationcode='Delete+Frame',handler=onDeleteFrame)
2037        littleSizer.Add(frameDelete,0,WACV)
2038        mainSizer.Add(littleSizer,0,)
2039    G2frame.dataWindow.SetDataSize()
2040    if startScroll: # reset scroll to saved position
2041        G2frame.dataWindow.Scroll(0,startScroll) # set to saved scroll position
2042        wx.Yield()
2043
2044################################################################################
2045##### Stress/Strain
2046################################################################################
2047
2048def UpdateStressStrain(G2frame,data):
2049    '''Shows and handles the controls on the "Stress/Strain"
2050    data tree entry
2051    '''
2052   
2053    def OnAppendDzero(event):
2054        data['d-zero'].append({'Dset':1.0,'Dcalc':0.0,'pixLimit':10,'cutoff':1.0,
2055            'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]],'Emat':[1.0,1.0,1.0],'fixDset':False})
2056        UpdateStressStrain(G2frame,data)
2057       
2058    def OnUpdateDzero(event):
2059        for item in data['d-zero']:
2060            if item['Dcalc']:   #skip unrefined ones
2061                item['Dset'] = item['Dcalc']
2062        UpdateStressStrain(G2frame,data)
2063           
2064    def OnCopyStrSta(event):
2065        Names = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
2066        if len(Names) == 1:
2067            G2frame.ErrorDialog('Nothing to copy controls to','There must be more than one "IMG" pattern')
2068            return
2069        Source = G2frame.GPXtree.GetItemText(G2frame.Image)
2070        Names.pop(Names.index(Source))
2071        dlg = G2G.G2MultiChoiceDialog(G2frame,'Copy stress/strain controls','Copy controls from '+Source+' to:',Names)
2072        try:
2073            if dlg.ShowModal() == wx.ID_OK:
2074                items = dlg.GetSelections()
2075                for item in items:
2076                    name = Names[item]
2077                    Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
2078                    CId = G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain')
2079                    oldData = G2frame.GPXtree.GetItemPyData(CId)
2080                    load = oldData.get('Sample load',0.0)
2081                    Data = copy.deepcopy(data)
2082                    Data['Sample load'] = load
2083                    G2frame.GPXtree.SetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'),Data)
2084        finally:
2085            dlg.Destroy()
2086            G2frame.GPXtree.SelectItem(G2frame.PickId)
2087
2088    def OnLoadStrSta(event):
2089        pth = G2G.GetImportPath(G2frame)
2090        if not pth: pth = '.'
2091        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
2092            'image control files (*.strsta)|*.strsta',wx.FD_OPEN)
2093        try:
2094            if dlg.ShowModal() == wx.ID_OK:
2095                filename = dlg.GetPath()
2096                File = open(filename,'r')
2097                S = File.read()
2098                data = eval(S)
2099                Controls = G2frame.GPXtree.GetItemPyData(
2100                    G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2101                G2img.FitStrSta(G2frame.ImageZ,data,Controls)
2102                UpdateStressStrain(G2frame,data)
2103                G2plt.PlotExposedImage(G2frame,event=event)
2104                G2plt.PlotStrain(G2frame,data,newPlot=True)
2105                File.close()
2106        finally:
2107            dlg.Destroy()
2108
2109    def OnSaveStrSta(event):
2110        pth = G2G.GetExportPath(G2frame)
2111        dlg = wx.FileDialog(G2frame, 'Choose stress/strain file', pth, '', 
2112            'image control files (*.strsta)|*.strsta',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2113        try:
2114            if dlg.ShowModal() == wx.ID_OK:
2115                filename = dlg.GetPath()
2116                File = open(filename,'w')
2117                keys = ['Type','Sample phi','Sample z','Sample load']
2118                keys2 = ['Dset','Dcalc','pixLimit','cutoff','Emat','fixDset']
2119                File.write('{\n\t')
2120                for key in keys:
2121                    if key in 'Type':
2122                        File.write("'"+key+"':'"+data[key]+"',")
2123                    else:
2124                        File.write("'"+key+"':"+str(data[key])+',')
2125                File.write('\n\t'+"'d-zero':[\n")
2126                for data2 in data['d-zero']:
2127                    File.write('\t\t{')
2128                    for key in keys2:
2129                        File.write("'"+key+"':"+str(data2[key])+',')
2130                    File.write("'ImxyObs':[[],[]],'ImtaObs':[[],[]],'ImtaCalc':[[],[]]},\n")
2131                File.write('\t]\n}')
2132                File.close()
2133        finally:
2134            dlg.Destroy()
2135           
2136    def OnStrStaSample(event):
2137        filename = ''
2138        pth = G2G.GetImportPath(G2frame)
2139        if not pth: pth = '.'
2140        dlg = wx.FileDialog(G2frame, 'Choose multihistogram metadata text file', pth, '', 
2141            'metadata file (*.*)|*.*',wx.FD_OPEN)
2142        try:
2143            if dlg.ShowModal() == wx.ID_OK:
2144                filename = dlg.GetPath()
2145                File = open(filename,'r')
2146                S = File.readline()
2147                newItems = []
2148                itemNames = []
2149                Comments = []
2150                while S:
2151                    if S[0] == '#':
2152                        Comments.append(S)
2153                        S = File.readline()
2154                        continue
2155                    S = S.replace(',',' ').replace('\t',' ')
2156                    Stuff = S[:-1].split()
2157                    itemNames.append(Stuff[0])
2158                    newItems.append(Stuff[1:])
2159                    S = File.readline()               
2160                File.close()
2161        finally:
2162            dlg.Destroy()
2163        if not filename:
2164            G2frame.ErrorDialog('Nothing to do','No file selected')
2165            return
2166        dataDict = dict(zip(itemNames,newItems))
2167        ifany = False
2168        Names = [' ','Sample phi','Sample z','Sample load']
2169        dlg = G2G.G2ColumnIDDialog( G2frame,' Choose multihistogram metadata columns:',
2170            'Select columns',Comments,Names,np.array(newItems).T)
2171        try:
2172            if dlg.ShowModal() == wx.ID_OK:
2173                colNames,newData = dlg.GetSelection()
2174                dataDict = dict(zip(itemNames,newData.T))
2175                for item in colNames:
2176                    if item != ' ':
2177                        ifany = True
2178        finally:
2179            dlg.Destroy()
2180        if not ifany:
2181            G2frame.ErrorDialog('Nothing to do','No columns identified')
2182            return
2183        histList = []
2184        item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)       
2185        while item:
2186            name = G2frame.GPXtree.GetItemText(item)
2187            if name.startswith('IMG'):
2188                histList.append(name)
2189            item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
2190        colIds = {}
2191        for i,name in enumerate(colNames):
2192            if name != ' ':
2193                colIds[name] = i
2194        for hist in histList:
2195            name = hist.split()[1]  #this is file name
2196            if name in dataDict:
2197                newItems = {}
2198                for item in colIds:
2199                    newItems[item] = float(dataDict[name][colIds[item]])
2200                Id = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,hist)
2201                stsrData = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id,'Stress/Strain'))
2202                stsrData.update(newItems)       
2203        UpdateStressStrain(G2frame,data)       
2204   
2205    def OnPlotStrSta(event):
2206        Controls = G2frame.GPXtree.GetItemPyData(
2207            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2208        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
2209        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
2210        G2plt.PlotExposedImage(G2frame,event=event)
2211        G2frame.G2plotNB.Delete('Ring Intensities')
2212        G2plt.PlotXY(G2frame,RingInt,labelX='Azimuth',
2213            labelY='MRD',newPlot=True,Title='Ring Intensities',
2214            names=Names,lines=True)
2215       
2216    def OnSaveStrRing(event):
2217        Controls = G2frame.GPXtree.GetItemPyData(
2218            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2219        RingInt = G2img.IntStrSta(G2frame.ImageZ,data,Controls)
2220        Names = ['d=%.3f'%(ring['Dcalc']) for ring in data['d-zero']]
2221        pth = G2G.GetExportPath(G2frame)
2222        dlg = wx.FileDialog(G2frame, 'Choose strain ring intensity file', pth, '', 
2223            'ring intensity file (*.txt)|*.txt',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2224        try:
2225            if dlg.ShowModal() == wx.ID_OK:
2226                filename = dlg.GetPath()
2227                File = open(filename,'w')
2228                for i,name in enumerate(Names):
2229                    File.write('%s%s\n'%(' Ring intensity for ',name))
2230                    File.write('%12s %12s\n'%('Azimuth','RMD'))
2231                    for item in RingInt[i].T:
2232                        File.write(' %12.3f %12.3f\n'%(item[0],item[1]))
2233                    File.write('\n')
2234                File.close()
2235        finally:
2236            dlg.Destroy()
2237               
2238               
2239    def OnFitStrSta(event):
2240        Controls = G2frame.GPXtree.GetItemPyData(
2241            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls'))
2242        G2img.FitStrSta(G2frame.ImageZ,data,Controls)
2243        print ('Strain fitting finished')
2244        UpdateStressStrain(G2frame,data)
2245        G2plt.PlotExposedImage(G2frame,event=event)
2246        G2plt.PlotStrain(G2frame,data,newPlot=True)
2247       
2248    def OnFitAllStrSta(event):
2249        choices = G2gd.GetGPXtreeDataNames(G2frame,['IMG ',])
2250        od = {'label_1':'Copy to next','value_1':False,'label_2':'Reverse order','value_2':False}
2251        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices,extraOpts=od)
2252        names = []
2253        if dlg.ShowModal() == wx.ID_OK:
2254            for sel in dlg.GetSelections():
2255                names.append(choices[sel])
2256            Id =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Sequential strain fit results')
2257            if Id:
2258                SeqResult = G2frame.GPXtree.GetItemPyData(Id)
2259            else:
2260                SeqResult = {}
2261                Id = G2frame.GPXtree.AppendItem(parent=G2frame.root,text='Sequential strain fit results')
2262            SeqResult.update({'SeqPseudoVars':{},'SeqParFitEqList':[]})
2263        else:
2264            dlg.Destroy()
2265            return
2266        dlg.Destroy()
2267        if not names:
2268            return
2269        dlg = wx.ProgressDialog('Sequential IMG Strain fit','Data set name = '+names[0],len(names), 
2270            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT)         
2271        wx.BeginBusyCursor()
2272        goodnames = []
2273        if od['value_2']:
2274            names.reverse()
2275        try:
2276            varyList = []
2277            variables = []
2278            for i,name in enumerate(names):
2279                print (' Sequential strain fit for '+name)
2280                GoOn = dlg.Update(i,newmsg='Data set name = '+name)[0]
2281                if not GoOn:
2282                    break
2283                sId =  G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name)
2284                G2frame.Image = sId
2285                Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Image Controls'))
2286                StaCtrls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,sId, 'Stress/Strain'))
2287                if not len(StaCtrls['d-zero']):
2288                    continue
2289                goodnames.append(name)
2290                Npix,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(sId)
2291                image = GetImageZ(G2frame,Controls)
2292                sig = []
2293                if i and od['value_1']:
2294                    for j,ring in enumerate(StaCtrls['d-zero']):
2295                        ring['Emat'] = copy.copy(variables[4*j:4*j+3])
2296                varyList = []
2297                variables = []
2298                #get results from previous & put in StaCtrls
2299                G2img.FitStrSta(image,StaCtrls,Controls)
2300                G2plt.PlotStrain(G2frame,StaCtrls,newPlot=True)
2301                parmDict = {'Sample load':StaCtrls['Sample load'],}
2302                varyNames = ['e11','e12','e22']
2303                Nvar = 5*len(StaCtrls['d-zero'])
2304                coVar = np.zeros((Nvar,Nvar))
2305                for j,item in enumerate(StaCtrls['d-zero']):
2306                    variables += item['Emat']
2307                    sig += item['Esig']
2308                    varylist = ['%d;%s'%(j,Name) for Name in varyNames]
2309                    varyList += varylist
2310                    parmDict.update(dict(zip(varylist,item['Emat'])))
2311                    parmDict['%d;Dcalc'%(j)] = item['Dcalc']
2312                    variables.append(1.e6*(item['Dcalc']/item['Dset']-1.))
2313                    varyList.append('%d;h-mstrain'%(j))
2314                    sig.append(0)
2315                    parmDict['%d;Ivar'%(j)] = item['Ivar']
2316                    variables.append(item['Ivar'])
2317                    varyList.append('%d;Ivar'%(j))
2318                    sig.append(0)
2319                    j4 = j*5
2320                    coVar[j4:j4+3,j4:j4+3] = item['covMat']
2321                SeqResult[name] = {'variables':variables,'varyList':varyList,'sig':sig,'Rvals':[],
2322                    'covMatrix':coVar,'title':name,'parmDict':parmDict}
2323            else:
2324                SeqResult['histNames'] = goodnames
2325                dlg.Destroy()
2326                print (' ***** Sequential strain refinement successful *****')
2327        finally:
2328            wx.EndBusyCursor()   
2329        SeqResult['histNames'] = choices
2330        G2frame.GPXtree.SetItemPyData(Id,SeqResult)
2331        G2frame.GPXtree.SelectItem(Id)
2332        print ('All images fitted')
2333       
2334    def SamSizer():
2335       
2336        def OnStrainType(event):
2337            data['Type'] = strType.GetValue()
2338       
2339        samSizer = wx.FlexGridSizer(0,4,0,5)
2340        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Strain type: '),0,WACV)
2341        strType = wx.ComboBox(G2frame.dataWindow,value=data['Type'],choices=['True','Conventional'],
2342            style=wx.CB_READONLY|wx.CB_DROPDOWN)
2343        strType.SetValue(data['Type'])
2344        strType.Bind(wx.EVT_COMBOBOX, OnStrainType)
2345        samSizer.Add(strType,0,WACV)
2346        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample phi: '),0,WACV)
2347        samPhi = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample phi',nDig=(10,3),typeHint=float,min=-360.,max=360.)
2348        samSizer.Add(samPhi,0,WACV)
2349        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample delta-z(mm): '),0,WACV)
2350        samZ = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample z',nDig=(10,3),typeHint=float)
2351        samSizer.Add(samZ,0,WACV)
2352        samSizer.Add(wx.StaticText(G2frame.dataWindow,-1,label=' Sample load(MPa): '),0,WACV)
2353        samLoad = G2G.ValidatedTxtCtrl(G2frame.dataWindow,data,'Sample load',
2354                nDig=[8,3],typeHint=float,)
2355        samSizer.Add(samLoad,0,WACV)
2356
2357        return samSizer
2358       
2359    def DzeroSizer():
2360               
2361        def OnStrainChange(event):
2362#            print (event)
2363            r,c = event.GetRow(),event.GetCol()
2364            if c == 0:
2365                data['d-zero'][r]['Dset'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.25),20.)
2366            elif c == 1:
2367                data['d-zero'][r]['fixDset'] = bool(StrainGrid.GetCellValue(r,c))
2368            elif c == 3:
2369                data['d-zero'][r]['cutoff'] = min(max(float(StrainGrid.GetCellValue(r,c)),0.5),20.)
2370            elif c == 4:
2371                data['d-zero'][r]['pixLimit'] = int(StrainGrid.GetCellValue(r,c))
2372            elif c == 8:
2373                del data['d-zero'][r]
2374                StrainTable.DeleteRow(r)
2375                wx.CallAfter(UpdateStressStrain,G2frame,data)
2376            G2plt.PlotExposedImage(G2frame,event=event)
2377            G2plt.PlotStrain(G2frame,data,newPlot=True)
2378           
2379        def OnSetCol(event):
2380            c = event.GetCol()
2381            if c == 1:
2382                StrainGrid.ClearSelection()
2383                StrainGrid.SelectCol(c,True)
2384                choice = ['Y - set all','N - set none',]
2385                dlg = wx.SingleChoiceDialog(G2frame,'Select option for '+StrainGrid.GetColLabelValue(c-1),
2386                    'Strain controls',choice)
2387                dlg.CenterOnParent()
2388                if dlg.ShowModal() == wx.ID_OK:
2389                    sel = dlg.GetSelection()
2390                    if sel == 0:
2391                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=True
2392                    else:
2393                        for row in range(StrainGrid.GetNumberRows()): data['d-zero'][row]['fixDset']=False
2394                wx.CallAfter(UpdateStressStrain,G2frame,data)
2395           
2396        colTypes = [wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_BOOL,wg.GRID_VALUE_FLOAT+':10,5',wg.GRID_VALUE_FLOAT+':10,1',
2397            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',]
2398        colIds = ['d-zero','Poisson\n mean?','d-zero ave','I/Ib','nPix','e11','e12','e22','Delete?','h-mustrain','Ivar']
2399        rowIds = [str(i) for i in range(len(data['d-zero']))]
2400        table = [[item['Dset'],item.get('fixDset',False),item['Dcalc'],item['cutoff'],item['pixLimit'], 
2401            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']]
2402        StrainTable = G2G.Table(table,rowLabels=rowIds,colLabels=colIds,types=colTypes)
2403        StrainGrid = G2G.GSGrid(G2frame.dataWindow)
2404        StrainGrid.SetTable(StrainTable,True)
2405        StrainGrid.AutoSizeColumns(True)
2406        for r in range(len(data['d-zero'])):
2407            StrainGrid.SetCellStyle(r,2,VERY_LIGHT_GREY,True)
2408            StrainGrid.SetCellStyle(r,5,VERY_LIGHT_GREY,True)
2409            StrainGrid.SetCellStyle(r,6,VERY_LIGHT_GREY,True)
2410            StrainGrid.SetCellStyle(r,7,VERY_LIGHT_GREY,True)
2411            StrainGrid.SetCellStyle(r,9,VERY_LIGHT_GREY,True)
2412            StrainGrid.SetCellStyle(r,10,VERY_LIGHT_GREY,True)
2413        if 'phoenix' in wx.version():
2414            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGED, OnStrainChange)
2415        else:
2416            StrainGrid.Bind(wg.EVT_GRID_CELL_CHANGE, OnStrainChange)
2417        StrainGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnSetCol)
2418        return StrainGrid
2419# patches
2420    if 'Sample load' not in data:
2421        data['Sample load'] = 0.0
2422# end patches
2423   
2424    G2frame.dataWindow.ClearData()
2425    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.StrStaMenu)
2426    G2frame.Bind(wx.EVT_MENU, OnAppendDzero, id=G2G.wxID_APPENDDZERO)
2427    G2frame.Bind(wx.EVT_MENU, OnUpdateDzero, id=G2G.wxID_UPDATEDZERO)
2428    G2frame.Bind(wx.EVT_MENU, OnFitStrSta, id=G2G.wxID_STRSTAFIT)
2429    G2frame.Bind(wx.EVT_MENU, OnPlotStrSta, id=G2G.wxID_STRSTAPLOT) 
2430    G2frame.Bind(wx.EVT_MENU, OnSaveStrRing, id=G2G.wxID_STRRINGSAVE) 
2431    G2frame.Bind(wx.EVT_MENU, OnFitAllStrSta, id=G2G.wxID_STRSTAALLFIT)
2432    G2frame.Bind(wx.EVT_MENU, OnCopyStrSta, id=G2G.wxID_STRSTACOPY)
2433    G2frame.Bind(wx.EVT_MENU, OnLoadStrSta, id=G2G.wxID_STRSTALOAD)
2434    G2frame.Bind(wx.EVT_MENU, OnSaveStrSta, id=G2G.wxID_STRSTASAVE)
2435    G2frame.Bind(wx.EVT_MENU, OnStrStaSample, id=G2G.wxID_STRSTSAMPLE)       
2436    if G2frame.StrainKey == 'a':    #probably doesn't happen
2437        G2frame.GetStatusBar().SetStatusText('Add strain ring active - LB pick d-zero value',1)
2438    else:
2439        G2frame.GetStatusBar().SetStatusText("To add strain data: On 2D Powder Image, key a:add ring",1)
2440       
2441    mainSizer = G2frame.dataWindow.GetSizer()
2442    mainSizer.Add((5,10),0)
2443    mainSizer.Add(SamSizer())
2444    mainSizer.Add((5,10),0)
2445    mainSizer.Add(DzeroSizer())
2446    G2frame.dataWindow.SetDataSize()
2447   
2448###########################################################################
2449# Autointegration
2450###########################################################################
2451def ReadMask(filename):
2452    'Read a mask (.immask) file'
2453    File = open(filename,'r')
2454    save = {}
2455    S = File.readline()
2456    while S:
2457        if S[0] == '#':
2458            S = File.readline()
2459            continue
2460        [key,val] = S.strip().split(':',1)
2461        if key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
2462            save[key] = eval(val)
2463        S = File.readline()
2464    File.close()
2465    CleanupMasks(save)
2466    return save
2467
2468def ReadControls(filename):
2469    'read an image controls (.imctrl) file'
2470    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2471            'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2472            'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2473            'PolaVal','SampleAbs','dark image','background image']
2474    File = open(filename,'r')
2475    save = {}
2476    S = File.readline()
2477    while S:
2478        if S[0] == '#':
2479            S = File.readline()
2480            continue
2481        [key,val] = S.strip().split(':',1)
2482        if key in ['type','calibrant','binType','SampleShape',]:    #strings
2483            save[key] = val
2484        elif key in ['rotation']:
2485            save[key] = float(val)
2486        elif key in ['center',]:
2487            if ',' in val:
2488                save[key] = eval(val)
2489            else:
2490                vals = val.strip('[] ').split()
2491                save[key] = [float(vals[0]),float(vals[1])] 
2492        elif key in cntlList:
2493            save[key] = eval(val)
2494        S = File.readline()
2495    File.close()
2496    return save
2497
2498def Read_imctrl(imctrl_file):
2499    '''Read an image control file and record control parms into a dict, with some simple
2500    type conversions
2501    '''
2502    save = {'filename':imctrl_file}
2503    immask_file = os.path.splitext(imctrl_file)[0]+'.immask'
2504    if os.path.exists(immask_file):
2505        save['maskfile'] = immask_file
2506    else:
2507        save['maskfile'] = '(none)'
2508    cntlList = ['wavelength','distance','tilt','invert_x','invert_y','type',
2509                        'fullIntegrate','outChannels','outAzimuths','LRazimuth','IOtth','azmthOff','DetDepth',
2510                        'calibskip','pixLimit','cutoff','calibdmin','Flat Bkg',
2511                        'PolaVal','SampleAbs','dark image','background image','setdist']
2512    File = open(imctrl_file,'r')
2513    fullIntegrate = False
2514    try:
2515        S = File.readline()
2516        while S:
2517            if S[0] == '#':
2518                S = File.readline()
2519                continue
2520            [key,val] = S.strip().split(':',1)
2521            if val.find(':') != -1:
2522                #print 'rejecting ',key,val
2523                S = File.readline()
2524                continue
2525            if key in ['type','calibrant','binType','SampleShape',]:    #strings
2526                save[key] = val
2527            elif key == 'rotation':
2528                save[key] = float(val)
2529            elif key == 'fullIntegrate':
2530                fullIntegrate = eval(val)
2531            elif key == 'LRazimuth':
2532                vals = eval(val)
2533                save['LRazimuth_min'] = float(vals[0])
2534                save['LRazimuth_max'] = float(vals[1])
2535            elif key == 'IOtth':
2536                save['IOtth_min'],save['IOtth_max'] = eval(val)[0:2]
2537            elif key == 'center':
2538                if ',' in val:
2539                    vals = eval(val)
2540                else:
2541                    vals = val.strip('[] ').split()
2542                    vals = [float(vals[0]),float(vals[1])] 
2543                save['center_x'],save['center_y'] = vals[0:2]
2544            elif key in cntlList:
2545                save[key] = eval(val)
2546            S = File.readline()
2547    finally:
2548        File.close()
2549        if fullIntegrate: save['LRazimuth_min'],save['LRazimuth_max'] = 0.,360.
2550    return save
2551   
2552class AutoIntFrame(wx.Frame):
2553    '''Creates a wx.Frame window for the Image AutoIntegration.
2554    The intent is that this will be used as a non-modal dialog window.
2555   
2556    Implements a Start button that morphs into a pause and resume button.
2557    This button starts a processing loop that is repeated every
2558    :meth:`PollTime` seconds.
2559
2560    :param wx.Frame G2frame: main GSAS-II frame
2561    :param float PollTime: frequency in seconds to repeat calling the
2562      processing loop. (Default is 30.0 seconds.)
2563    '''
2564    def __init__(self,G2frame,PollTime=30.0):
2565        def OnStart(event):
2566            '''Called when the start button is pressed. Changes button label
2567            to Pause. When Pause is pressed the label changes to Resume.
2568            When either Start or Resume is pressed, the processing loop
2569            is started. When Pause is pressed, the loop is stopped.
2570            '''
2571            # check inputs for errors before starting
2572            #err = ''
2573            #if not any([self.params[fmt] for fmt in self.fmtlist]):
2574            #    err += '\nPlease select at least one output format\n'
2575            #if err:
2576            #    G2G.G2MessageBox(self,err)
2577            #    return
2578            self.Pause = False
2579            # change button label
2580            if self.btnstart.GetLabel() != 'Pause':
2581                self.btnstart.SetLabel('Pause')
2582                self.Status.SetStatusText('Press Pause to delay integration or Reset to prepare to reintegrate all images')
2583                if self.timer.IsRunning(): self.timer.Stop()
2584                self.PreventReEntryTimer = False
2585                if self.StartLoop():
2586                    G2G.G2MessageBox(self,'Error in setting up integration. See console')
2587                    return
2588                self.OnTimerLoop(None) # run once immediately
2589                if not self.Pause:
2590                    # no pause, so start timer to check for new files
2591                    self.timer.Start(int(1000*PollTime),oneShot=False)
2592                    return
2593            # we will get to this point if Paused
2594            self.OnPause()
2595           
2596        def OnReset(event):
2597            '''Called when Reset button is pressed. This stops the
2598            processing loop and resets the list of integrated files so
2599            all images can be reintegrated.
2600            '''
2601            self.btnstart.SetLabel('Restart')
2602            self.Status.SetStatusText('Press Restart to reload and re-integrate images matching filter')
2603            if self.timer.IsRunning(): self.timer.Stop()
2604            self.Reset = True
2605            self.Pause = True
2606           
2607        def OnQuit(event):
2608            '''Stop the processing loop and close the Frame
2609            '''
2610            if self.timer.IsRunning(): self.timer.Stop() # make sure we stop first
2611            wx.CallAfter(self.Destroy)
2612           
2613        def OnBrowse(event):
2614            '''Responds when the Browse button is pressed to load a file.
2615            The routine determines which button was pressed and gets the
2616            appropriate file type and loads it into the appropriate place
2617            in the dict.
2618            '''
2619            if btn3 == event.GetEventObject():
2620                dlg = wx.DirDialog(
2621                    self, 'Select directory for output files',
2622                    self.params['outdir'],wx.DD_DEFAULT_STYLE)
2623                dlg.CenterOnParent()
2624                try:
2625                    if dlg.ShowModal() == wx.ID_OK:
2626                        self.params['outdir'] = dlg.GetPath()
2627                        fInp3.SetValue(self.params['outdir'])
2628                finally:
2629                    dlg.Destroy()
2630                return
2631            elif btn4 == event.GetEventObject():
2632                msg = ''
2633                pth = G2G.GetExportPath(G2frame)
2634                dlg = wx.FileDialog(
2635                    self, 'Select a PDF parameter file',
2636                    pth, self.params['pdfprm'], 
2637                    "PDF controls file (*.pdfprm)|*.pdfprm",
2638                    wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
2639                dlg.CenterOnParent()
2640                try:
2641                    if dlg.ShowModal() == wx.ID_OK:
2642                        self.params['pdfprm'] = dlg.GetPath()
2643                        fInp4.SetValue(self.params['pdfprm'])
2644                        scanPDFprm()
2645                        msg = self.checkPDFprm(True)
2646                finally:
2647                    dlg.Destroy()
2648                if 'Error' in msg:
2649                    print(msg)
2650                    lbl = 'PDFPRM error'
2651                else:
2652                    msg = 'Information from file {}\n\n{}'.format(self.params['pdfprm'],msg)
2653                    lbl = 'PDFPRM information'
2654                G2G.G2MessageBox(self,msg,lbl)
2655                return
2656               
2657        def OnRadioSelect(event):
2658            '''Respond to a radiobutton selection and when in table
2659            mode, get distance-dependent parameters from user.
2660            '''
2661            self.Evaluator = None
2662            if self.useTable.GetValue():
2663                dlg = None
2664                try:
2665                    dlg = IntegParmTable(self) # create the dialog
2666                    dlg.CenterOnParent()
2667                    if dlg.ShowModal() == wx.ID_OK:
2668                        self.ImgTblParms = dlg.parms
2669                        self.IMfileList = dlg.IMfileList
2670                        self.Evaluator = DefineEvaluator(dlg)
2671                        self.params['Mode'] = 'table'
2672                        self.editTable.Enable(True)
2673                    else:
2674                        self.useActive.SetValue(True)
2675                finally:
2676                    if dlg: dlg.Destroy()
2677            elif self.useActive.GetValue():
2678                self.params['Mode'] = 'active'
2679                self.imageBase = G2frame.Image
2680                self.useActive.SetLabel("Active Image: "+
2681                        G2frame.GPXtree.GetItemText(self.imageBase))
2682                self.editTable.Enable(False)
2683            else:
2684                print('unexpected mode in OnRadioSelect')
2685
2686        def OnEditTable(event):
2687            '''Called to edit the distance-dependent parameter look-up table.
2688            Should be called only when table is defined and active.
2689            '''
2690            dlg = None
2691            try:
2692                dlg = IntegParmTable(self,self.ImgTblParms,self.IMfileList)
2693                dlg.CenterOnParent()
2694                if dlg.ShowModal() == wx.ID_OK:
2695                    self.ImgTblParms = dlg.parms
2696                    self.IMfileList = dlg.IMfileList
2697                    self.Evaluator = DefineEvaluator(dlg)
2698                    self.params['Mode'] = 'table'
2699                    self.editTable.Enable(True)
2700                else:
2701                    self.useActive.SetValue(True)
2702                    self.params['Mode'] = 'active'
2703                    self.imageBase = G2frame.Image
2704                    self.useActive.SetLabel("Active Image: "+
2705                            G2frame.GPXtree.GetItemText(self.imageBase))
2706                    self.editTable.Enable(False)
2707            finally:
2708                if dlg: dlg.Destroy()
2709               
2710        def showPDFctrls(event):
2711            '''Called to show or hide AutoPDF widgets. Note that fInp4 must be included in the
2712            sizer layout with .Show(True) before .Show(False) will work properly.
2713            '''
2714            fInp4.Enable(self.params['ComputePDF'])
2715            fInp4.Show(self.params['ComputePDF'])
2716            fInp4a.Enable(self.params['ComputePDF'])
2717            btn4.Enable(self.params['ComputePDF'])
2718            cOpt.Enable(self.params['ComputePDF'])
2719            if self.params['ComputePDF']:
2720                lbl4.SetForegroundColour("black")
2721                lbl4a.SetForegroundColour("black")
2722            else:
2723                lbl4.SetForegroundColour("gray")
2724                lbl4a.SetForegroundColour("gray")
2725                                   
2726        def scanPDFprm(**kw):
2727            fInp4.invalid = not os.path.exists(fInp4.GetValue())
2728            fInp4._IndicateValidity()
2729           
2730        def OnAutoScale(event):
2731            self.AutoScale = autoscale.GetValue()
2732           
2733        def OnAutoScaleName(event):
2734            self.AutoScaleName = scalename.GetValue()
2735            self.Scale[0] = self.AutoScales[self.AutoScaleName]
2736            scaleval.SetValue(self.Scale[0])
2737               
2738        ##################################################
2739        # beginning of __init__ processing
2740        ##################################################
2741        self.G2frame = G2frame
2742        self.ImgTblParms = None
2743        self.IMfileList = None
2744        self.Evaluator = None
2745        self.params = {}
2746        self.Reset = False
2747        self.Pause = False
2748        self.PreventReEntryShowMatch = False
2749        self.PreventReEntryTimer = False
2750        self.params['IMGfile'] = ''
2751        self.params['MaskFile'] = ''
2752        self.params['IgnoreMask'] = True
2753        self.fmtlist = G2IO.ExportPowderList(G2frame)
2754        self.timer = wx.Timer()
2755        self.timer.Bind(wx.EVT_TIMER,self.OnTimerLoop)
2756        self.imageBase = G2frame.Image
2757        self.params['ComputePDF'] = False
2758        self.params['pdfDmax'] = 0.0
2759        self.params['pdfprm'] = ''
2760        self.params['optPDF'] = True
2761        self.pdfControls = {}
2762        self.AutoScale = False
2763        self.Scale = [1.0,]
2764
2765        G2frame.GPXtree.GetSelection()
2766        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(self.imageBase) 
2767        self.params['readdir'],fileroot = os.path.split(imagefile)
2768        self.params['filter'] = '*'+os.path.splitext(fileroot)[1]
2769        self.params['outdir'] = os.path.abspath(self.params['readdir'])
2770        Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
2771            G2frame,self.imageBase, 'Comments'))
2772        DefaultAutoScaleNames = GSASIIpath.GetConfigValue('Autoscale_ParmNames')
2773        self.AutoScaleName = GSASIIpath.GetConfigValue('DefaultAutoScale')
2774        self.AutoScales = {}
2775        if DefaultAutoScaleNames is not None:
2776            for comment in Comments:
2777                if '=' in comment:
2778                    name,val = comment.split('=',1) 
2779                    if name in DefaultAutoScaleNames:
2780                        try:
2781                            self.AutoScales[name] = float(val)
2782                            if name == self.AutoScaleName:
2783                                self.Scale[0] = float(val)
2784                        except ValueError:
2785                            continue
2786        wx.Frame.__init__(self, G2frame, title='Automatic Integration',
2787                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
2788        self.Status = self.CreateStatusBar()
2789        self.Status.SetStatusText('Press Start to load and integrate images matching filter')
2790        mnpnl = wx.Panel(self)
2791        mnsizer = wx.BoxSizer(wx.VERTICAL)
2792        # box for integration controls & masks input
2793        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Integration Control")
2794        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2795        lblsizr.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Use integration parameters from:'))
2796        self.useActive = wx.RadioButton(mnpnl, wx.ID_ANY, 
2797                            style = wx.RB_GROUP)
2798        self.useActive.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2799        self.useActive.SetLabel("Active Image: "+
2800                                G2frame.GPXtree.GetItemText(self.imageBase))
2801        lblsizr.Add(self.useActive,0,wx.EXPAND,1)
2802        self.useActive.SetValue(True)
2803        minisizer = wx.BoxSizer(wx.HORIZONTAL)
2804        self.useTable = wx.RadioButton(mnpnl, wx.ID_ANY, "From distance look-up table")
2805        minisizer.Add(self.useTable,0,wx.ALIGN_LEFT|wx.ALL,1)
2806        self.useTable.Bind(wx.EVT_RADIOBUTTON, OnRadioSelect)
2807        self.editTable = wx.Button(mnpnl,  wx.ID_ANY, "Edit table")
2808        minisizer.Add(self.editTable,0,wx.ALIGN_LEFT,10)
2809        self.editTable.Enable(False)
2810        self.editTable.Bind(wx.EVT_BUTTON, OnEditTable)
2811        # bind button and deactivate be default
2812        lblsizr.Add(minisizer)
2813        mnsizer.Add(lblsizr,0,wx.EXPAND,0)
2814
2815        # file filter stuff
2816        sizer = wx.BoxSizer(wx.HORIZONTAL)
2817        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Read images from '))
2818        self.readDir = G2G.ValidatedTxtCtrl(mnpnl,self.params,'readdir',
2819                            OnLeave=self.ShowMatchingFiles,size=(200,-1))
2820        sizer.Add(self.readDir,1,wx.EXPAND,1)
2821        btn3 = wx.Button(mnpnl, wx.ID_ANY, "Browse")
2822        btn3.Bind(wx.EVT_BUTTON, self.SetSourceDir)
2823        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2824        mnsizer.Add(sizer,0,wx.EXPAND,0)
2825        # not yet implemented
2826        sizer = wx.BoxSizer(wx.HORIZONTAL)
2827        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Keep read images in tree '))
2828        self.params['keepReadImage'] = True
2829        keepImage = G2G.G2CheckBox(mnpnl,'',self.params,'keepReadImage')
2830        sizer.Add(keepImage)
2831        keepImage.Enable(False)
2832        sizer.Add((-1,-1),1,wx.EXPAND,1)
2833        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'  Image filter'))
2834        flterInp = G2G.ValidatedTxtCtrl(mnpnl,self.params,'filter',
2835                                        OnLeave=self.ShowMatchingFiles)
2836        sizer.Add(flterInp)
2837        mnsizer.Add(sizer,0,wx.EXPAND,0)
2838       
2839        self.ListBox = wx.ListBox(mnpnl,size=(-1,100))
2840        mnsizer.Add(self.ListBox,1,wx.EXPAND,1)
2841        self.ShowMatchingFiles(self.params['filter'])
2842
2843        # box for output selections
2844        lbl = wx.StaticBox(mnpnl, wx.ID_ANY, "Output settings")
2845        lblsizr = wx.StaticBoxSizer(lbl, wx.VERTICAL)
2846        sizer = wx.BoxSizer(wx.HORIZONTAL)
2847        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Write to: '),0,wx.ALIGN_CENTER_VERTICAL)
2848        fInp3 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'outdir',notBlank=False,size=(300,-1))
2849        sizer.Add(fInp3,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2850        btn3 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2851        btn3.Bind(wx.EVT_BUTTON, OnBrowse)
2852        sizer.Add(btn3,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2853        lblsizr.Add(sizer,0,wx.EXPAND)
2854        sizer = wx.BoxSizer(wx.HORIZONTAL)
2855        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Select format(s):'))
2856        for dfmt in self.fmtlist:
2857            sizer.Add((6,2)) # add a bit of extra space
2858            fmt = dfmt[1:]
2859            self.params[fmt] = False
2860            btn = G2G.G2CheckBox(mnpnl,dfmt,self.params,fmt)
2861            sizer.Add(btn)
2862        lblsizr.Add(sizer)
2863        sizer = wx.BoxSizer(wx.HORIZONTAL)
2864        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Separate dir for each format: '))
2865        self.params['SeparateDir'] = False
2866        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'SeparateDir'))
2867        lblsizr.Add(sizer)
2868        if self.AutoScales:
2869            sizer = wx.BoxSizer(wx.HORIZONTAL)
2870            autoscale = wx.CheckBox(mnpnl,label='Do autoscaling with:')
2871            autoscale.Bind(wx.EVT_CHECKBOX,OnAutoScale)
2872            sizer.Add(autoscale,0,WACV)
2873            scalename = wx.ComboBox(mnpnl,value=self.AutoScaleName,choices=list(self.AutoScales.keys()),
2874                style=wx.CB_READONLY|wx.CB_DROPDOWN)
2875            scalename.Bind(wx.EVT_COMBOBOX,OnAutoScaleName)
2876            sizer.Add(scalename,0,WACV)
2877            sizer.Add(wx.StaticText(mnpnl,label=' to '),0,WACV)
2878            scaleval = G2G.ValidatedTxtCtrl(mnpnl,self.Scale,0,nDig=(10,2),min=1.)
2879            sizer.Add(scaleval,0,WACV)
2880            lblsizr.Add(sizer,0)
2881        #ToDO: Autonormalize, parm name?, scaling value?
2882        sizer = wx.BoxSizer(wx.HORIZONTAL)
2883        sizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'Autocompute PDF:'),0,wx.ALIGN_CENTER_VERTICAL)
2884        sizer.Add(G2G.G2CheckBox(mnpnl,'',self.params,'ComputePDF',OnChange=showPDFctrls))
2885        lbl4a = wx.StaticText(mnpnl, wx.ID_ANY,'Max detector distance: ')
2886        sizer.Add(lbl4a,0,wx.ALIGN_CENTER_VERTICAL)
2887        fInp4a = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfDmax',min=0.0)
2888        sizer.Add(fInp4a,0,wx.ALIGN_CENTER_VERTICAL)
2889        cOpt = G2G.G2CheckBox(mnpnl,'Optimize',self.params,'optPDF')
2890        sizer.Add(cOpt)
2891        lblsizr.Add(sizer,0)
2892        sizer = wx.BoxSizer(wx.HORIZONTAL)
2893        lbl4 = wx.StaticText(mnpnl, wx.ID_ANY,'PDF control: ')
2894        sizer.Add(lbl4,0,wx.ALIGN_CENTER_VERTICAL)
2895        fInp4 = G2G.ValidatedTxtCtrl(mnpnl,self.params,'pdfprm',notBlank=True,size=(300,-1),
2896                                     OnLeave=scanPDFprm)
2897        sizer.Add(fInp4,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2898        btn4 = wx.Button(mnpnl,  wx.ID_ANY, "Browse")
2899        btn4.Bind(wx.EVT_BUTTON, OnBrowse)
2900        sizer.Add(btn4,0,wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
2901        lblsizr.Add(sizer,0,wx.EXPAND)
2902        mnsizer.Add(lblsizr,0,wx.ALIGN_CENTER|wx.EXPAND,1)
2903        # buttons on bottom
2904        mnsizer.Add(wx.StaticText(mnpnl, wx.ID_ANY,'AutoIntegration controls'),0,wx.TOP,5)
2905        sizer = wx.BoxSizer(wx.HORIZONTAL)
2906        sizer.Add((20,-1))
2907        self.btnstart = wx.Button(mnpnl,  wx.ID_ANY, "Start")
2908        self.btnstart.Bind(wx.EVT_BUTTON, OnStart)
2909        sizer.Add(self.btnstart)
2910        self.btnreset = wx.Button(mnpnl,  wx.ID_ANY, "Reset")
2911        self.btnreset.Bind(wx.EVT_BUTTON, OnReset)
2912        sizer.Add(self.btnreset)
2913        sizer.Add((20,-1),wx.EXPAND,1)
2914        self.btnclose = wx.Button(mnpnl,  wx.ID_ANY, "Close")
2915        self.btnclose.Bind(wx.EVT_BUTTON, OnQuit)
2916        sizer.Add(self.btnclose)
2917        sizer.Add((20,-1))
2918        mnsizer.Add(sizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP,5)
2919        # finish up window
2920        mnpnl.SetSizer(mnsizer)
2921        OnRadioSelect(None) # disable widgets
2922        mnsizer.Fit(self)
2923        self.CenterOnParent()
2924        self.Show()
2925        showPDFctrls(None)
2926
2927    def checkPDFprm(self,ShowContents=False):
2928        '''Read in the PDF (.pdfprm) parameter file and check for problems.
2929        If ShowContents is True, a formatted text version of some of the file
2930        contents is returned. If errors are found, the return string will contain
2931        the string "Error:" at least once.
2932        '''
2933        self.pdfControls = {}
2934        msg = ''
2935        File = None
2936        try:
2937            File = open(self.params['pdfprm'],'r')
2938            S = File.readline()
2939            while S:
2940                if '#' in S:
2941                    S = File.readline()
2942                    continue
2943                key,val = S.split(':',1)
2944                try:
2945                    self.pdfControls[key] = eval(val)
2946                except:
2947                    self.pdfControls[key] = val
2948                S = File.readline()
2949        except Exception as err:
2950            msg += 'PDF Processing Error: error with open or read of {}'.format(self.params['pdfprm'])
2951            if GSASIIpath.GetConfigValue('debug'):
2952                print('DBG_'+msg)
2953                print('DBG_'+err)
2954            self.pdfControls = {}
2955            return msg
2956        finally:
2957            if File: File.close()
2958        formula = ''
2959        for el in self.pdfControls['ElList']:
2960            if self.pdfControls['ElList'][el]['FormulaNo'] <= 0: continue
2961            if formula: formula += ' '
2962            formula += '{}({:.1f})'.format(el,self.pdfControls['ElList'][el]['FormulaNo'])
2963        if not formula:
2964            msg += 'Error: no chemical formula in file'
2965        for key in ['Sample Bkg.','Container','Container Bkg.']:
2966            if key not in self.pdfControls:
2967                if msg: msg += '\n'
2968                msg += 'Error: missing key in self.pdfControls: '+key
2969                continue
2970        if msg or not ShowContents: return msg  # stop on error
2971        msg += 'Default formula: '+formula+'\n'
2972        for key in ['Sample Bkg.','Container','Container Bkg.']:
2973            name = self.pdfControls[key]['Name']
2974            mult = self.pdfControls[key].get('Mult',0.0)
2975            if not name: continue
2976            msg += '\n{}: {:.2f} * "{}"'.format(key,mult,name)
2977            if not G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,name):
2978                msg += ' *** missing ***'
2979        return msg
2980   
2981    def SetSourceDir(self,event):
2982        '''Use a dialog to get a directory for image files
2983        '''
2984        dlg = wx.DirDialog(self, 'Select directory for image files',
2985                        self.params['readdir'],wx.DD_DEFAULT_STYLE)
2986        dlg.CenterOnParent()
2987        try:
2988            if dlg.ShowModal() == wx.ID_OK:
2989                self.params['readdir'] = dlg.GetPath()
2990            self.readDir.SetValue(self.params['readdir'])
2991            self.ShowMatchingFiles(None)
2992        finally:
2993            dlg.Destroy()
2994        return
2995       
2996    def ShowMatchingFiles(self,value,invalid=False,**kwargs):
2997        '''Find and show images in the tree and the image files matching the image
2998        file directory (self.params['readdir']) and the image file filter
2999        (self.params['filter']) and add this information to the GUI list box
3000        '''
3001        G2frame = self.G2frame
3002        if invalid: return
3003        msg = ''
3004        if self.PreventReEntryShowMatch: return
3005        self.PreventReEntryShowMatch = True
3006        imageFileList = []
3007        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3008            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3009            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3010            if imagefile not in imageFileList: imageFileList.append(imagefile)
3011            if img not in G2frame.IntegratedList:
3012                if msg: msg += '\n'
3013                msg += '  ' + img
3014        if msg: msg = "Loaded images to integrate:\n" + msg + "\n"
3015        msg1 = ""
3016        try:
3017            if os.path.exists(self.params['readdir']): 
3018                imageList = sorted(
3019                    glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
3020                if not imageList:
3021                    msg1 = 'Warning: No files match search string '+os.path.join(self.params['readdir'],self.params['filter'])
3022                else:
3023                    for fil in imageList:
3024                        if fil not in imageFileList: msg1 += '\n  '+fil
3025                    if msg1:
3026                        msg += 'Files to integrate from '+os.path.join(self.params['readdir'],self.params['filter'])+msg1
3027                    else:
3028                        msg += 'No files found to read in '+self.params['readdir']
3029            else:
3030                msg += 'Warning: does not exist: '+self.params['readdir']
3031        except IndexError:
3032            msg += 'Error searching for files named '+os.path.join(self.params['readdir'],self.params['filter'])
3033        self.ListBox.Clear()
3034        self.ListBox.AppendItems(msg.split('\n'))
3035        self.PreventReEntryShowMatch = False
3036        return
3037       
3038    def OnPause(self):
3039        '''Respond to Pause, changes text on button/Status line, if needed
3040        Stops timer
3041        self.Pause should already be True
3042        '''
3043        if self.timer.IsRunning(): self.timer.Stop()
3044        if self.btnstart.GetLabel() == 'Restart':
3045            return
3046        if self.btnstart.GetLabel() != 'Resume':
3047            print('\nPausing autointegration\n')
3048            self.btnstart.SetLabel('Resume')
3049            self.Status.SetStatusText(
3050                    'Press Resume to continue integration or Reset to prepare to reintegrate all images')
3051        self.Pause = True
3052           
3053    def IntegrateImage(self,img,useTA=None,useMask=None):
3054        '''Integrates a single image. Ids for created PWDR entries (more than one is possible)
3055        are placed in G2frame.IntgOutList
3056        '''
3057        G2frame = self.G2frame
3058        imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3059        G2frame.Image = imgId
3060        G2frame.PickId = G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Image Controls')
3061        # do integration
3062        size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3063        if self.AutoScale:
3064            Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3065                G2frame,imgId, 'Comments'))
3066            for comment in Comments:
3067                if '=' in comment:
3068                    name,val = comment.split('=',1) 
3069                    if name == self.AutoScaleName:
3070                        val = float(val)
3071                        if val > 0.:
3072                            Scale = self.Scale[0]/val
3073                        break
3074        masks = G2frame.GPXtree.GetItemPyData(
3075            G2gd.GetGPXtreeItemId(G2frame,G2frame.Image, 'Masks'))
3076        data = G2frame.GPXtree.GetItemPyData(G2frame.PickId)
3077        # simulate a Image Controls press, since that is where the
3078        # integration is hidden
3079        UpdateImageControls(G2frame,data,masks,useTA=useTA,useMask=useMask,IntegrateOnly=True)
3080        G2frame.IntegratedList.append(img) # note this as integrated
3081        # split name and control number
3082        try:
3083            s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile)[0])[1])
3084        except AttributeError: # not sure why, but sometimes imagefile is a list here (should not be)!
3085            s = re.split(r'(\d+)\Z',os.path.split(os.path.splitext(imagefile[0])[0])[1])
3086        namepre = s[0]
3087        if len(s) > 1:
3088            namenum = s[1]
3089        else:
3090            namenum = ''
3091        for Id in G2frame.IntgOutList: # loop over newly created PWDR entry(ies)
3092            # save the created PWDR tree names so that a reset can delete them
3093            G2frame.Image = Id
3094            treename = G2frame.GPXtree.GetItemText(Id)
3095            G2frame.AutointPWDRnames.append(treename)
3096            # write out the images in the selected formats
3097            Sdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,Id, 'Sample Parameters'))
3098            if self.AutoScale:
3099                print ('Rescale by %.4f'%(Scale))
3100                y,w = G2frame.GPXtree.GetItemPyData(Id)[1][1:3]
3101                y *= Scale
3102                w /= Scale**2
3103            # determine the name for the current file
3104            fileroot = namepre
3105            if len(G2frame.IntgOutList) > 1:
3106                fileroot += "_AZM"
3107                if 'Azimuth' in Sdata:
3108                    fileroot += str(int(10*Sdata['Azimuth']))
3109                fileroot += "_" 
3110            fileroot += namenum
3111            # loop over selected formats
3112            for dfmt in self.fmtlist:
3113                if not self.params[dfmt[1:]]: continue
3114                if self.params['SeparateDir']:
3115                    subdir = dfmt[1:]
3116                else:
3117                    subdir = ''
3118                fil = os.path.join(self.params['outdir'],subdir,fileroot)
3119                G2IO.ExportPowder(G2frame,treename,fil+'.x',dfmt) # dummy extension (.x) is replaced before write)
3120               
3121    def EnableButtons(self,flag):
3122        '''Relabels and enable/disables the buttons at window bottom when auto-integration is running
3123        '''
3124        # for unclear reasons disabling these buttons causes OnRadioSelect to be invoked
3125        # on windows
3126        if sys.platform != "win32":
3127            for item in (self.btnstart,self.btnreset,self.btnclose): item.Enable(flag)
3128        self.btnstart.SetLabel('Pause')
3129        wx.Yield()
3130               
3131    def ResetFromTable(self,dist):
3132        '''Sets integration parameters based on values from
3133        the lookup table
3134        '''
3135        #dist = self.controlsDict['distance']
3136        interpDict,imgctrl,immask = self.Evaluator(dist) # interpolated calibration values
3137        if GSASIIpath.GetConfigValue('debug'):
3138            print ('DBG_interpolated values: ',interpDict)
3139        self.ImageControls = ReadControls(imgctrl)
3140        self.ImageControls.update(interpDict)
3141        self.ImageControls['showLines'] = True
3142        self.ImageControls['ring'] = []
3143        self.ImageControls['rings'] = []
3144        self.ImageControls['ellipses'] = []
3145        self.ImageControls['setDefault'] = False
3146        for i in 'range','size','GonioAngles':
3147            if i in self.ImageControls:
3148                del self.ImageControls[i]
3149        # load copy of Image Masks
3150        if immask:
3151            self.ImageMasks = ReadMask(immask)
3152            if list(self.ImageMasks['Thresholds'][0]) == self.ImageMasks['Thresholds'][1]:     #avoid copy of unchanged thresholds
3153                del self.ImageMasks['Thresholds']
3154        else:
3155            self.ImageMasks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[]}
3156       
3157    def StartLoop(self):
3158        '''Prepare to start autointegration timer loop.
3159        Save current Image params for use in future integrations
3160        also label the window so users understand what is being used
3161        '''
3162        print('\nStarting new autointegration\n')
3163        G2frame = self.G2frame
3164        # show current IMG base
3165        if self.params['Mode'] != 'table':
3166            self.useActive.SetLabel("Active Image: "+
3167                                    G2frame.GPXtree.GetItemText(self.imageBase))
3168            # load copy of Image Controls from current image and clean up
3169            # items that should not be copied
3170            self.ImageControls = copy.deepcopy(
3171                G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3172                    G2frame,self.imageBase, 'Image Controls')))
3173            self.ImageControls['showLines'] = True
3174            self.ImageControls['ring'] = []
3175            self.ImageControls['rings'] = []
3176            self.ImageControls['ellipses'] = []
3177            self.ImageControls['setDefault'] = False
3178            del self.ImageControls['range']
3179            del self.ImageControls['size']
3180            del self.ImageControls['GonioAngles']
3181            # load copy of Image Masks, keep thresholds
3182            self.ImageMasks = copy.deepcopy(
3183                G2frame.GPXtree.GetItemPyData(
3184                    G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks')))
3185            self.Thresholds = self.ImageMasks['Thresholds'][:]
3186            if list(self.Thresholds[0]) == self.Thresholds[1]:     #avoid copy of unchanged thresholds
3187                del self.ImageMasks['Thresholds']   
3188        # make sure all output directories exist
3189        if self.params['SeparateDir']:
3190            for dfmt in self.fmtlist:
3191                if not self.params[dfmt[1:]]: continue
3192                dir = os.path.join(self.params['outdir'],dfmt[1:])
3193                if not os.path.exists(dir): os.makedirs(dir)
3194        else:
3195            if not os.path.exists(self.params['outdir']):
3196                os.makedirs(self.params['outdir'])
3197        if self.Reset: # special things to do after Reset has been pressed
3198            self.G2frame.IntegratedList = []
3199           
3200            if self.params['Mode'] != 'table': # reset controls and masks for all IMG items in tree to master
3201                for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3202                    # update controls from master
3203                    controlsDict = G2frame.GPXtree.GetItemPyData(
3204                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Image Controls'))
3205                    controlsDict.update(self.ImageControls)
3206                    # update masks from master
3207                    ImageMasks = G2frame.GPXtree.GetItemPyData(
3208                        G2gd.GetGPXtreeItemId(G2frame,self.imageBase, 'Masks'))
3209                    ImageMasks.update(self.ImageMasks)
3210            # delete all PWDR items created after last Start was pressed
3211            idlist = []
3212            item, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
3213            while item:
3214                itemName = G2frame.GPXtree.GetItemText(item)
3215                if itemName in G2frame.AutointPWDRnames:
3216                    idlist.append(item)
3217                item, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
3218            for item in idlist:
3219                G2frame.GPXtree.Delete(item)
3220            wx.Yield()
3221            self.Reset = False
3222        G2frame.AutointPWDRnames = [] # list of created PWDR tree item names
3223        G2frame.AutointPDFnames = [] # list of created PWDR tree item names
3224        # check that AutoPDF input is OK, offer chance to use alternate PWDRs if referenced ones
3225        # are not present
3226        if self.params['ComputePDF']:
3227            msg = self.checkPDFprm()
3228            if 'Error:' in msg:
3229                print(msg)
3230                return True
3231            fileList = []
3232            Id, cookie = G2frame.GPXtree.GetFirstChild(G2frame.root)
3233            while Id:
3234                name = G2frame.GPXtree.GetItemText(Id)
3235                if name.startswith('PWDR '): fileList.append(name)
3236                Id, cookie = G2frame.GPXtree.GetNextChild(G2frame.root, cookie)
3237            if not fileList:
3238                print(msg)
3239                print('No PWDR entries to select')
3240                return True
3241            for key in ['Sample Bkg.','Container','Container Bkg.']:
3242                name = self.pdfControls[key]['Name']
3243                if not name: continue
3244                if not G2gd.GetGPXtreeItemId(G2frame,G2frame.root,name):
3245                    indx = G2G.ItemSelector(fileList, self, header='Select PWDR item',
3246                                    title='Select a PWDR tree item for '+key+'\n(or cancel to quit)')
3247                    if indx is None:
3248                        print('No PWDR entry selected for '+key)
3249                        return True
3250                    self.pdfControls[key]['Name'] = fileList[indx]
3251        return False
3252               
3253    def OnTimerLoop(self,event):
3254        '''A method that is called every :meth:`PollTime` seconds that is
3255        used to check for new files and process them. Integrates new images.
3256        Also optionally sets up and computes PDF.
3257        This is called only after the "Start" button is pressed (then its label reads "Pause").
3258        '''
3259        def AutoIntegrateImage(imgId,useTA=None,useMask=None):
3260            '''Integrates an image that has been read into the data tree and updates the
3261            AutoInt window.
3262            '''
3263            img = G2frame.GPXtree.GetItemText(imgId)
3264            controlsDict = G2frame.GPXtree.GetItemPyData(
3265                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3266            ImageMasks = G2frame.GPXtree.GetItemPyData(
3267                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
3268            if self.params['Mode'] == 'table': # look up parameter values from table
3269                useTA = None        #force remake of x,y-->2th,azm map
3270                self.ResetFromTable(controlsDict['setdist'])
3271            # update controls from master
3272            controlsDict.update(self.ImageControls)
3273            # update masks from master w/o Thresholds
3274            ImageMasks.update(self.ImageMasks)
3275            self.EnableButtons(False)
3276            try:
3277                self.IntegrateImage(img,useTA=useTA,useMask=useMask)
3278            finally:
3279                self.EnableButtons(True)
3280            self.G2frame.oldImagefile = '' # mark image as changed; reread as needed
3281            wx.Yield()
3282            self.ShowMatchingFiles(self.params['filter'])
3283            wx.Yield()
3284           
3285        def AutoComputePDF(imgId):
3286            '''Computes a PDF for a PWDR data tree tree item
3287            '''
3288            for pwdr in G2frame.AutointPWDRnames[:]:
3289                if not pwdr.startswith('PWDR '): continue
3290                if pwdr in G2frame.AutointPDFnames: continue
3291                PWid = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,pwdr)
3292                controlsDict = G2frame.GPXtree.GetItemPyData(
3293                    G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3294                if self.params['pdfDmax'] != 0 and controlsDict['distance'] > self.params['pdfDmax']:
3295                    print('Skipping PDF for '+pwdr+' due to detector position')
3296                    continue
3297                # Setup PDF
3298                Data = G2frame.GPXtree.GetItemPyData(PWid)[1]
3299                pwdrMin = np.min(Data[1])
3300                Parms = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3301                    G2frame,PWid,'Instrument Parameters'))[0]
3302                fullLimits = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3303                    G2frame,PWid,'Limits'))[0]
3304                if 'C' in Parms['Type'][0]:
3305                    qMax = tth2q(fullLimits[1],G2mth.getWave(Parms))
3306                else:
3307                    qMax = tof2q(fullLimits[0],Parms['difC'][1])
3308                Qlimits = [0.9*qMax,qMax]
3309
3310                item = pwdr
3311                Comments = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3312                    G2frame,imgId, 'Comments'))
3313                ElList = {}
3314                sumnum = 1.0
3315                for item in Comments:           #grab chemical formula from Comments, if there
3316                    if 'formula' in item[:15].lower():
3317                        formula = item.split('=')[1].split()
3318                        elems = formula[::2]
3319                        nums = formula[1::2]
3320                        formula = zip(elems,nums)
3321                        sumnum = 0.
3322                        for [elem,num] in formula:
3323                            ElData = G2elem.GetElInfo(elem,Parms)
3324                            ElData['FormulaNo'] = float(num)
3325                            sumnum += float(num)
3326                            ElList[elem] = ElData
3327                PDFnames = G2gd.GetGPXtreeDataNames(G2frame,['PDF ',])
3328                PDFid = G2obj.CreatePDFitems(G2frame,pwdr,ElList.copy(),Qlimits,sumnum,pwdrMin,PDFnames)
3329                if not PDFid: continue
3330                PDFdata = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(
3331                    G2frame,PDFid, 'PDF Controls'))
3332                PDFdata.update(copy.deepcopy(self.pdfControls))
3333                if ElList: PDFdata['ElList'] = ElList # override with formula from comments, if present
3334                PDFdata['Sample']['Name'] = pwdr
3335                # compute PDF
3336                wx.Yield()
3337                G2pdG.computePDF(G2frame,PDFdata)
3338                wx.Yield()
3339                G2frame.PatternId = PDFid
3340                G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3341                if self.params['optPDF']:
3342                    G2pdG.OptimizePDF(G2frame,PDFdata,maxCycles=10,)
3343                    wx.Yield()
3344                    G2plt.PlotISFG(G2frame,PDFdata,newPlot=False,plotType='G(R)')
3345                G2frame.AutointPDFnames.append(pwdr)
3346                # save names of PDF entry to be deleted later if needed
3347                G2frame.AutointPWDRnames.append(G2frame.GPXtree.GetItemText(PDFid))
3348           
3349        G2frame = self.G2frame
3350        try:
3351            self.currImageList = sorted(
3352                glob.glob(os.path.join(self.params['readdir'],self.params['filter'])))
3353            self.ShowMatchingFiles(self.params['filter'])
3354        except IndexError:
3355            self.currImageList = []
3356            return
3357
3358        if self.PreventReEntryTimer: return
3359        self.PreventReEntryTimer = True
3360        imageFileList = []
3361        # integrate the images that have already been read in, but
3362        # have not yet been processed           
3363        oldData = {'tilt':0.,'distance':0.,'rotation':0.,'center':[0.,0.],'DetDepth':0.,'azmthOff':0.}
3364        oldMhash = 0
3365        if 'useTA' not in dir(self):    #initial definition; reuse if after Resume
3366            self.useTA = None
3367            self.useMask = None
3368        for img in G2gd.GetGPXtreeDataNames(G2frame,['IMG ']):
3369            imgId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,img)
3370            size,imagefile,imagetag = G2frame.GPXtree.GetImageLoc(imgId)
3371            # Create a list of image files that have been read in
3372            if imagefile not in imageFileList: imageFileList.append(imagefile)
3373            # skip if already integrated
3374            if img in G2frame.IntegratedList: continue
3375            Data = G2frame.GPXtree.GetItemPyData(
3376                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Image Controls'))
3377            sameTA = True
3378            for item in ['tilt','distance','rotation','center','DetDepth','azmthOff']:
3379                if Data[item] != oldData[item]:
3380                    sameTA = False
3381            if not sameTA:
3382                t0 = time.time()
3383                self.useTA = G2img.MakeUseTA(Data,blkSize)
3384                print(' Use new image controls; xy->th,azm mtime: %.3f'%(time.time()-t0))
3385            Mask = G2frame.GPXtree.GetItemPyData(
3386                G2gd.GetGPXtreeItemId(G2frame,imgId, 'Masks'))
3387            Mhash = copy.deepcopy(Mask)
3388            Mhash.pop('Thresholds')
3389            Mhash = hash(str(Mhash))
3390            if  Mhash != oldMhash:
3391                t0 = time.time()
3392                self.useMask = G2img.MakeUseMask(Data,Mask,blkSize)
3393                print(' Use new mask; make mask time: %.3f'%(time.time()-t0))
3394                oldMhash = Mhash
3395            AutoIntegrateImage(imgId,self.useTA,self.useMask)
3396            oldData = Data
3397            if self.pdfControls: AutoComputePDF(imgId)
3398            self.Pause |= G2frame.PauseIntegration
3399            if self.Pause:
3400                self.OnPause()
3401                self.PreventReEntryTimer = False
3402                self.Raise()
3403                return
3404
3405        # loop over image files matching glob, reading in any new ones
3406        if self.useTA is None or self.useMask is None:
3407            print('Integration will not be fast; there is no beginning image controls')     #TODO: work this out??
3408        for newImage in self.currImageList:
3409            if newImage in imageFileList or self.Pause: continue # already read?
3410            for imgId in G2IO.ReadImages(G2frame,newImage):
3411                AutoIntegrateImage(imgId,self.useTA,self.useMask)
3412                if self.pdfControls: AutoComputePDF(imgId)
3413                self.Pause |= G2frame.PauseIntegration
3414                if self.Pause:
3415                    self.OnPause()
3416                    self.PreventReEntryTimer = False
3417                    self.Raise()
3418                    return
3419        if GSASIIpath.GetConfigValue('debug'):
3420            import datetime
3421            print ("DBG_Timer tick at {:%d %b %Y %H:%M:%S}\n".format(datetime.datetime.now()))
3422        self.PreventReEntryTimer = False
3423        self.Raise()
3424
3425def DefineEvaluator(dlg):
3426    '''Creates a function that provides interpolated values for a given distance value
3427    '''
3428    def Evaluator(dist):
3429        '''Interpolate image parameters for a supplied distance value
3430
3431        :param float dist: distance to use for interpolation
3432        :returns: a list with 3 items:
3433
3434          * a dict with interpolated parameter values,
3435          * the closest imctrl and
3436          * the closest maskfile (or None)
3437        '''           
3438        x = np.array([float(i) for i in parms[0]])
3439        closest = abs(x-dist).argmin()
3440        D = {'setdist':dist}
3441        imctfile = IMfileList[closest]
3442        if parms[-1][closest].lower() != '(none)':
3443            maskfile = parms[-1][closest]
3444        else:
3445            maskfile = None
3446        for c in range(1,cols-1):
3447            lbl = ParmList[c]
3448            if lbl in nonInterpVars:
3449                if lbl in ['outChannels',]:
3450                    D[lbl] = int(float(parms[c][closest]))
3451                else:
3452                    D[lbl] = float(parms[c][closest])
3453            else:
3454                y = np.array([float(i) for i in parms[c]])
3455                D[lbl] = np.interp(dist,x,y)
3456        # full integration when angular range is 0
3457        D['fullIntegrate'] = (D['LRazimuth_min'] == D['LRazimuth_max'])
3458        # conversion for paired values
3459        for a,b in ('center_x','center_y'),('LRazimuth_min','LRazimuth_max'),('IOtth_min','IOtth_max'):
3460            r = a.split('_')[0]
3461            D[r] = [D[a],D[b]]
3462            if r in ['LRazimuth',]:
3463                D[r] = [int(D[a]),int(D[b])]
3464            del D[a]
3465            del D[b]
3466        return D,imctfile,maskfile
3467    # save local copies of values needed in Evaluator
3468    parms = dlg.ReadImageParmTable()
3469    IMfileList = dlg.IMfileList
3470    cols = dlg.list.GetColumnCount()
3471    ParmList = dlg.ParmList
3472    nonInterpVars = dlg.nonInterpVars
3473    return Evaluator
3474
3475class IntegParmTable(wx.Dialog):
3476    '''Creates a dialog window with a table of integration parameters.
3477    :meth:`ShowModal` will return wx.ID_OK if the process has been successful.
3478    In this case, :func:`DefineEvaluator` should be called to obtain a function that
3479    creates a dictionary with interpolated parameter values.
3480    '''
3481    ParmList = ('setdist','distance','center_x','center_y','wavelength','tilt','rotation','DetDepth',
3482            'LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max','outChannels',
3483            'maskfile',
3484            )
3485    nonInterpVars = ('tilt','rotation','LRazimuth_min','LRazimuth_max','IOtth_min','IOtth_max',
3486                     'outChannels')  # values in this list are taken from nearest rather than interpolated
3487    HeaderList = ('Set Dist','Calib Dist','X cntr','Y cntr','wavelength','tilt','rotation','DetDepth',
3488            'Azimuth min','Azimuth max','2Th min','2Th max','Int. pts',
3489            'Mask File',
3490            )
3491    def __init__(self,parent,parms=None,IMfileList=None,readFileList=None):
3492        self.G2frame = parent.G2frame
3493        dlg = None
3494        pth = ''
3495        wx.Dialog.__init__(self,parent,style=wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE)
3496        if readFileList:
3497            self.parms,self.IMfileList = self.ReadFiles(readFileList)
3498        elif parms:
3499            self.parms = parms # list of values by column
3500            self.IMfileList = IMfileList # list of .imctrl file names for each entry in table
3501        else:
3502            self.parms = [] # list of values by column
3503            self.IMfileList = [] # list of .imctrl file names for each entry in table
3504            files = []
3505            try:
3506                pth = G2G.GetImportPath(self.G2frame)
3507                if not pth: pth = '.'
3508                dlg = wx.FileDialog(parent, 'Read previous table or build new table by selecting image control files', pth,
3509                    style=wx.FD_OPEN| wx.FD_MULTIPLE,
3510                    wildcard='Integration table (*.imtbl)|*.imtbl|image control files (.imctrl)|*.imctrl')
3511                dlg.CenterOnParent()
3512                if dlg.ShowModal() == wx.ID_OK:
3513                    files = dlg.GetPaths()
3514                    self.parms,self.IMfileList = self.ReadFiles(files)
3515            finally:
3516                if dlg: dlg.Destroy()
3517            if not files:
3518                wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3519                return
3520        mainSizer = wx.BoxSizer(wx.VERTICAL)
3521        self.list = ImgIntLstCtrl(self, wx.ID_ANY,style=wx.LC_REPORT| wx.BORDER_SUNKEN)
3522        mainSizer.Add(self.list,1,wx.EXPAND,1)
3523        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
3524        btn = wx.Button(self, wx.ID_OK)
3525        btnsizer.Add(btn)
3526        btn = wx.Button(self, wx.ID_ANY,'Save as file')
3527        btn.Bind(wx.EVT_BUTTON,self._onSave)
3528        btnsizer.Add(btn)
3529        btn = wx.Button(self, wx.ID_CLOSE,'Quit')
3530        btn.Bind(wx.EVT_BUTTON,self._onClose)
3531        btnsizer.Add(btn)
3532        mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)   
3533        self.SetSizer(mainSizer)
3534        self.list.FillList(self.parms)
3535       
3536    def ReadFiles(self,files):
3537        '''Reads a list of .imctrl files or a single .imtbl file
3538        '''
3539        tmpDict = {}
3540        if not files: return
3541        # option 1, a dump from a previous save
3542        if os.path.splitext(files[0])[1] == '.imtbl':
3543            fp = open(files[0],'r')
3544            S = fp.readline()
3545            while S:
3546                if S[0] != '#':
3547                    [key,val] = S[:-1].split(':',1)
3548                    tmpDict[key] = eval(val)
3549                S = fp.readline()
3550            fp.close()
3551            # delete entries where files do not exist
3552            m1 = [i for i,f in enumerate(tmpDict['filenames']) if not os.path.exists(f)]
3553            if m1:
3554                print('\nimctrl file not found:')
3555                for i in m1: print('\t#'+str(i)+': '+tmpDict['filenames'][i])
3556            m2 = [i for i,f in enumerate(tmpDict['maskfile']) if not (os.path.exists(f) or f.startswith('('))]
3557            if m2:
3558                print('\nmask file not found')
3559                for i in m2: print('\t#'+str(i)+': '+tmpDict['maskfile'][i])
3560            m3 = [i for i,d in enumerate(tmpDict['distance']) if d < 0]
3561            if m3:
3562                print('\nDropping entries due to negative distance: '+str(m3))
3563            m = sorted(set(m1 + m2 + m3))
3564            m.reverse()
3565            for c in m:
3566                for key in tmpDict:
3567                    del tmpDict[key][c]
3568            fileList = tmpDict.get('filenames','[]')
3569            parms = []
3570            if 'setdist' not in tmpDict:
3571                print(u'Old file, recreate before using: {}'.format(files[0]))
3572                return [[]],[]
3573            for key in self.ParmList:
3574                try:
3575                    float(tmpDict[key][0])
3576                    parms.append([str(G2py3.FormatSigFigs(val1,sigfigs=5)) for val1 in tmpDict[key]])
3577                except ValueError:
3578                    parms.append(tmpDict[key])
3579                except IndexError:
3580                    print('No valid image control entries read')
3581                    wx.CallAfter(self.EndModal,wx.ID_CANCEL)
3582                    return [[]],[]
3583            return parms,fileList
3584        # option 2, read in a list of files
3585        for file in files: # read all files; place in dict by distance
3586            imgDict = Read_imctrl(file)
3587            dist = imgDict.get('setdist',imgDict['distance'])
3588            if dist is None:
3589                print('Skipping old file, redo: {}'.format(file))
3590            tmpDict[dist] = imgDict
3591        parms = [[] for key in self.ParmList]
3592        fileList = []
3593        for d in sorted(tmpDict):
3594            fileList.append(tmpDict[d].get('filename'))
3595            if d is None: continue
3596            if d < 0: continue
3597            for i,key in enumerate(self.ParmList):
3598                val = tmpDict[d].get(key)
3599                try:
3600                    val = str(G2py3.FormatSigFigs(val,sigfigs=5))
3601                except:
3602                    val = str(val)
3603                parms[i].append(val)
3604        return parms,fileList
3605   
3606    def ReadImageParmTable(self):
3607        '''Reads possibly edited values from the ListCtrl table and returns a list
3608        of values for each column.
3609        '''
3610        rows = self.list.GetItemCount()
3611        cols = self.list.GetColumnCount()
3612        parms = []
3613        for c in range(cols):
3614            parms.append([])
3615            for r in range(rows):
3616                parms[c].append(self.list.GetItem(r,c).GetText())
3617        return parms
3618
3619    def _onClose(self,event):
3620        'Called when Cancel button is pressed'
3621        self.EndModal(wx.ID_CANCEL)
3622       
3623    def _onSave(self,event):
3624        'Called when save button is pressed; creates a .imtbl file'
3625        fil = ''
3626        if self.G2frame.GSASprojectfile:
3627            fil = os.path.splitext(self.G2frame.GSASprojectfile)[0]+'.imtbl'
3628        dir,f = os.path.split(fil)
3629        pth = G2G.GetExportPath(self.G2frame)
3630        try:
3631            dlg = wx.FileDialog(self, 'Save table data as',
3632                        defaultDir=pth, defaultFile=f, style=wx.SAVE,
3633                        wildcard='G2 Image Param Table file (*.imtbl)|*.imtbl')
3634            dlg.CenterOnParent()
3635            if dlg.ShowModal() != wx.ID_OK: return
3636            fil = dlg.GetPath()
3637            fil = os.path.splitext(fil)[0]+'.imtbl'
3638        finally:
3639            dlg.Destroy()       
3640        parms = self.ReadImageParmTable()
3641        print('Writing image parameter table as '+fil)
3642        fp = open(fil,'w')
3643        for c in range(len(parms)-1):
3644            lbl = self.ParmList[c]
3645            fp.write(lbl+': '+str([eval(i) for i in parms[c]])+'\n')
3646        lbl = self.ParmList[c+1]
3647        fp.write(lbl+': '+str(parms[c+1])+'\n')
3648        lbl = 'filenames'
3649        fp.write(lbl+': '+str(self.IMfileList)+'\n')
3650        fp.close()
3651   
3652class ImgIntLstCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin,listmix.TextEditMixin):
3653    '''Creates a custom ListCtrl for editing Image Integration parameters
3654    '''
3655    def __init__(self, parent, ID, pos=wx.DefaultPosition,size=(1000,200),style=0):
3656        self.parent=parent
3657        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
3658        listmix.ListCtrlAutoWidthMixin.__init__(self)
3659        listmix.TextEditMixin.__init__(self)
3660        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDouble)
3661        #self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColClick)
3662       
3663    def FillList(self,parms):
3664        'Places the current parms into the table'
3665        # the use of InsertStringItem and SetStringItem are depricated in 4.0 but
3666        # I am not quite sure how to replace them with InsertItem and SetItem yet.
3667        # Perhaps switch to  ULC.UltimateListCtrl?
3668        #
3669        maxint = 2**31-1
3670        self.ClearAll()
3671        self.rowlen = len(self.parent.ParmList)
3672        for i,lbl in enumerate(self.parent.HeaderList):
3673            self.InsertColumn(i, lbl)
3674        for r,d in enumerate(parms[0]):
3675            if d is None: continue
3676            if d == 'None': continue
3677            if float(d) < 0: continue
3678            index = self.InsertStringItem(maxint, d)
3679            for j in range(1,len(parms)):
3680                self.SetStringItem(index, j, parms[j][r])
3681        for i,lbl in enumerate(self.parent.ParmList):
3682            self.SetColumnWidth(i, wx.LIST_AUTOSIZE)
3683
3684    def OnDouble(self,evt):
3685        'respond to a double-click'
3686        self.CloseEditor()
3687        fil = '(none)'
3688        pth = G2G.GetImportPath(self.parent.G2frame)
3689        if not pth: pth = '.'
3690        try:
3691            dlg = wx.FileDialog(self, 'Select mask or control file to add (Press cancel if none)', pth,
3692                style=wx.FD_OPEN,wildcard='Add GSAS-II mask file (.immask)|*.immask|add image control file (.imctrl)|*.imctrl')
3693            dlg.CenterOnParent()
3694            if dlg.ShowModal() == wx.ID_OK:
3695                fil = dlg.GetPath()
3696        finally:
3697            dlg.Destroy()
3698        if os.path.splitext(fil)[1] != '.imctrl':
3699            self.SetStringItem(self.curRow, self.rowlen-1, fil)
3700            self.SetColumnWidth(self.rowlen-1, wx.LIST_AUTOSIZE)
3701        else:
3702            # insert or overwrite an instrument parameter set
3703            if not os.path.exists(fil):
3704                print('Does not exist: '+fil)
3705                return
3706            imgDict = Read_imctrl(fil)
3707            dist = imgDict['distance']
3708            parms = self.parent.ReadImageParmTable()
3709            x = np.array([float(i) for i in parms[0]])
3710            closest = abs(x-dist).argmin()
3711            closeX = x[closest]
3712            # fix IMfileList
3713            for c,lbl in enumerate(self.parent.ParmList):
3714                try:
3715                    vali = G2py3.FormatSigFigs(float(imgDict[lbl]),sigfigs=5)
3716                except ValueError:
3717                    vali = imgDict[lbl]
3718                if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3719                    parms[c][closest] = vali
3720                elif dist > closeX: # insert after
3721                    parms[c].insert(closest+1,vali)
3722                else:
3723                    parms[c].insert(closest,vali)
3724            if abs(closeX-dist) < 1.: # distance is within 1 mm, replace
3725                self.parent.IMfileList[closest] = fil
3726            elif dist > closeX: # insert after
3727                self.parent.IMfileList.insert(closest+1,fil)
3728            else:
3729                self.parent.IMfileList.insert(closest,fil)
3730            self.FillList(parms)
3731# Autointegration end
3732###########################################################################
3733
3734def testColumnMetadata(G2frame):
3735    '''Test the column-oriented metadata parsing, as implemented at 1-ID, by showing results
3736    when using a .par and .lbls pair.
3737   
3738     * Select a .par file, if more than one in selected dir.
3739     * Select the .*lbls file, if more than one matching .par file.
3740     * Parse the .lbls file, showing errors if encountered; loop until errors are fixed.
3741     * Search for an image or a line in the .par file and show the results when interpreted
3742     
3743    See :func:`GSASIIfiles.readColMetadata` for more details.
3744    '''
3745    if not GSASIIpath.GetConfigValue('Column_Metadata_directory'):
3746        G2G.G2MessageBox(G2frame,'The configuration option for I-ID Metadata is not set.\n'+
3747                         'Please use the File/Preferences menu to set Column_Metadata_directory',
3748                         'Warning')
3749        return
3750    parFiles = glob.glob(os.path.join(GSASIIpath.GetConfigValue('Column_Metadata_directory'),'*.par'))
3751    if not parFiles: 
3752        G2G.G2MessageBox(G2frame,'No .par files found in directory {}. '
3753                         .format(GSASIIpath.GetConfigValue('Column_Metadata_directory'))+
3754                         '\nThis is set by config variable Column_Metadata_directory '+
3755                         '(Set in File/Preferences menu).',
3756                         'Warning')
3757        return
3758    parList = []
3759    for parFile in parFiles:
3760        lblList = []
3761        parRoot = os.path.splitext(parFile)[0]
3762        for f in glob.glob(parRoot+'.*lbls'):
3763            if os.path.exists(f): lblList.append(f)
3764        if not len(lblList):
3765            continue
3766        parList.append(parFile)
3767    if len(parList) == 0:
3768        G2G.G2MessageBox(G2frame,'No .lbls or .EXT_lbls file found for .par file(s) in directory {}. '
3769                         .format(GSASIIpath.GetConfigValue('Column_Metadata_directory'))+
3770                         '\nThis is set by config variable Column_Metadata_directory '+
3771                         '(Set in File/Preferences menu).',
3772                         'Warning')
3773        return
3774    elif len(parList) == 1:
3775        parFile = parList[0]
3776    else:
3777        dlg = G2G.G2SingleChoiceDialog(G2frame,
3778                'More than 1 .par file found. (Better if only 1!). Choose the one to test in '+
3779                GSASIIpath.GetConfigValue('Column_Metadata_directory'),
3780                'Choose .par file', [os.path.split(i)[1] for i in parList])
3781        if dlg.ShowModal() == wx.ID_OK:
3782            parFile = parList[dlg.GetSelection()]
3783            dlg.Destroy()
3784        else:
3785            dlg.Destroy()
3786            return
3787    # got .par file; now work on .*lbls file
3788    lblList = []
3789    parRoot = os.path.splitext(parFile)[0]
3790    for f in glob.glob(parRoot+'.*lbls'):
3791        if os.path.exists(f): lblList.append(f)
3792    if not len(lblList):
3793        raise Exception('How did this happen! No .*lbls files for '+parFile)
3794    elif len(lblList) == 1:
3795        lblFile = lblList[0]
3796    else:
3797        dlg = G2G.G2SingleChoiceDialog(G2frame,
3798                'Select label file for .par file '+parFile,
3799                'Choose label file', [os.path.split(i)[1] for i in lblList])
3800        if dlg.ShowModal() == wx.ID_OK:
3801            lblFile = lblList[dlg.GetSelection()]
3802            dlg.Destroy()
3803        else:
3804            dlg.Destroy()
3805            return
3806    # parse the labels file
3807    errors = True
3808    while errors:
3809        labels,lbldict,keyCols,keyExp,errors = G2fil.readColMetadataLabels(lblFile)
3810        if errors:
3811            t = "Error reading file "+lblFile
3812            for l in errors:
3813                t += '\n'
3814                t += l
3815            t += "\n\nPlease edit the file and press OK (or Cancel to quit)"
3816            dlg = wx.MessageDialog(G2frame,message=t,
3817                caption="Read Error",style=wx.ICON_ERROR| wx.OK|wx.STAY_ON_TOP|wx.CANCEL)
3818            if dlg.ShowModal() != wx.ID_OK: return           
3819    # request a line number, read that line
3820    dlg = G2G.SingleStringDialog(G2frame,'Read what',
3821                                 'Enter a line number or an image file name (-1=last line)',
3822                                 '-1',size=(400,-1))
3823    if dlg.Show():
3824        fileorline = dlg.GetValue()
3825        dlg.Destroy()
3826    else:
3827        dlg.Destroy()
3828        return
3829    # and report the generated key pairs in metadata dict
3830    linenum = None
3831    try:
3832        linenum = int(fileorline)
3833    except:
3834        imageName = os.path.splitext(os.path.split(fileorline)[1])[0]
3835
3836    fp = open(parFile,'Ur')
3837    for iline,line in enumerate(fp):
3838        if linenum is not None:
3839            if iline == linenum:
3840                items = line.strip().split(' ')
3841                n = "Line {}".format(iline)
3842                break
3843            else:
3844                continue
3845        else:
3846            items = line.strip().split(' ')
3847            nameList = keyExp['filename'](*[items[j] for j in keyCols['filename']])
3848            if type(nameList) is str:
3849                if nameList != imageName: continue
3850                name = nameList
3851                break
3852            else:
3853                for name in nameList:
3854                    print (name,name == imageName)
3855                    if name == imageName:
3856                        n = "Image {} found in line {}".format(imageName,iline)
3857                        break # got a match
3858                else:
3859                    continue
3860                break
3861    else:
3862        if linenum is not None:
3863            n = "Line {}".format(iline)
3864        else:
3865            n = "Image {} not found. Reporting line {}".format(imageName,iline)
3866        items = line.strip().split(' ')
3867    fp.close()
3868    metadata = G2fil.evalColMetadataDicts(items,labels,lbldict,keyCols,keyExp,True)
3869    title = "Results: ("+n+")"
3870    t = ['Files: '+parFile,lblFile,' ']
3871    n = ["Named parameters:"]
3872    l = ['',"Labeled columns:"]
3873    for key in sorted(metadata):
3874        if key == "filename" or key.startswith('label_prm'): continue
3875        if key in keyCols:
3876            n += [{} = {}".format(key,metadata[key])]
3877        elif key in lbldict.values():
3878            l += [{} = {}".format(key,metadata[key])]
3879        else:
3880            t += ["** Unexpected:  {}".format(key,metadata[key])]
3881    if type(metadata['filename']) is str:
3882        l += ["","Filename: "+ metadata['filename']]
3883    else:
3884        l += ["","Filename(s): "]
3885        for i,j in enumerate(metadata['filename']):
3886            if i: l[-1] += ', '
3887            l[-1] += j
3888    t += n + l + ['','Unused columns:']
3889    usedCols = list(lbldict.keys())
3890    for i in keyCols.values(): usedCols += i
3891    for i in range(len(items)):
3892        if i in usedCols: continue
3893        t += [{}: {}".format(i,items[i])]
3894    dlg = G2G.G2SingleChoiceDialog(None,title,'Column metadata parse results',t,
3895                                   monoFont=True,filterBox=False,size=(400,600),
3896                                   style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE|wx.OK)
3897    dlg.ShowModal()
Note: See TracBrowser for help on using the repository browser.