Commit a5261418 authored by Jan Tojnar's avatar Jan Tojnar
Browse files

wip: Try to make rendering inside GtkGLArea work

This attempts to port the GTK example away from
the GTK 2-era GtkGLArea library and PyGTK
to modern PyGObject and GTK’s built-in GtkGLArea.

Also switched to vtkGenericOpenGLRenderWindow since
that should work with custom contexts.

Gtk.Widget.get_pointer method is also deprecated so I switched
used GtkEventControllerMotion.

Currently does not work	– displays garbled mess in the widget.

vtk/vtk#18395
parent 4ff08689
"""
Description:
Provides a simple VTK widget for pyGtk. This embeds a
Provides a simple VTK widget for GTK 3 with pygobject3. This embeds a
vtkRenderWindow inside a GTK widget. This is based on
vtkTkRenderWidget.py. The GtkVTKRenderWindowBase class provides the
abstraction necessary for someone to use their own interaction
behaviour. The method names are similar to those in
vtkInteractorStyle.h.
The class uses the gtkgl.GtkGLArea widget (gtkglarea). This avoids
The class uses the Gtk.GLArea widget. This avoids
a lot of problems with flicker.
There is a working example at the bottom.
......@@ -36,12 +36,15 @@ Bugs:
"""
import gtk, GDK, gtkgl
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from vtkmodules.vtkRenderingCore import vtkCellPicker, vtkProperty, vtkRenderWindow
import math
class GtkVTKRenderWindowBase(gtkgl.GtkGLArea):
class GtkVTKRenderWindowBase(Gtk.GLArea):
""" A base class that enables one to embed a vtkRenderWindow into
a pyGTK widget. This class embeds the RenderWindow correctly.
......@@ -51,11 +54,8 @@ class GtkVTKRenderWindowBase(gtkgl.GtkGLArea):
vtkInteractorStyle class included with VTK. """
def __init__(self, *args):
l = list(args)
attr = (gtkgl.RGBA, gtkgl.DOUBLEBUFFER)
l.insert(0, self)
l.insert(1, attr)
apply(gtkgl.GtkGLArea.__init__, l)
# attr = (Gtk.RGBA, Gtk.DOUBLEBUFFER)
Gtk.GLArea.__init__(self, *args)
self._RenderWindow = vtkRenderWindow()
# private attributes
......@@ -68,13 +68,13 @@ class GtkVTKRenderWindowBase(gtkgl.GtkGLArea):
self.ConnectSignals()
# need this to be able to handle key_press events.
self.set_flags(gtk.CAN_FOCUS)
self.set_can_focus(True)
# default size
self.set_usize(300, 300)
self.set_size_request(300, 300)
def ConnectSignals(self):
self.connect("realize", self.OnRealize)
self.connect("expose_event", self.OnExpose)
self.connect("render", self.OnRender)
self.connect("configure_event", self.OnConfigure)
self.connect("button_press_event", self.OnButtonDown)
self.connect("button_release_event", self.OnButtonUp)
......@@ -83,12 +83,14 @@ class GtkVTKRenderWindowBase(gtkgl.GtkGLArea):
self.connect("leave_notify_event", self.OnLeave)
self.connect("key_press_event", self.OnKeyPress)
self.connect("delete_event", self.OnDestroy)
self.add_events(GDK.EXPOSURE_MASK| GDK.BUTTON_PRESS_MASK |
GDK.BUTTON_RELEASE_MASK |
GDK.KEY_PRESS_MASK |
GDK.POINTER_MOTION_MASK |
GDK.POINTER_MOTION_HINT_MASK |
GDK.ENTER_NOTIFY_MASK | GDK.LEAVE_NOTIFY_MASK)
self.add_events(Gdk.EventMask.EXPOSURE_MASK |
Gdk.EventMask.BUTTON_PRESS_MASK |
Gdk.EventMask.BUTTON_RELEASE_MASK |
Gdk.EventMask.KEY_PRESS_MASK |
Gdk.EventMask.POINTER_MOTION_MASK |
Gdk.EventMask.POINTER_MOTION_HINT_MASK |
Gdk.EventMask.ENTER_NOTIFY_MASK |
Gdk.EventMask.LEAVE_NOTIFY_MASK)
def GetRenderWindow(self):
return self._RenderWindow
......@@ -122,59 +124,57 @@ class GtkVTKRenderWindowBase(gtkgl.GtkGLArea):
self._RenderWindow.Render()
def OnRealize(self, *args):
if self.__Created == 0:
# you can't get the xid without the window being realized.
self.realize()
win_id = str(self.get_window().xid)
self._RenderWindow.SetWindowInfo(win_id)
self.__Created = 1
return gtk.TRUE
self.make_current()
if self.get_error() is not None:
raise RuntimeError('Error in making current')
return True
def OnConfigure(self, wid, event=None):
sz = self._RenderWindow.GetSize()
if (event.width != sz[0]) or (event.height != sz[1]):
self._RenderWindow.SetSize(event.width, event.height)
return gtk.TRUE
return True
def OnExpose(self, *args):
def OnRender(self, *args):
self.Render()
return gtk.TRUE
return True
def OnDestroy(self, event=None):
self.hide()
del self._RenderWindow
self.destroy()
return gtk.TRUE
return True
def OnButtonDown(self, wid, event):
"""Mouse button pressed."""
self._RenderWindow.SetDesiredUpdateRate(self._DesiredUpdateRate)
return gtk.TRUE
return True
def OnButtonUp(self, wid, event):
"""Mouse button released."""
self._RenderWindow.SetDesiredUpdateRate(self._StillUpdateRate)
return gtk.TRUE
return True
def OnMouseMove(self, wid, event):
"""Mouse has moved."""
return gtk.TRUE
return True
def OnEnter(self, wid, event):
"""Entering the vtkRenderWindow."""
return gtk.TRUE
return True
def OnLeave(self, wid, event):
"""Leaving the vtkRenderWindow."""
return gtk.TRUE
return True
def OnKeyPress(self, wid, event):
"""Key pressed."""
return gtk.TRUE
return True
def OnKeyRelease(self, wid, event):
"Key released."
return gtk.TRUE
return True
class GtkVTKRenderWindow(GtkVTKRenderWindowBase):
......@@ -183,9 +183,7 @@ class GtkVTKRenderWindow(GtkVTKRenderWindowBase):
based on the vtkRenderWidget.py provided with the VTK sources."""
def __init__(self, *args):
l = list(args)
l.insert(0, self)
apply(GtkVTKRenderWindowBase.__init__, l)
GtkVTKRenderWindowBase.__init__(self, *args)
self._CurrentRenderer = None
self._CurrentCamera = None
......@@ -216,51 +214,51 @@ class GtkVTKRenderWindow(GtkVTKRenderWindowBase):
return self.EndMotion(wid, event)
def OnMouseMove(self, wid, event=None):
if ((event.state & GDK.BUTTON1_MASK) == GDK.BUTTON1_MASK):
if ((event.state & GDK.SHIFT_MASK) == GDK.SHIFT_MASK):
if ((event.state & Gdk.ModifierType.BUTTON1_MASK) == Gdk.ModifierType.BUTTON1_MASK):
if ((event.state & Gdk.ModifierType.SHIFT_MASK) == Gdk.ModifierType.SHIFT_MASK):
m = self.get_pointer()
self.Pan(m[0], m[1])
return gtk.TRUE
self.Pan(m.x, m.y)
return True
else:
m = self.get_pointer()
self.Rotate(m[0], m[1])
return gtk.TRUE
elif ((event.state & GDK.BUTTON2_MASK) == GDK.BUTTON2_MASK):
self.Rotate(m.x, m.y)
return True
elif ((event.state & Gdk.ModifierType.BUTTON2_MASK) == Gdk.ModifierType.BUTTON2_MASK):
m = self.get_pointer()
self.Pan(m[0], m[1])
return gtk.TRUE
elif ((event.state & GDK.BUTTON3_MASK) == GDK.BUTTON3_MASK):
self.Pan(m.x, m.y)
return True
elif ((event.state & Gdk.ModifierType.BUTTON3_MASK) == Gdk.ModifierType.BUTTON3_MASK):
m = self.get_pointer()
self.Zoom(m[0], m[1])
return gtk.TRUE
self.Zoom(m.x, m.y)
return True
else:
return gtk.FALSE
return False
def OnEnter(self, wid, event=None):
self.grab_focus()
w = self.get_pointer()
self.UpdateRenderer(w[0], w[1])
return gtk.TRUE
return True
def OnLeave(self, wid, event):
return gtk.TRUE
return True
def OnKeyPress(self, wid, event=None):
if (event.keyval == GDK.r) or (event.keyval == GDK.R):
if (event.keyval == Gdk.KEY_r) or (event.keyval == Gdk.KEY_R):
self.Reset()
return gtk.TRUE
elif (event.keyval == GDK.w) or (event.keyval == GDK.W):
return True
elif (event.keyval == Gdk.KEY_w) or (event.keyval == Gdk.KEY_W):
self.Wireframe()
return gtk.TRUE
elif (event.keyval == GDK.s) or (event.keyval == GDK.S):
return True
elif (event.keyval == Gdk.KEY_s) or (event.keyval == Gdk.KEY_S):
self.Surface()
return gtk.TRUE
elif (event.keyval == GDK.p) or (event.keyval == GDK.P):
return True
elif (event.keyval == Gdk.KEY_p) or (event.keyval == Gdk.KEY_P):
m = self.get_pointer()
self.PickActor(m[0], m[1])
return gtk.TRUE
self.PickActor(m.x, m.y)
return True
else:
return gtk.FALSE
return False
def GetZoomFactor(self):
return self._CurrentZoom
......@@ -284,8 +282,8 @@ class GtkVTKRenderWindow(GtkVTKRenderWindowBase):
UpdateRenderer will identify the renderer under the mouse and set
up _CurrentRenderer, _CurrentCamera, and _CurrentLight.
"""
windowX = self.get_window().width
windowY = self.get_window().height
windowX = self.get_window().get_width()
windowY = self.get_window().get_height()
renderers = self._RenderWindow.GetRenderers()
numRenderers = renderers.GetNumberOfItems()
......@@ -324,12 +322,12 @@ class GtkVTKRenderWindow(GtkVTKRenderWindowBase):
x = event.x
y = event.y
self.UpdateRenderer(x,y)
return gtk.TRUE
return True
def EndMotion(self, wid, event=None):
if self._CurrentRenderer:
self.Render()
return gtk.TRUE
return True
def Rotate(self,x,y):
if self._CurrentRenderer:
......@@ -486,22 +484,22 @@ def main():
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
# The main window
window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL)
window.set_title("A GtkVTKRenderWindow Demo!")
window.connect("destroy", gtk.mainquit)
window.connect("delete_event", gtk.mainquit)
window.connect("destroy", Gtk.main_quit)
window.connect("delete_event", Gtk.main_quit)
window.set_border_width(10)
# A VBox into which widgets are packed.
vbox = gtk.GtkVBox(spacing=3)
# A Box into which widgets are packed.
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=3)
window.add(vbox)
vbox.show()
# The GtkVTKRenderWindow
gvtk = GtkVTKRenderWindow()
#gvtk.SetDesiredUpdateRate(1000)
gvtk.set_usize(400, 400)
vbox.pack_start(gvtk)
gvtk.set_size_request(400, 400)
vbox.pack_start(gvtk, False, False, 0)
gvtk.show()
# The VTK stuff.
......@@ -518,14 +516,14 @@ def main():
ren.AddActor(coneActor)
# A simple quit button
quit = gtk.GtkButton("Quit!")
quit.connect("clicked", gtk.mainquit)
vbox.pack_start(quit)
quit = Gtk.Button(label="Quit!")
quit.connect("clicked", Gtk.main_quit)
vbox.pack_start(quit, False, False, 0)
quit.show()
# show the main window and start event processing.
window.show()
gtk.mainloop()
Gtk.main()
if __name__ == "__main__":
......
"""
Description:
Provides a pyGtk vtkRenderWindowInteractor widget. This embeds a
vtkRenderWindow inside a GTK widget and uses the
Provides a vtkRenderWindowInteractor widget for GTK 3 with pygobject3.
This embeds a vtkRenderWindow inside a GTK widget and uses the
vtkGenericRenderWindowInteractor for the event handling. This is
based on vtkTkRenderWindow.py.
The class uses the gtkgl.GtkGLArea widget (gtkglarea). This avoids
The class uses the Gtk.GLArea widget. This avoids
a lot of problems with flicker.
There is a working example at the bottom.
......@@ -26,30 +26,37 @@ Bugs:
"""
import gtk, GDK, gtkgl
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from vtk.all import vtkGenericOpenGLRenderWindow
from vtk import all as vtk
from vtkmodules.vtkRenderingUI import vtkGenericRenderWindowInteractor
import math
class GtkVTKRenderWindowInteractor(gtkgl.GtkGLArea):
class GtkVTKRenderWindowInteractor(Gtk.GLArea):
""" Embeds a vtkRenderWindow into a pyGTK widget and uses
""" Embeds a vtkRenderWindow into a Gtk widget and uses
vtkGenericRenderWindowInteractor for the event handling. This
class embeds the RenderWindow correctly. A __getattr__ hook is
provided that makes the class behave like a
vtkGenericRenderWindowInteractor."""
def __init__(self, *args):
l = list(args)
attr = (gtkgl.RGBA, gtkgl.DOUBLEBUFFER)
l.insert(0, self)
l.insert(1, attr)
apply(gtkgl.GtkGLArea.__init__, l)
self._RenderWindow = vtkRenderWindow()
# attr = (gtkgl.RGBA, gtkgl.DOUBLEBUFFER)
# double buffered by default
Gtk.GLArea.__init__(self, *args)
self._RenderWindow = vtkGenericOpenGLRenderWindow()
# private attributes
self.__Created = 0
self._ActiveButton = 0
self._pointerX = 0
self._pointerY = 0
self._motionController = Gtk.EventControllerMotion(widget=self)
self._Iren = vtkGenericRenderWindowInteractor()
self._Iren.SetRenderWindow(self._RenderWindow)
......@@ -59,19 +66,21 @@ class GtkVTKRenderWindowInteractor(gtkgl.GtkGLArea):
self.ConnectSignals()
# need this to be able to handle key_press events.
self.set_flags(gtk.CAN_FOCUS)
self.set_can_focus(True)
# default size
self.set_usize(300, 300)
def set_usize(self, w, h):
gtkgl.GtkGLArea.set_usize(self, w, h)
self.set_size_request(w, h)
self._RenderWindow.SetSize(w, h)
self._Iren.SetSize(w, h)
self._Iren.ConfigureEvent()
def ConnectSignals(self):
self._motionController.connect('motion', self._updatePointerPosition)
self.connect("realize", self.OnRealize)
self.connect("expose_event", self.OnExpose)
self.connect("render", self.OnRender)
self.connect("configure_event", self.OnConfigure)
self.connect("button_press_event", self.OnButtonDown)
self.connect("button_release_event", self.OnButtonUp)
......@@ -80,12 +89,16 @@ class GtkVTKRenderWindowInteractor(gtkgl.GtkGLArea):
self.connect("leave_notify_event", self.OnLeave)
self.connect("key_press_event", self.OnKeyPress)
self.connect("delete_event", self.OnDestroy)
self.add_events(GDK.EXPOSURE_MASK| GDK.BUTTON_PRESS_MASK |
GDK.BUTTON_RELEASE_MASK |
GDK.KEY_PRESS_MASK |
GDK.POINTER_MOTION_MASK |
GDK.POINTER_MOTION_HINT_MASK |
GDK.ENTER_NOTIFY_MASK | GDK.LEAVE_NOTIFY_MASK)
self.add_events(
Gdk.EventMask.EXPOSURE_MASK |
Gdk.EventMask.BUTTON_PRESS_MASK |
Gdk.EventMask.BUTTON_RELEASE_MASK |
Gdk.EventMask.KEY_PRESS_MASK |
Gdk.EventMask.POINTER_MOTION_MASK |
Gdk.EventMask.POINTER_MOTION_HINT_MASK |
Gdk.EventMask.ENTER_NOTIFY_MASK |
Gdk.EventMask.LEAVE_NOTIFY_MASK
)
def __getattr__(self, attr):
"""Makes the object behave like a
......@@ -99,7 +112,7 @@ class GtkVTKRenderWindowInteractor(gtkgl.GtkGLArea):
" has no attribute named " + attr)
def CreateTimer(self, obj, event):
gtk.timeout_add(10, self._Iren.TimerEvent)
GLib.timeout_add(10, self._Iren.TimerEvent)
def DestroyTimer(self, obj, event):
"""The timer is a one shot timer so will expire automatically."""
......@@ -109,140 +122,132 @@ class GtkVTKRenderWindowInteractor(gtkgl.GtkGLArea):
return self._RenderWindow
def Render(self):
if self.__Created:
self._RenderWindow.Render()
# self._RenderWindow.Render()
# Rendering does not work so I tried clearing the buffer but that does nothing either.
self._RenderWindow.GetState().vtkglClearColor(1, 1, 1, 1)
def OnRealize(self, *args):
if self.__Created == 0:
# you can't get the xid without the window being realized.
self.realize()
win_id = str(self.get_window().xid)
self._RenderWindow.SetWindowInfo(win_id)
self._Iren.Initialize()
self.__Created = 1
return gtk.TRUE
self.make_current()
if self.get_error() is not None:
raise RuntimeError('Error in making current')
self._Iren.Initialize()
return True
def OnConfigure(self, wid, event=None):
sz = self._RenderWindow.GetSize()
if (event.width != sz[0]) or (event.height != sz[1]):
self._Iren.SetSize(event.width, event.height)
self._Iren.ConfigureEvent()
return gtk.TRUE
return True
def OnExpose(self, *args):
def OnRender(self, *args):
self.Render()
return gtk.TRUE
return True
def OnDestroy(self, event=None):
self.hide()
del self._RenderWindow
self.destroy()
return gtk.TRUE
return True
def _GetCtrlShift(self, event):
ctrl, shift = 0, 0
if ((event.state & GDK.CONTROL_MASK) == GDK.CONTROL_MASK):
if ((event.state & Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK):
ctrl = 1
if ((event.state & GDK.SHIFT_MASK) == GDK.SHIFT_MASK):
if ((event.state & Gdk.ModifierType.SHIFT_MASK) == Gdk.ModifierType.SHIFT_MASK):
shift = 1
return ctrl, shift
def _updatePointerPosition(self, controller, x, y):
self._pointerX = x
self._pointerY = y
def OnButtonDown(self, wid, event):
"""Mouse button pressed."""
m = self.get_pointer()
ctrl, shift = self._GetCtrlShift(event)
self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
self._Iren.SetEventInformationFlipY(self._pointerX, self._pointerY, ctrl, shift,
chr(0), 0, None)
button = event.button
if button == 3:
self._Iren.RightButtonPressEvent()
return gtk.TRUE
return True
elif button == 1:
self._Iren.LeftButtonPressEvent()
return gtk.TRUE
return True
elif button == 2:
self._Iren.MiddleButtonPressEvent()
return gtk.TRUE
return True
else:
return gtk.FALSE
return False
def OnButtonUp(self, wid, event):
"""Mouse button released."""
m = self.get_pointer()
ctrl, shift = self._GetCtrlShift(event)
self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
self._Iren.SetEventInformationFlipY(self._pointerX, self._pointerY, ctrl, shift,
chr(0), 0, None)
button = event.button
if button == 3:
self._Iren.RightButtonReleaseEvent()
return gtk.TRUE
return True
elif button == 1:
self._Iren.LeftButtonReleaseEvent()
return gtk.TRUE
return True
elif button == 2:
self._Iren.MiddleButtonReleaseEvent()
return gtk.TRUE
return True
return gtk.FALSE
return False
def OnMouseMove(self, wid, event):
"""Mouse has moved."""
m = self.get_pointer()
ctrl, shift = self._GetCtrlShift(event)
self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
self._Iren.SetEventInformationFlipY(self._pointerX, self._pointerY, ctrl, shift,
chr(0), 0, None)
self._Iren.MouseMoveEvent()
return gtk.TRUE
return True
def OnEnter(self, wid, event):
"""Entering the vtkRenderWindow."""
self.grab_focus()
m = self.get_pointer()
ctrl, shift = self._GetCtrlShift(event)
self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
self._Iren.SetEventInformationFlipY(self._pointerX, self._pointerY, ctrl, shift,
chr(0), 0, None)
self._Iren.EnterEvent()
return gtk.TRUE
return True
def OnLeave(self, wid, event):
"""Leaving the vtkRenderWindow."""
m = self.get_pointer()
ctrl, shift = self._GetCtrlShift(event)
self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
self._Iren.SetEventInformationFlipY(self._pointerX, self._pointerY, ctrl, shift,