This commit is contained in:
cjw
2026-02-12 23:22:11 +08:00
parent 7b09eb3d89
commit 89660bba4e
5988 changed files with 2517516 additions and 0 deletions
@@ -0,0 +1,4 @@
"""Tkinter widgets for VTK."""
__all__ = ['vtkTkRenderWidget', 'vtkTkImageViewerWidget',
'vtkTkRenderWindowInteractor', 'vtkTkPhotoImage']
@@ -0,0 +1,101 @@
import sys, os
import vtkmodules
from vtkmodules.vtkCommonCore import vtkVersion
def vtkLoadPythonTkWidgets(interp):
"""vtkLoadPythonTkWidgets(interp) -- load vtk-tk widget extensions
This is a mess of mixed python and tcl code that searches for the
shared object file that contains the python-vtk-tk widgets. Both
the python path and the tcl path are searched.
"""
modname = 'vtkRenderingTk'
VTK_VERSIONED_INSTALL = "ON"
VTK_CUSTOM_LIBRARY_VERSION = "9.5"
if VTK_VERSIONED_INSTALL in ("0", "OFF", "NO", "FALSE", "N"):
name = modname
elif VTK_CUSTOM_LIBRARY_VERSION:
name = '%s-%s' % (modname,VTK_CUSTOM_LIBRARY_VERSION)
else:
X = vtkVersion.GetVTKMajorVersion()
Y = vtkVersion.GetVTKMinorVersion()
name = '%s-%d.%d' % (modname,X,Y)
pkgname = modname.lower().capitalize()
# find out if the module is already loaded
loadedpkgs = interp.call('info', 'loaded')
found = False
try:
# check for result returned as a string
found = (loadedpkgs.find(pkgname) >= 0)
except AttributeError:
# check for result returned as nested tuples
for pkgtuple in loadedpkgs:
found |= (pkgname in pkgtuple)
if found:
return
# create the platform-dependent file name
prefix = ''
if sys.platform == 'cygwin':
prefix = 'cyg'
elif os.name == 'posix':
prefix = 'lib'
extension = interp.call('info', 'sharedlibextension')
filename = prefix+name+extension
# create an list of paths to search
vtkmodules_dir = os.path.dirname(vtkmodules.__file__)
pathlist = [vtkmodules_dir]
# a likely relative path for linux
if sys.platform == 'linux':
package_dir = os.path.dirname(vtkmodules_dir)
python_dir = os.path.dirname(package_dir)
if os.path.basename(python_dir).startswith('python'):
lib_dir = os.path.dirname(python_dir)
if os.path.basename(lib_dir).startswith('lib'):
pathlist.append(lib_dir)
# add tcl paths, ensure that {} is handled properly
try:
auto_paths = interp.getvar('auto_path').split()
except AttributeError:
auto_paths = interp.getvar('auto_path')
for path in auto_paths:
prev = str(pathlist[-1])
try:
# try block needed when one uses Gordon McMillan's Python
# Installer.
if len(prev) > 0 and prev[0] == '{' and prev[-1] != '}':
pathlist[-1] = prev+' '+path
else:
pathlist.append(path)
except AttributeError:
pass
# a common installation path
if os.name == 'posix':
pathlist.append('/usr/local/lib')
# attempt to load
for path in pathlist:
try:
# If the path object is not str, it means that it is a
# Tkinter path object.
if not isinstance(path, str):
path = path.string
# try block needed when one uses Gordon McMillan's Python
# Installer.
if len(path) > 0 and path[0] == '{' and path[-1] == '}':
path = path[1:-1]
fullpath = os.path.join(path, filename)
except AttributeError:
pass
if ' ' in fullpath:
fullpath = '{'+fullpath+'}'
if interp.eval('catch {load '+fullpath+' '+pkgname+'}') == '0':
return
# re-generate the error
interp.call('load', filename, pkgname)
@@ -0,0 +1,366 @@
"""
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
"""
from __future__ import absolute_import
import math, os, sys
from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
from vtkmodules.vtkInteractionImage import vtkImageViewer
from vtkmodules.vtkRenderingCore import vtkActor2D, vtkTextMapper
import tkinter
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 = 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 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 = 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 = vtkActor2D()
actor.SetMapper(mapper)
actor.SetLayerNumber(1)
actor.GetPositionCoordinate().SetValue(4,22)
actor.GetProperty().SetColor(1,1,0.5)
actor.SetVisibility(0)
imager.AddViewProp(actor)
self._LevelActor = actor
mapper = 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 = vtkActor2D()
actor.SetMapper(mapper)
actor.SetLayerNumber(1)
actor.GetPositionCoordinate().SetValue(4,4)
actor.GetProperty().SetColor(1,1,0.5)
actor.SetVisibility(0)
imager.AddViewProp(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 _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
if (not self._ImageViewer.GetRenderWindow().
IsA('vtkCocoaRenderWindow')):
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.UpdateInformation()
info = input.GetOutputInformation(0)
ext = info.Get(vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT())
ext[4] = z
ext[5] = z
input.Update(0, 1, 0, ext)
(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 extent of the image.
(xMin,xMax,yMin,yMax,zMin,zMax) = input.GetExtent()
if (x < xMin or x > xMax or y < yMin or \
y > yMax or z < zMin or z > zMax):
return
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__":
from vtkmodules.vtkImagingSources import vtkImageCanvasSource2D
# load implementations for rendering and interaction factory classes
import vtkmodules.vtkRenderingOpenGL2
import vtkmodules.vtkInteractionStyle
canvas = 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.SetInputConnection(canvas.GetOutputPort())
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()
@@ -0,0 +1,28 @@
"""
A subclass of tkinter.PhotoImage that connects a
vtkImageData to a photo widget.
Created by Daniel Blezek, August 2002
"""
from __future__ import absolute_import
import sys
import tkinter
from .vtkLoadPythonTkWidgets import vtkLoadPythonTkWidgets
class vtkTkPhotoImage ( tkinter.PhotoImage ):
"""
A subclass of PhotoImage with helper functions
for displaying vtkImageData
"""
def __init__ ( self, **kw ):
# Caller the superclass
tkinter.PhotoImage.__init__ ( self, kw )
vtkLoadPythonTkWidgets ( self.tk )
def PutImageSlice ( self, image, z, orientation='transverse', window=256, level=128 ):
t = str ( image.__this__ )
s = 'vtkImageDataToTkPhoto %s %s %d %s %d %d' % ( t, self.name, z, orientation, window, level )
self.tk.eval ( s )
@@ -0,0 +1,474 @@
"""
A simple vtkTkRenderWidget for tkinter.
Created by David Gobbi, April 1999
May ??, 1999 - Modifications performed by Heather Drury,
to rewrite _pan to match method in TkInteractor.tcl
May 11, 1999 - Major rewrite by David Gobbi to make the
interactor bindings identical to the TkInteractor.tcl
bindings.
July 14, 1999 - Added modification by Ken Martin for VTK 2.4, to
use vtk widgets instead of Togl.
Aug 29, 1999 - Renamed file to vtkRenderWidget.py
Nov 14, 1999 - Added support for keyword 'rw'
Mar 23, 2000 - Extensive but backwards compatible changes,
improved documentation
A few important notes:
This class is meant to be used as a base-class widget for
doing VTK rendering in Python.
In VTK (and C++) there is a very important distinction between
public ivars (attributes in pythonspeak), protected ivars, and
private ivars. When you write a python class that you want
to 'look and feel' like a VTK class, you should follow these rules.
1) Attributes should never be public. Attributes should always be
either protected (prefixed with a single underscore) or private
(prefixed with a double underscore). You can provide access to
attributes through public Set/Get methods (same as VTK).
2) Use a single underscore to denote a protected attribute, e.g.
self._RenderWindow is protected (can be accessed from this
class or a derived class).
3) Use a double underscore to denote a private attribute, e.g.
self.__InExpose cannot be accessed outside of this class.
All attributes should be 'declared' in the __init__() function
i.e. set to some initial value. Don't forget that 'None' means
'NULL' - the python/vtk wrappers guarantee their equivalence.
"""
from __future__ import absolute_import
import math, os, sys
from vtkmodules.vtkRenderingCore import vtkCellPicker, vtkProperty, vtkRenderWindow
import tkinter
from .vtkLoadPythonTkWidgets import vtkLoadPythonTkWidgets
class vtkTkRenderWidget(tkinter.Widget):
"""
A vtkTkRenderWidget for Python.
Use GetRenderWindow() to get the vtkRenderWindow.
Create with the keyword stereo=1 in order to generate a
stereo-capable window.
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:
rw -- Use passed render window instead of creating a new one.
stereo -- If True, generate a stereo-capable window.
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: # check to see if a render window was specified
renderWindow = kw['rw']
except KeyError:
renderWindow = vtkRenderWindow()
try: # was a stereo rendering context requested?
if kw['stereo']:
renderWindow.StereoCapableWindowOn()
del kw['stereo']
except KeyError:
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['rw'] = renderWindow.GetAddressAsString("vtkRenderWindow")
tkinter.Widget.__init__(self, master, 'vtkTkRenderWidget', cnf, kw)
self._CurrentRenderer = None
self._CurrentCamera = None
self._CurrentZoom = 1.0
self._CurrentLight = None
self._ViewportCenterX = 0
self._ViewportCenterY = 0
self._Picker = vtkCellPicker()
self._PickedAssembly = None
self._PickedProperty = vtkProperty()
self._PickedProperty.SetColor(1,0,0)
self._PrePickedProperty = None
self._OldFocus = None
# used by the LOD actors
self._DesiredUpdateRate = 15
self._StillUpdateRate = 0.0001
# these record the previous mouse position
self._LastX = 0
self._LastY = 0
# private attributes
self.__InExpose = 0
# create the Tk bindings
self.BindTkRenderWidget()
def __getattr__(self,attr):
# because the tk part of vtkTkRenderWidget must have
# the only remaining reference to the RenderWindow when
# it is destroyed, we can't actually store the RenderWindow
# as an attribute but instead have to get it from the tk-side
if attr == '_RenderWindow':
return self.GetRenderWindow()
raise AttributeError(self.__class__.__name__ +
" has no attribute named " + attr)
def BindTkRenderWidget(self):
"""
Bind some default actions.
"""
self.bind("<ButtonPress>",
lambda e,s=self: s.StartMotion(e.x,e.y))
self.bind("<ButtonRelease>",
lambda e,s=self: s.EndMotion(e.x,e.y))
self.bind("<B1-Motion>",
lambda e,s=self: s.Rotate(e.x,e.y))
self.bind("<B2-Motion>",
lambda e,s=self: s.Pan(e.x,e.y))
self.bind("<B3-Motion>",
lambda e,s=self: s.Zoom(e.x,e.y))
self.bind("<Shift-B1-Motion>",
lambda e,s=self: s.Pan(e.x,e.y))
self.bind("<KeyPress-r>",
lambda e,s=self: s.Reset(e.x,e.y))
self.bind("<KeyPress-u>",
lambda e,s=self: s.deiconify())
self.bind("<KeyPress-w>",
lambda e,s=self: s.Wireframe())
self.bind("<KeyPress-s>",
lambda e,s=self: s.Surface())
self.bind("<KeyPress-p>",
lambda e,s=self: s.PickActor(e.x,e.y))
if self._FocusOnEnter:
self.bind("<Enter>",
lambda e,s=self: s.Enter(e.x,e.y))
self.bind("<Leave>",
lambda e,s=self: s.Leave(e.x,e.y))
else:
self.bind("<ButtonPress>",
lambda e,s=self: s.Enter(e.x,e.y))
self.bind("<Expose>",
lambda e,s=self: s.Expose())
def GetZoomFactor(self):
return self._CurrentZoom
def SetDesiredUpdateRate(self, rate):
"""Mirrors the method with the same name in
vtkRenderWindowInteractor."""
self._DesiredUpdateRate = rate
def GetDesiredUpdateRate(self):
"""Mirrors the method with the same name in
vtkRenderWindowInteractor."""
return self._DesiredUpdateRate
def SetStillUpdateRate(self, rate):
"""Mirrors the method with the same name in
vtkRenderWindowInteractor."""
self._StillUpdateRate = rate
def GetStillUpdateRate(self):
"""Mirrors the method with the same name in
vtkRenderWindowInteractor."""
return self._StillUpdateRate
def GetRenderWindow(self):
addr = self.tk.call(self._w, 'GetRenderWindow')[5:]
return vtkRenderWindow('_%s_vtkRenderWindow_p' % addr)
def GetPicker(self):
return self._Picker
def Expose(self):
if (not self.__InExpose):
self.__InExpose = 1
if (not self._RenderWindow.IsA('vtkCocoaRenderWindow')):
self.update()
self._RenderWindow.Render()
self.__InExpose = 0
def Render(self):
if (self._CurrentLight):
light = self._CurrentLight
light.SetPosition(self._CurrentCamera.GetPosition())
light.SetFocalPoint(self._CurrentCamera.GetFocalPoint())
self._RenderWindow.Render()
def UpdateRenderer(self,x,y):
"""
UpdateRenderer will identify the renderer under the mouse and set
up _CurrentRenderer, _CurrentCamera, and _CurrentLight.
"""
windowX = self.winfo_width()
windowY = self.winfo_height()
renderers = self._RenderWindow.GetRenderers()
numRenderers = renderers.GetNumberOfItems()
self._CurrentRenderer = None
renderers.InitTraversal()
for i in range(0,numRenderers):
renderer = renderers.GetNextItem()
vx,vy = (0,0)
if (windowX > 1):
vx = float(x)/(windowX-1)
if (windowY > 1):
vy = (windowY-float(y)-1)/(windowY-1)
(vpxmin,vpymin,vpxmax,vpymax) = renderer.GetViewport()
if (vx >= vpxmin and vx <= vpxmax and
vy >= vpymin and vy <= vpymax):
self._CurrentRenderer = renderer
self._ViewportCenterX = float(windowX)*(vpxmax-vpxmin)/2.0\
+vpxmin
self._ViewportCenterY = float(windowY)*(vpymax-vpymin)/2.0\
+vpymin
self._CurrentCamera = self._CurrentRenderer.GetActiveCamera()
lights = self._CurrentRenderer.GetLights()
lights.InitTraversal()
self._CurrentLight = lights.GetNextItem()
break
self._LastX = x
self._LastY = y
def GetCurrentRenderer(self):
return self._CurrentRenderer
def Enter(self,x,y):
self._OldFocus=self.focus_get()
self.focus()
self.StartMotion(x, y)
def Leave(self,x,y):
if (self._OldFocus != None):
self._OldFocus.focus()
def StartMotion(self,x,y):
self.GetRenderWindow().SetDesiredUpdateRate(self._DesiredUpdateRate)
self.UpdateRenderer(x,y)
def EndMotion(self,x,y):
self.GetRenderWindow().SetDesiredUpdateRate(self._StillUpdateRate)
if self._CurrentRenderer:
self.Render()
def Rotate(self,x,y):
if self._CurrentRenderer:
self._CurrentCamera.Azimuth(self._LastX - x)
self._CurrentCamera.Elevation(y - self._LastY)
self._CurrentCamera.OrthogonalizeViewUp()
self._LastX = x
self._LastY = y
self._CurrentRenderer.ResetCameraClippingRange()
self.Render()
def Pan(self,x,y):
if self._CurrentRenderer:
renderer = self._CurrentRenderer
camera = self._CurrentCamera
(pPoint0,pPoint1,pPoint2) = camera.GetPosition()
(fPoint0,fPoint1,fPoint2) = camera.GetFocalPoint()
if (camera.GetParallelProjection()):
renderer.SetWorldPoint(fPoint0,fPoint1,fPoint2,1.0)
renderer.WorldToDisplay()
fx,fy,fz = renderer.GetDisplayPoint()
renderer.SetDisplayPoint(fx-x+self._LastX,
fy+y-self._LastY,
fz)
renderer.DisplayToWorld()
fx,fy,fz,fw = renderer.GetWorldPoint()
camera.SetFocalPoint(fx,fy,fz)
renderer.SetWorldPoint(pPoint0,pPoint1,pPoint2,1.0)
renderer.WorldToDisplay()
fx,fy,fz = renderer.GetDisplayPoint()
renderer.SetDisplayPoint(fx-x+self._LastX,
fy+y-self._LastY,
fz)
renderer.DisplayToWorld()
fx,fy,fz,fw = renderer.GetWorldPoint()
camera.SetPosition(fx,fy,fz)
else:
(fPoint0,fPoint1,fPoint2) = camera.GetFocalPoint()
# Specify a point location in world coordinates
renderer.SetWorldPoint(fPoint0,fPoint1,fPoint2,1.0)
renderer.WorldToDisplay()
# Convert world point coordinates to display coordinates
dPoint = renderer.GetDisplayPoint()
focalDepth = dPoint[2]
aPoint0 = self._ViewportCenterX + (x - self._LastX)
aPoint1 = self._ViewportCenterY - (y - self._LastY)
renderer.SetDisplayPoint(aPoint0,aPoint1,focalDepth)
renderer.DisplayToWorld()
(rPoint0,rPoint1,rPoint2,rPoint3) = renderer.GetWorldPoint()
if (rPoint3 != 0.0):
rPoint0 = rPoint0/rPoint3
rPoint1 = rPoint1/rPoint3
rPoint2 = rPoint2/rPoint3
camera.SetFocalPoint((fPoint0 - rPoint0) + fPoint0,
(fPoint1 - rPoint1) + fPoint1,
(fPoint2 - rPoint2) + fPoint2)
camera.SetPosition((fPoint0 - rPoint0) + pPoint0,
(fPoint1 - rPoint1) + pPoint1,
(fPoint2 - rPoint2) + pPoint2)
self._LastX = x
self._LastY = y
self.Render()
def Zoom(self,x,y):
if self._CurrentRenderer:
renderer = self._CurrentRenderer
camera = self._CurrentCamera
zoomFactor = math.pow(1.02,(0.5*(self._LastY - y)))
self._CurrentZoom = self._CurrentZoom * zoomFactor
if camera.GetParallelProjection():
parallelScale = camera.GetParallelScale()/zoomFactor
camera.SetParallelScale(parallelScale)
else:
camera.Dolly(zoomFactor)
renderer.ResetCameraClippingRange()
self._LastX = x
self._LastY = y
self.Render()
def Reset(self,x,y):
if self._CurrentRenderer:
self._CurrentRenderer.ResetCamera()
self.Render()
def Wireframe(self):
actors = self._CurrentRenderer.GetActors()
numActors = actors.GetNumberOfItems()
actors.InitTraversal()
for i in range(0,numActors):
actor = actors.GetNextItem()
actor.GetProperty().SetRepresentationToWireframe()
self.Render()
def Surface(self):
actors = self._CurrentRenderer.GetActors()
numActors = actors.GetNumberOfItems()
actors.InitTraversal()
for i in range(0,numActors):
actor = actors.GetNextItem()
actor.GetProperty().SetRepresentationToSurface()
self.Render()
def PickActor(self,x,y):
if self._CurrentRenderer:
renderer = self._CurrentRenderer
picker = self._Picker
windowY = self.winfo_height()
picker.Pick(x,(windowY - y - 1),0.0,renderer)
assembly = picker.GetAssembly()
if (self._PickedAssembly != None and
self._PrePickedProperty != None):
self._PickedAssembly.SetProperty(self._PrePickedProperty)
# release hold of the property
self._PrePickedProperty.UnRegister(self._PrePickedProperty)
self._PrePickedProperty = None
if (assembly != None):
self._PickedAssembly = assembly
self._PrePickedProperty = self._PickedAssembly.GetProperty()
# hold onto the property
self._PrePickedProperty.Register(self._PrePickedProperty)
self._PickedAssembly.SetProperty(self._PickedProperty)
self.Render()
#----------------------------------------------------------------------------
def vtkRenderWidgetConeExample():
"""Like it says, just a simple example
"""
from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
# load implementations for rendering and interaction factory classes
import vtkmodules.vtkRenderingOpenGL2
import vtkmodules.vtkInteractionStyle
# create root window
root = tkinter.Tk()
# create vtkTkRenderWidget
pane = vtkTkRenderWidget(root,width=300,height=300)
ren = vtkRenderer()
pane.GetRenderWindow().AddRenderer(ren)
cone = vtkConeSource()
cone.SetResolution(8)
coneMapper = vtkPolyDataMapper()
coneMapper.SetInputConnection(cone.GetOutputPort())
coneActor = vtkActor()
coneActor.SetMapper(coneMapper)
ren.AddActor(coneActor)
# pack the pane into the tk root
pane.pack()
# start the tk mainloop
root.mainloop()
if __name__ == "__main__":
vtkRenderWidgetConeExample()
@@ -0,0 +1,411 @@
"""
A fully functional VTK widget for tkinter that uses
vtkGenericRenderWindowInteractor. The widget is called
vtkTkRenderWindowInteractor. The initialization part of this code is
similar to that of the vtkTkRenderWidget.
Created by Prabhu Ramachandran, April 2002
"""
from __future__ import absolute_import
import math, os, sys
from vtkmodules.vtkRenderingCore import vtkRenderWindow
from vtkmodules.vtkRenderingUI import vtkGenericRenderWindowInteractor
import tkinter
from .vtkLoadPythonTkWidgets import vtkLoadPythonTkWidgets
class vtkTkRenderWindowInteractor(tkinter.Widget):
""" A vtkTkRenderWidndowInteractor for Python.
Use GetRenderWindow() to get the vtkRenderWindow.
Create with the keyword stereo=1 in order to generate a
stereo-capable window.
Create with the keyword focus_on_enter=1 to enable
focus-follows-mouse. The default is for a click-to-focus mode.
__getattr__ is used to make the widget also behave like a
vtkGenericRenderWindowInteractor.
"""
def __init__(self, master, cnf={}, **kw):
"""
Constructor.
Keyword arguments:
rw -- Use passed render window instead of creating a new one.
stereo -- If True, generate a stereo-capable window.
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: # check to see if a render window was specified
renderWindow = kw['rw']
except KeyError:
renderWindow = vtkRenderWindow()
try: # was a stereo rendering context requested?
if kw['stereo']:
renderWindow.StereoCapableWindowOn()
del kw['stereo']
except KeyError:
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['rw'] = renderWindow.GetAddressAsString("vtkRenderWindow")
tkinter.Widget.__init__(self, master, 'vtkTkRenderWidget', cnf, kw)
self._Iren = vtkGenericRenderWindowInteractor()
self._Iren.SetRenderWindow(self._RenderWindow)
self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)
self._OldFocus = None
# private attributes
self.__InExpose = 0
# create the Tk bindings
self.BindEvents()
#self.tk_focusFollowsMouse()
def __getattr__(self, attr):
# because the tk part of vtkTkRenderWidget must have
# the only remaining reference to the RenderWindow when
# it is destroyed, we can't actually store the RenderWindow
# as an attribute but instead have to get it from the tk-side
if attr == '__vtk__':
return lambda t=self._Iren: t
elif attr == '_RenderWindow':
return self.GetRenderWindow()
elif hasattr(self._Iren, attr):
return getattr(self._Iren, attr)
else:
raise AttributeError(self.__class__.__name__ +
" has no attribute named " + attr)
def BindEvents(self):
""" Bind all the events. """
self.bind("<Motion>",
lambda e, s=self: s.MouseMoveEvent(e, 0, 0))
self.bind("<Control-Motion>",
lambda e, s=self: s.MouseMoveEvent(e, 1, 0))
self.bind("<Shift-Motion>",
lambda e, s=self: s.MouseMoveEvent(e, 1, 1))
self.bind("<Control-Shift-Motion>",
lambda e, s=self: s.MouseMoveEvent(e, 0, 1))
# Left Button
self.bind("<ButtonPress-1>",
lambda e, s=self: s.LeftButtonPressEvent(e, 0, 0))
self.bind("<Control-ButtonPress-1>",
lambda e, s=self: s.LeftButtonPressEvent(e, 1, 0))
self.bind("<Shift-ButtonPress-1>",
lambda e, s=self: s.LeftButtonPressEvent(e, 0, 1))
self.bind("<Control-Shift-ButtonPress-1>",
lambda e, s=self: s.LeftButtonPressEvent(e, 1, 1))
self.bind("<ButtonRelease-1>",
lambda e, s=self: s.LeftButtonReleaseEvent(e, 0, 0))
self.bind("<Control-ButtonRelease-1>",
lambda e, s=self: s.LeftButtonReleaseEvent(e, 1, 0))
self.bind("<Shift-ButtonRelease-1>",
lambda e, s=self: s.LeftButtonReleaseEvent(e, 0, 1))
self.bind("<Control-Shift-ButtonRelease-1>",
lambda e, s=self: s.LeftButtonReleaseEvent(e, 1, 1))
# Middle Button
self.bind("<ButtonPress-2>",
lambda e, s=self: s.MiddleButtonPressEvent(e, 0, 0))
self.bind("<Control-ButtonPress-2>",
lambda e, s=self: s.MiddleButtonPressEvent(e, 1, 0))
self.bind("<Shift-ButtonPress-2>",
lambda e, s=self: s.MiddleButtonPressEvent(e, 0, 1))
self.bind("<Control-Shift-ButtonPress-2>",
lambda e, s=self: s.MiddleButtonPressEvent(e, 1, 1))
self.bind("<ButtonRelease-2>",
lambda e, s=self: s.MiddleButtonReleaseEvent(e, 0, 0))
self.bind("<Control-ButtonRelease-2>",
lambda e, s=self: s.MiddleButtonReleaseEvent(e, 1, 0))
self.bind("<Shift-ButtonRelease-2>",
lambda e, s=self: s.MiddleButtonReleaseEvent(e, 0, 1))
self.bind("<Control-Shift-ButtonRelease-2>",
lambda e, s=self: s.MiddleButtonReleaseEvent(e, 1, 1))
# Right Button
self.bind("<ButtonPress-3>",
lambda e, s=self: s.RightButtonPressEvent(e, 0, 0))
self.bind("<Control-ButtonPress-3>",
lambda e, s=self: s.RightButtonPressEvent(e, 1, 0))
self.bind("<Shift-ButtonPress-3>",
lambda e, s=self: s.RightButtonPressEvent(e, 0, 1))
self.bind("<Control-Shift-ButtonPress-3>",
lambda e, s=self: s.RightButtonPressEvent(e, 1, 1))
self.bind("<ButtonRelease-3>",
lambda e, s=self: s.RightButtonReleaseEvent(e, 0, 0))
self.bind("<Control-ButtonRelease-3>",
lambda e, s=self: s.RightButtonReleaseEvent(e, 1, 0))
self.bind("<Shift-ButtonRelease-3>",
lambda e, s=self: s.RightButtonReleaseEvent(e, 0, 1))
self.bind("<Control-Shift-ButtonRelease-3>",
lambda e, s=self: s.RightButtonReleaseEvent(e, 1, 1))
if sys.platform == 'win32':
self.bind("<MouseWheel>",
lambda e, s=self: s.MouseWheelEvent(e, 0, 0))
self.bind("<Control-MouseWheel>",
lambda e, s=self: s.MouseWheelEvent(e, 1, 0))
self.bind("<Shift-MouseWheel>",
lambda e, s=self: s.MouseWheelEvent(e, 0, 1))
self.bind("<Control-Shift-MouseWheel>",
lambda e, s=self: s.MouseWheelEvent(e, 1, 1))
else:
# Mouse wheel forward event
self.bind("<ButtonPress-4>",
lambda e, s=self: s.MouseWheelForwardEvent(e, 0, 0))
self.bind("<Control-ButtonPress-4>",
lambda e, s=self: s.MouseWheelForwardEvent(e, 1, 0))
self.bind("<Shift-ButtonPress-4>",
lambda e, s=self: s.MouseWheelForwardEvent(e, 0, 1))
self.bind("<Control-Shift-ButtonPress-4>",
lambda e, s=self: s.MouseWheelForwardEvent(e, 1, 1))
# Mouse wheel backward event
self.bind("<ButtonPress-5>",
lambda e, s=self: s.MouseWheelBackwardEvent(e, 0, 0))
self.bind("<Control-ButtonPress-5>",
lambda e, s=self: s.MouseWheelBackwardEvent(e, 1, 0))
self.bind("<Shift-ButtonPress-5>",
lambda e, s=self: s.MouseWheelBackwardEvent(e, 0, 1))
self.bind("<Control-Shift-ButtonPress-5>",
lambda e, s=self: s.MouseWheelBackwardEvent(e, 1, 1))
# Key related events
self.bind("<KeyPress>",
lambda e, s=self: s.KeyPressEvent(e, 0, 0))
self.bind("<Control-KeyPress>",
lambda e, s=self: s.KeyPressEvent(e, 1, 0))
self.bind("<Shift-KeyPress>",
lambda e, s=self: s.KeyPressEvent(e, 0, 1))
self.bind("<Control-Shift-KeyPress>",
lambda e, s=self: s.KeyPressEvent(e, 1, 1))
self.bind("<KeyRelease>",
lambda e, s=self: s.KeyReleaseEvent(e, 0, 0))
self.bind("<Control-KeyRelease>",
lambda e, s=self: s.KeyReleaseEvent(e, 1, 0))
self.bind("<Shift-KeyRelease>",
lambda e, s=self: s.KeyReleaseEvent(e, 0, 1))
self.bind("<Control-Shift-KeyRelease>",
lambda e, s=self: s.KeyReleaseEvent(e, 1, 1))
self.bind("<Enter>",
lambda e, s=self: s.EnterEvent(e, 0, 0))
self.bind("<Control-Enter>",
lambda e, s=self: s.EnterEvent(e, 1, 0))
self.bind("<Shift-Enter>",
lambda e, s=self: s.EnterEvent(e, 0, 1))
self.bind("<Control-Shift-Enter>",
lambda e, s=self: s.EnterEvent(e, 1, 1))
self.bind("<Leave>",
lambda e, s=self: s.LeaveEvent(e, 0, 0))
self.bind("<Control-Leave>",
lambda e, s=self: s.LeaveEvent(e, 1, 0))
self.bind("<Shift-Leave>",
lambda e, s=self: s.LeaveEvent(e, 0, 1))
self.bind("<Control-Shift-Leave>",
lambda e, s=self: s.LeaveEvent(e, 1, 1))
self.bind("<Configure>", self.ConfigureEvent)
self.bind("<Expose>",lambda e,s=self: s.ExposeEvent())
def CreateTimer(self, obj, evt):
self.after(10, self._Iren.TimerEvent)
def DestroyTimer(self, obj, event):
"""The timer is a one shot timer so will expire automatically."""
return 1
def _GrabFocus(self, enter=0):
self._OldFocus=self.focus_get()
self.focus()
def MouseMoveEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
self._Iren.MouseMoveEvent()
def LeftButtonPressEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
self._Iren.LeftButtonPressEvent()
if not self._FocusOnEnter:
self._GrabFocus()
def LeftButtonReleaseEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
self._Iren.LeftButtonReleaseEvent()
def MiddleButtonPressEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
self._Iren.MiddleButtonPressEvent()
if not self._FocusOnEnter:
self._GrabFocus()
def MiddleButtonReleaseEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
self._Iren.MiddleButtonReleaseEvent()
def RightButtonPressEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
self._Iren.RightButtonPressEvent()
if not self._FocusOnEnter:
self._GrabFocus()
def RightButtonReleaseEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
self._Iren.RightButtonReleaseEvent()
def MouseWheelEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
if event.delta > 0:
self._Iren.MouseWheelForwardEvent()
else:
self._Iren.MouseWheelBackwardEvent()
def MouseWheelForwardEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
self._Iren.MouseWheelForwardEvent()
def MouseWheelBackwardEvent(self, event, ctrl, shift):
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, chr(0), 0, None)
self._Iren.MouseWheelBackwardEvent()
def KeyPressEvent(self, event, ctrl, shift):
key = chr(0)
if event.keysym_num < 128:
key = chr(event.keysym_num)
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, key, 0, event.keysym)
self._Iren.KeyPressEvent()
self._Iren.CharEvent()
def KeyReleaseEvent(self, event, ctrl, shift):
key = chr(0)
if event.keysym_num < 128:
key = chr(event.keysym_num)
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
shift, key, 0, event.keysym)
self._Iren.KeyReleaseEvent()
def ConfigureEvent(self, event):
oldwidth, oldheight = self._Iren.GetSize()
self._Iren.SetSize(event.width, event.height)
self._Iren.ConfigureEvent()
# check whether if the window has expanded vs shrunk
if event.width <= oldwidth and event.height <= oldheight:
# there will be no ExposeEvent if the window didn't grow,
# so post a render to occur after any event processing
self.after(0, self.Render)
def EnterEvent(self, event, ctrl, shift):
if self._FocusOnEnter:
self._GrabFocus()
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl, shift,
chr(0), 0, None)
self._Iren.EnterEvent()
def LeaveEvent(self, event, ctrl, shift):
if self._FocusOnEnter and (self._OldFocus != None):
self._OldFocus.focus()
self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl, shift,
chr(0), 0, None)
self._Iren.LeaveEvent()
def ExposeEvent(self):
if (not self.__InExpose):
self.__InExpose = 1
if (not self._RenderWindow.IsA('vtkCocoaRenderWindow')):
self.update()
self._RenderWindow.Render()
self.__InExpose = 0
def GetRenderWindow(self):
addr = self.tk.call(self._w, 'GetRenderWindow')[5:]
return vtkRenderWindow('_%s_vtkRenderWindow_p' % addr)
def Render(self):
self._RenderWindow.Render()
#----------------------------------------------------------------------------
def vtkRenderWindowInteractorConeExample():
"""Like it says, just a simple example
"""
from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
# load implementations for rendering and interaction factory classes
import vtkmodules.vtkRenderingOpenGL2
import vtkmodules.vtkInteractionStyle
# create root window
root = tkinter.Tk()
# create vtkTkRenderWidget
pane = vtkTkRenderWindowInteractor(root, width=300, height=300)
pane.Initialize()
def quit(obj=root):
obj.quit()
pane.AddObserver("ExitEvent", lambda o,e,q=quit: q())
ren = vtkRenderer()
pane.GetRenderWindow().AddRenderer(ren)
cone = vtkConeSource()
cone.SetResolution(8)
coneMapper = vtkPolyDataMapper()
coneMapper.SetInputConnection(cone.GetOutputPort())
coneActor = vtkActor()
coneActor.SetMapper(coneMapper)
ren.AddActor(coneActor)
# pack the pane into the tk root
pane.pack(fill='both', expand=1)
pane.Start()
# start the tk mainloop
root.mainloop()
if __name__ == "__main__":
vtkRenderWindowInteractorConeExample()