source: trunk/GSASIIimgGUI.py @ 2625

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

fix problem with using dark/background images - GetCheckImageFile? needed to refer to treeId rather than G2frame.Image
fix Progress bar problem in integration should be destroyed after each use

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