You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
360 lines
11 KiB
360 lines
11 KiB
"""
|
|
A vtkTkImageViewerWidget for python, which is based on the
|
|
vtkTkImageWindowWidget.
|
|
|
|
Specify double=1 to get a double-buffered window.
|
|
|
|
Created by David Gobbi, Nov 1999
|
|
"""
|
|
|
|
import Tkinter
|
|
import math, os, sys
|
|
import vtk
|
|
|
|
from vtkLoadPythonTkWidgets import vtkLoadPythonTkWidgets
|
|
|
|
class vtkTkImageViewerWidget(Tkinter.Widget):
|
|
"""
|
|
A vtkTkImageViewerWidget for Python.
|
|
|
|
Use GetImageViewer() to get the vtkImageViewer.
|
|
|
|
Create with the keyword double=1 in order to generate a
|
|
double-buffered viewer.
|
|
|
|
Create with the keyword focus_on_enter=1 to enable
|
|
focus-follows-mouse. The default is for a click-to-focus mode.
|
|
"""
|
|
def __init__(self, master, cnf={}, **kw):
|
|
"""
|
|
Constructor.
|
|
|
|
Keyword arguments:
|
|
|
|
iv -- Use passed image viewer instead of creating a new one.
|
|
|
|
double -- If True, generate a double-buffered viewer.
|
|
Defaults to False.
|
|
|
|
focus_on_enter -- If True, use a focus-follows-mouse mode.
|
|
Defaults to False where the widget will use a click-to-focus
|
|
mode.
|
|
"""
|
|
# load the necessary extensions into tk
|
|
vtkLoadPythonTkWidgets(master.tk)
|
|
|
|
try: # use specified vtkImageViewer
|
|
imageViewer = kw['iv']
|
|
except KeyError: # or create one if none specified
|
|
imageViewer = vtk.vtkImageViewer()
|
|
|
|
doubleBuffer = 0
|
|
try:
|
|
if kw['double']:
|
|
doubleBuffer = 1
|
|
del kw['double']
|
|
except:
|
|
pass
|
|
|
|
# check if focus should follow mouse
|
|
if kw.get('focus_on_enter'):
|
|
self._FocusOnEnter = 1
|
|
del kw['focus_on_enter']
|
|
else:
|
|
self._FocusOnEnter = 0
|
|
|
|
kw['iv'] = imageViewer.GetAddressAsString("vtkImageViewer")
|
|
Tkinter.Widget.__init__(self, master, 'vtkTkImageViewerWidget',
|
|
cnf, kw)
|
|
if doubleBuffer:
|
|
imageViewer.GetRenderWindow().DoubleBufferOn()
|
|
|
|
self.BindTkImageViewer()
|
|
|
|
def __getattr__(self,attr):
|
|
# because the tk part of vtkTkImageViewerWidget must have
|
|
# the only remaining reference to the ImageViewer when
|
|
# it is destroyed, we can't actually store the ImageViewer
|
|
# as an attribute but instead have to get it from the tk-side
|
|
if attr == '_ImageViewer':
|
|
addr = self.tk.call(self._w, 'GetImageViewer')[5:]
|
|
return vtk.vtkImageViewer('_%s_vtkImageViewer_p' % addr)
|
|
raise AttributeError, self.__class__.__name__ + \
|
|
" has no attribute named " + attr
|
|
|
|
def GetImageViewer(self):
|
|
return self._ImageViewer
|
|
|
|
def Render(self):
|
|
self._ImageViewer.Render()
|
|
|
|
def BindTkImageViewer(self):
|
|
imager = self._ImageViewer.GetRenderer()
|
|
|
|
# stuff for window level text.
|
|
mapper = vtk.vtkTextMapper()
|
|
mapper.SetInput("none")
|
|
t_prop = mapper.GetTextProperty()
|
|
t_prop.SetFontFamilyToTimes()
|
|
t_prop.SetFontSize(18)
|
|
t_prop.BoldOn()
|
|
t_prop.ShadowOn()
|
|
|
|
self._LevelMapper = mapper
|
|
|
|
actor = vtk.vtkActor2D()
|
|
actor.SetMapper(mapper)
|
|
actor.SetLayerNumber(1)
|
|
actor.GetPositionCoordinate().SetValue(4,22)
|
|
actor.GetProperty().SetColor(1,1,0.5)
|
|
actor.SetVisibility(0)
|
|
imager.AddActor2D(actor)
|
|
|
|
self._LevelActor = actor
|
|
|
|
mapper = vtk.vtkTextMapper()
|
|
mapper.SetInput("none")
|
|
t_prop = mapper.GetTextProperty()
|
|
t_prop.SetFontFamilyToTimes()
|
|
t_prop.SetFontSize(18)
|
|
t_prop.BoldOn()
|
|
t_prop.ShadowOn()
|
|
|
|
self._WindowMapper = mapper
|
|
|
|
actor = vtk.vtkActor2D()
|
|
actor.SetMapper(mapper)
|
|
actor.SetLayerNumber(1)
|
|
actor.GetPositionCoordinate().SetValue(4,4)
|
|
actor.GetProperty().SetColor(1,1,0.5)
|
|
actor.SetVisibility(0)
|
|
imager.AddActor2D(actor)
|
|
|
|
self._WindowActor = actor
|
|
|
|
self._LastX = 0
|
|
self._LastY = 0
|
|
self._OldFocus = 0
|
|
self._InExpose = 0
|
|
|
|
# bindings
|
|
# window level
|
|
self.bind("<ButtonPress-1>",
|
|
lambda e,s=self: s.StartWindowLevelInteraction(e.x,e.y))
|
|
self.bind("<B1-Motion>",
|
|
lambda e,s=self: s.UpdateWindowLevelInteraction(e.x,e.y))
|
|
self.bind("<ButtonRelease-1>",
|
|
lambda e,s=self: s.EndWindowLevelInteraction())
|
|
|
|
# Get the value
|
|
self.bind("<ButtonPress-3>",
|
|
lambda e,s=self: s.StartQueryInteraction(e.x,e.y))
|
|
self.bind("<B3-Motion>",
|
|
lambda e,s=self: s.UpdateQueryInteraction(e.x,e.y))
|
|
self.bind("<ButtonRelease-3>",
|
|
lambda e,s=self: s.EndQueryInteraction())
|
|
|
|
self.bind("<Expose>",
|
|
lambda e,s=self: s.ExposeTkImageViewer())
|
|
self.bind("<Enter>",
|
|
lambda e,s=self: s.EnterTkViewer())
|
|
self.bind("<Leave>",
|
|
lambda e,s=self: s.LeaveTkViewer())
|
|
self.bind("<KeyPress-e>",
|
|
lambda e,s=self: s.quit())
|
|
self.bind("<KeyPress-r>",
|
|
lambda e,s=self: s.ResetTkImageViewer())
|
|
|
|
def GetImageViewer(self):
|
|
return self._ImageViewer
|
|
|
|
def Render(self):
|
|
self._ImageViewer.Render()
|
|
|
|
def _GrabFocus(self):
|
|
self._OldFocus=self.focus_get()
|
|
self.focus()
|
|
|
|
def EnterTkViewer(self):
|
|
if self._FocusOnEnter:
|
|
self._GrabFocus()
|
|
|
|
def LeaveTkViewer(self):
|
|
if self._FocusOnEnter and (self._OldFocus != None):
|
|
self._OldFocus.focus()
|
|
|
|
def ExposeTkImageViewer(self):
|
|
if (self._InExpose == 0):
|
|
self._InExpose = 1
|
|
self.update()
|
|
self._ImageViewer.Render()
|
|
self._InExpose = 0
|
|
|
|
def StartWindowLevelInteraction(self,x,y):
|
|
if not self._FocusOnEnter:
|
|
self._GrabFocus()
|
|
viewer = self._ImageViewer
|
|
self._LastX = x
|
|
self._LastY = y
|
|
self._Window = float(viewer.GetColorWindow())
|
|
self._Level = float(viewer.GetColorLevel())
|
|
|
|
# make the window level text visible
|
|
self._LevelActor.SetVisibility(1)
|
|
self._WindowActor.SetVisibility(1)
|
|
|
|
self.UpdateWindowLevelInteraction(x,y)
|
|
|
|
def EndWindowLevelInteraction(self):
|
|
# make the window level text invisible
|
|
self._LevelActor.SetVisibility(0)
|
|
self._WindowActor.SetVisibility(0)
|
|
self.Render()
|
|
|
|
def UpdateWindowLevelInteraction(self,x,y):
|
|
# compute normalized delta
|
|
dx = 4.0*(x - self._LastX)/self.winfo_width()*self._Window
|
|
dy = 4.0*(self._LastY - y)/self.winfo_height()*self._Level
|
|
|
|
# abs so that direction does not flip
|
|
if (self._Window < 0.0):
|
|
dx = -dx
|
|
if (self._Level < 0.0):
|
|
dy = -dy
|
|
|
|
# compute new window level
|
|
window = self._Window + dx
|
|
if (window < 0.0):
|
|
level = self._Level + dy
|
|
else:
|
|
level = self._Level - dy
|
|
|
|
viewer = self._ImageViewer
|
|
viewer.SetColorWindow(window)
|
|
viewer.SetColorLevel(level)
|
|
|
|
self._WindowMapper.SetInput("Window: %g" % window)
|
|
self._LevelMapper.SetInput("Level: %g" % level)
|
|
|
|
self.Render()
|
|
|
|
|
|
def ResetTkImageViewer(self):
|
|
# Reset: Set window level to show all values
|
|
viewer = self._ImageViewer
|
|
input = viewer.GetInput()
|
|
if (input == None):
|
|
return
|
|
|
|
# Get the extent in viewer
|
|
z = viewer.GetZSlice()
|
|
|
|
input.SetUpdateExtent(-99999,99999,-99999,99999,z,z)
|
|
input.Update()
|
|
|
|
(low,high) = input.GetScalarRange()
|
|
|
|
viewer.SetColorWindow(high - low)
|
|
viewer.SetColorLevel((high + low) * 0.5)
|
|
|
|
self.Render()
|
|
|
|
def StartQueryInteraction(self,x,y):
|
|
if not self._FocusOnEnter:
|
|
self._GrabFocus()
|
|
# Query PixleValue stuff
|
|
self._WindowActor.SetVisibility(1)
|
|
self.UpdateQueryInteraction(x,y)
|
|
|
|
def EndQueryInteraction(self):
|
|
self._WindowActor.SetVisibility(0)
|
|
self.Render()
|
|
|
|
def UpdateQueryInteraction(self,x,y):
|
|
viewer = self._ImageViewer
|
|
input = viewer.GetInput()
|
|
z = viewer.GetZSlice()
|
|
|
|
# y is flipped upside down
|
|
y = self.winfo_height() - y
|
|
|
|
# make sure point is in the whole extent of the image.
|
|
(xMin,xMax,yMin,yMax,zMin,zMax) = input.GetWholeExtent()
|
|
if (x < xMin or x > xMax or y < yMin or \
|
|
y > yMax or z < zMin or z > zMax):
|
|
return
|
|
|
|
input.SetUpdateExtent(x,x,y,y,z,z)
|
|
input.Update()
|
|
numComps = input.GetNumberOfScalarComponents()
|
|
text = ""
|
|
for i in xrange(numComps):
|
|
val = input.GetScalarComponentAsDouble(x,y,z,i)
|
|
text = "%s %.1f" % (text,val)
|
|
|
|
self._WindowMapper.SetInput("(%d, %d): %s" % (x,y,text))
|
|
|
|
self.Render()
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# an example of how to use this widget
|
|
if __name__ == "__main__":
|
|
canvas = vtk.vtkImageCanvasSource2D()
|
|
canvas.SetNumberOfScalarComponents(3)
|
|
canvas.SetScalarType(3)
|
|
canvas.SetExtent(0,511,0,511,0,0)
|
|
canvas.SetDrawColor(100,100,0)
|
|
canvas.FillBox(0,511,0,511)
|
|
canvas.SetDrawColor(200,0,200)
|
|
canvas.FillBox(32,511,100,500)
|
|
canvas.SetDrawColor(100,0,0)
|
|
canvas.FillTube(550,20,30,400,5)
|
|
canvas.SetDrawColor(255,255,255)
|
|
canvas.DrawSegment3D(10,20,0,90,510,0)
|
|
canvas.SetDrawColor(200,50,50)
|
|
canvas.DrawSegment3D(510,90,0,10,20,0)
|
|
|
|
# Check segment clipping
|
|
canvas.SetDrawColor(0,200,0)
|
|
canvas.DrawSegment(-10,30,30,-10)
|
|
canvas.DrawSegment(-10,481,30,521)
|
|
canvas.DrawSegment(481,-10,521,30)
|
|
canvas.DrawSegment(481,521,521,481)
|
|
|
|
# Check Filling a triangle
|
|
canvas.SetDrawColor(20,200,200)
|
|
canvas.FillTriangle(-100,100,190,150,40,300)
|
|
|
|
# Check drawing a circle
|
|
canvas.SetDrawColor(250,250,10)
|
|
canvas.DrawCircle(350,350,200.0)
|
|
|
|
# Check drawing a point
|
|
canvas.SetDrawColor(250,250,250)
|
|
canvas.DrawPoint(350,350)
|
|
canvas.DrawPoint(350,550)
|
|
|
|
# Test filling functionality
|
|
canvas.SetDrawColor(55,0,0)
|
|
canvas.DrawCircle(450,350,80.0)
|
|
canvas.SetDrawColor(100,255,100)
|
|
canvas.FillPixel(450,350)
|
|
|
|
# Create the GUI: two renderer widgets and a quit button
|
|
|
|
frame = Tkinter.Frame()
|
|
|
|
widget = vtkTkImageViewerWidget(frame,width=512,height=512,double=1)
|
|
viewer = widget.GetImageViewer()
|
|
viewer.SetInput(canvas.GetOutput())
|
|
viewer.SetColorWindow(256)
|
|
viewer.SetColorLevel(127.5)
|
|
|
|
button = Tkinter.Button(frame,text="Quit",command=frame.quit)
|
|
|
|
widget.pack(side='top',padx=3,pady=3,fill='both',expand='t')
|
|
frame.pack(fill='both',expand='t')
|
|
button.pack(fill='x')
|
|
|
|
frame.mainloop()
|
|
|
|
|