source: trunk/GSASIIimgGUI.py @ 2723

Last change on this file since 2723 was 2723, checked in by vondreele, 5 years ago

add patch for missing PolaVal? in old image gpx files
Stress/strain image plots now have calculated rings drawn from pixel centers where the intensity distribution is determined

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