source: trunk/GSASIIimgGUI.py @ 4107

Last change on this file since 4107 was 4107, checked in by vondreele, 2 years ago

put covar matrix in Covariance from multi-dist recalibrate

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