License New BSD license
Lines 291
Keywords
PyQt (6) Qt (4) VTK (6)
Included in this Library
Permissions
Owner: Stou S.
Viewable by Everyone
Editable by All Siafoo Users
Hide
Writing an article is easy - try our reStructured Text demo Join Siafoo Now or Learn More

QVTKRenderWindowInteractor Atom Feed 0

In Brief The version of QVTKRenderWindowInteractor that ships with VTK 5.2 causes crashes when embedded in a window. The solution seems to be to subclass the OpenGL widget instead of the plain Widget class (at least that's what the wxPython version does). I've tested this on GNU/Linux and Windows XP... more
# 's
  1"""
2A simple VTK widget for PyQt v4, the Qt v4 bindings for Python.
3See http://www.trolltech.com for Qt documentation, and
4http://www.riverbankcomputing.co.uk for PyQt.
5
6This class is based on the vtkGenericRenderWindowInteractor and is
7therefore fairly powerful. It should also play nicely with the
8vtk3DWidget code.
9
10Created by Prabhu Ramachandran, May 2002
11Based on David Gobbi's QVTKRenderWidget.py
12
13Changes by Gerard Vermeulen Feb. 2003
14 Win32 support.
15
16Changes by Gerard Vermeulen, May 2003
17 Bug fixes and better integration with the Qt framework.
18
19Changes by Phil Thompson, Nov. 2006
20 Ported to PyQt v4.
21 Added support for wheel events.
22
23Changes by Phil Thompson, Oct. 2007
24 Bug fixes.
25
26Changes by Phil Thompson, Mar. 2008
27 Added cursor support.
28
29Changes by Stou Sandalski, July. 2009
30 Fixed cursor typo, subclassed QGLWidget not GLWidget to allow for embedding
31"""
32
33from PyQt4 import QtCore, QtGui, QtOpenGL
34import vtk
35
36
37class QVTKRenderWindowInteractor(QtOpenGL.QGLWidget):
38
39 """ A QVTKRenderWindowInteractor for Python and Qt. Uses a
40 vtkGenericRenderWindowInteractor to handle the interactions. Use
41 GetRenderWindow() to get the vtkRenderWindow. Create with the
42 keyword stereo=1 in order to generate a stereo-capable window.
43
44 The user interface is summarized in vtkInteractorStyle.h:
45
46 - Keypress j / Keypress t: toggle between joystick (position
47 sensitive) and trackball (motion sensitive) styles. In joystick
48 style, motion occurs continuously as long as a mouse button is
49 pressed. In trackball style, motion occurs when the mouse button
50 is pressed and the mouse pointer moves.
51
52 - Keypress c / Keypress o: toggle between camera and object
53 (actor) modes. In camera mode, mouse events affect the camera
54 position and focal point. In object mode, mouse events affect
55 the actor that is under the mouse pointer.
56
57 - Button 1: rotate the camera around its focal point (if camera
58 mode) or rotate the actor around its origin (if actor mode). The
59 rotation is in the direction defined from the center of the
60 renderer's viewport towards the mouse position. In joystick mode,
61 the magnitude of the rotation is determined by the distance the
62 mouse is from the center of the render window.
63
64 - Button 2: pan the camera (if camera mode) or translate the actor
65 (if object mode). In joystick mode, the direction of pan or
66 translation is from the center of the viewport towards the mouse
67 position. In trackball mode, the direction of motion is the
68 direction the mouse moves. (Note: with 2-button mice, pan is
69 defined as <Shift>-Button 1.)
70
71 - Button 3: zoom the camera (if camera mode) or scale the actor
72 (if object mode). Zoom in/increase scale if the mouse position is
73 in the top half of the viewport; zoom out/decrease scale if the
74 mouse position is in the bottom half. In joystick mode, the amount
75 of zoom is controlled by the distance of the mouse pointer from
76 the horizontal centerline of the window.
77
78 - Keypress 3: toggle the render window into and out of stereo
79 mode. By default, red-blue stereo pairs are created. Some systems
80 support Crystal Eyes LCD stereo glasses; you have to invoke
81 SetStereoTypeToCrystalEyes() on the rendering window. Note: to
82 use stereo you also need to pass a stereo=1 keyword argument to
83 the constructor.
84
85 - Keypress e: exit the application.
86
87 - Keypress f: fly to the picked point
88
89 - Keypress p: perform a pick operation. The render window interactor
90 has an internal instance of vtkCellPicker that it uses to pick.
91
92 - Keypress r: reset the camera view along the current view
93 direction. Centers the actors and moves the camera so that all actors
94 are visible.
95
96 - Keypress s: modify the representation of all actors so that they
97 are surfaces.
98
99 - Keypress u: invoke the user-defined function. Typically, this
100 keypress will bring up an interactor that you can type commands in.
101
102 - Keypress w: modify the representation of all actors so that they
103 are wireframe.
104 """
105
106 # Map between VTK and Qt cursors.
107 _CURSOR_MAP = {
108 0: QtCore.Qt.ArrowCursor, # VTK_CURSOR_DEFAULT
109 1: QtCore.Qt.ArrowCursor, # VTK_CURSOR_ARROW
110 2: QtCore.Qt.SizeBDiagCursor, # VTK_CURSOR_SIZENE
111 3: QtCore.Qt.SizeFDiagCursor, # VTK_CURSOR_SIZENWSE
112 4: QtCore.Qt.SizeBDiagCursor, # VTK_CURSOR_SIZESW
113 5: QtCore.Qt.SizeFDiagCursor, # VTK_CURSOR_SIZESE
114 6: QtCore.Qt.SizeVerCursor, # VTK_CURSOR_SIZENS
115 7: QtCore.Qt.SizeHorCursor, # VTK_CURSOR_SIZEWE
116 8: QtCore.Qt.SizeAllCursor, # VTK_CURSOR_SIZEALL
117 9: QtCore.Qt.PointingHandCursor, # VTK_CURSOR_HAND
118 10: QtCore.Qt.CrossCursor, # VTK_CURSOR_CROSSHAIR
119 }
120
121 def __init__(self, parent=None, **kw):
122 # the current button
123 self._ActiveButton = QtCore.Qt.NoButton
124
125 # private attributes
126 self.__oldFocus = None
127 self.__saveX = 0
128 self.__saveY = 0
129 self.__saveModifiers = QtCore.Qt.NoModifier
130 self.__saveButtons = QtCore.Qt.NoButton
131
132 # do special handling of some keywords:
133 # stereo, rw
134
135 stereo = 0
136
137 if kw.has_key('stereo'):
138 if kw['stereo']:
139 stereo = 1
140
141 rw = None
142
143 if kw.has_key('rw'):
144 rw = kw['rw']
145
146 # create qt-level widget
147 QtOpenGL.QGLWidget.__init__(self, parent)
148
149 if rw: # user-supplied render window
150 self._RenderWindow = rw
151 else:
152 self._RenderWindow = vtk.vtkRenderWindow()
153
154 self._RenderWindow.SetWindowInfo(str(int(self.winId())))
155
156 if stereo: # stereo mode
157 self._RenderWindow.StereoCapableWindowOn()
158 self._RenderWindow.SetStereoTypeToCrystalEyes()
159
160 self._Iren = vtk.vtkGenericRenderWindowInteractor()
161 self._Iren.SetRenderWindow(self._RenderWindow)
162
163 # do all the necessary qt setup
164 self.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
165 self.setAttribute(QtCore.Qt.WA_PaintOnScreen)
166 self.setMouseTracking(True) # get all mouse events
167 self.setFocusPolicy(QtCore.Qt.WheelFocus)
168 self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding))
169
170 self._Timer = QtCore.QTimer(self)
171 self.connect(self._Timer, QtCore.SIGNAL('timeout()'), self.TimerEvent)
172
173 self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
174 self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)
175 self._Iren.GetRenderWindow().AddObserver('CursorChangedEvent',
176 self.CursorChangedEvent)
177
178 def __getattr__(self, attr):
179 """Makes the object behave like a vtkGenericRenderWindowInteractor"""
180 if attr == '__vtk__':
181 return lambda t=self._Iren: t
182 elif hasattr(self._Iren, attr):
183 return getattr(self._Iren, attr)
184 else:
185 raise AttributeError, self.__class__.__name__ + \
186 " has no attribute named " + attr
187
188 def CreateTimer(self, obj, evt):
189 self._Timer.start(10)
190
191 def DestroyTimer(self, obj, evt):
192 self._Timer.stop()
193 return 1
194
195 def TimerEvent(self):
196 self._Iren.TimerEvent()
197
198 def CursorChangedEvent(self, obj, evt):
199 """Called when the CursorChangedEvent fires on the render window."""
200 # This indirection is needed since when the event fires, the current
201 # cursor is not yet set so we defer this by which time the current
202 # cursor should have been set.
203 QtCore.QTimer.singleShot(0, self.ShowCursor)
204
205 def HideCursor(self):
206 """Hides the cursor."""
207 self.setCursor(QtCore.Qt.BlankCursor)
208
209 def ShowCursor(self):
210 """Shows the cursor."""
211 vtk_cursor = self._Iren.GetRenderWindow().GetCurrentCursor()
212 qt_cursor = self._CURSOR_MAP.get(vtk_cursor, QtCore.Qt.ArrowCursor)
213 self.setCursor(qt_cursor)
214
215 def sizeHint(self):
216 return QtCore.QSize(400, 400)
217
218 def paintEngine(self):
219 return None
220
221 def paintEvent(self, ev):
222 self._RenderWindow.Render()
223
224 def resizeEvent(self, ev):
225 w = self.width()
226 h = self.height()
227
228 self._RenderWindow.SetSize(w, h)
229 self._Iren.SetSize(w, h)
230
231 def _GetCtrlShift(self, ev):
232 ctrl = shift = False
233
234 if hasattr(ev, 'modifiers'):
235 if ev.modifiers() & QtCore.Qt.ShiftModifier:
236 shift = True
237 if ev.modifiers() & QtCore.Qt.ControlModifier:
238 ctrl = True
239 else:
240 if self.__saveModifiers & QtCore.Qt.ShiftModifier:
241 shift = True
242 if self.__saveModifiers & QtCore.Qt.ControlModifier:
243 ctrl = True
244
245 return ctrl, shift
246
247 def enterEvent(self, ev):
248 if not self.hasFocus():
249 self.__oldFocus = self.focusWidget()
250 self.setFocus()
251
252 ctrl, shift = self._GetCtrlShift(ev)
253 self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
254 ctrl, shift, chr(0), 0, None)
255 self._Iren.EnterEvent()
256
257 def leaveEvent(self, ev):
258 if self.__saveButtons == QtCore.Qt.NoButton and self.__oldFocus:
259 self.__oldFocus.setFocus()
260 self.__oldFocus = None
261
262 ctrl, shift = self._GetCtrlShift(ev)
263 self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
264 ctrl, shift, chr(0), 0, None)
265 self._Iren.LeaveEvent()
266
267 def mousePressEvent(self, ev):
268 ctrl, shift = self._GetCtrlShift(ev)
269 repeat = 0
270 if ev.type() == QtCore.QEvent.MouseButtonDblClick:
271 repeat = 1
272 self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
273 ctrl, shift, chr(0), repeat, None)
274
275 self._ActiveButton = ev.button()
276
277 if self._ActiveButton == QtCore.Qt.LeftButton:
278 self._Iren.LeftButtonPressEvent()
279 elif self._ActiveButton == QtCore.Qt.RightButton:
280 self._Iren.RightButtonPressEvent()
281 elif self._ActiveButton == QtCore.Qt.MidButton:
282 self._Iren.MiddleButtonPressEvent()
283
284 def mouseReleaseEvent(self, ev):
285 ctrl, shift = self._GetCtrlShift(ev)
286 self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
287 ctrl, shift, chr(0), 0, None)
288
289 if self._ActiveButton == QtCore.Qt.LeftButton:
290 self._Iren.LeftButtonReleaseEvent()
291 elif self._ActiveButton == QtCore.Qt.RightButton:
292 self._Iren.RightButtonReleaseEvent()
293 elif self._ActiveButton == QtCore.Qt.MidButton:
294 self._Iren.MiddleButtonReleaseEvent()
295
296 def mouseMoveEvent(self, ev):
297 self.__saveModifiers = ev.modifiers()
298 self.__saveButtons = ev.buttons()
299 self.__saveX = ev.x()
300 self.__saveY = ev.y()
301
302 ctrl, shift = self._GetCtrlShift(ev)
303 self._Iren.SetEventInformationFlipY(ev.x(), ev.y(),
304 ctrl, shift, chr(0), 0, None)
305 self._Iren.MouseMoveEvent()
306
307 def keyPressEvent(self, ev):
308 ctrl, shift = self._GetCtrlShift(ev)
309 if ev.key() < 256:
310 key = str(ev.text())
311 else:
312 key = chr(0)
313
314 self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
315 ctrl, shift, key, 0, None)
316 self._Iren.KeyPressEvent()
317 self._Iren.CharEvent()
318
319 def keyReleaseEvent(self, ev):
320 ctrl, shift = self._GetCtrlShift(ev)
321 if ev.key() < 256:
322 key = chr(ev.key())
323 else:
324 key = chr(0)
325
326 self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY,
327 ctrl, shift, key, 0, None)
328 self._Iren.KeyReleaseEvent()
329
330 def wheelEvent(self, ev):
331 if ev.delta() >= 0:
332 self._Iren.MouseWheelForwardEvent()
333 else:
334 self._Iren.MouseWheelBackwardEvent()
335
336 def GetRenderWindow(self):
337 return self._RenderWindow
338
339 def Render(self):
340 self.update()
341
342
343def QVTKRenderWidgetConeExample():
344 """A simple example that uses the QVTKRenderWindowInteractor class."""
345
346 # every QT app needs an app
347 app = QtGui.QApplication(['QVTKRenderWindowInteractor'])
348
349 # create the widget
350 widget = QVTKRenderWindowInteractor()
351 widget.Initialize()
352 widget.Start()
353 # if you dont want the 'q' key to exit comment this.
354 widget.AddObserver("ExitEvent", lambda o, e, a=app: a.quit())
355
356 ren = vtk.vtkRenderer()
357 widget.GetRenderWindow().AddRenderer(ren)
358
359 cone = vtk.vtkConeSource()
360 cone.SetResolution(8)
361
362 coneMapper = vtk.vtkPolyDataMapper()
363 coneMapper.SetInput(cone.GetOutput())
364
365 coneActor = vtk.vtkActor()
366 coneActor.SetMapper(coneMapper)
367
368 ren.AddActor(coneActor)
369
370 # show the widget
371 widget.show()
372 # start event processing
373 app.exec_()
374
375if __name__ == "__main__":
376 QVTKRenderWidgetConeExample()

The version of QVTKRenderWindowInteractor that ships with VTK 5.2 causes crashes when embedded in a window. The solution seems to be to subclass the OpenGL widget instead of the plain Widget class (at least that's what the wxPython version does). I've tested this on GNU/Linux and Windows XP

Oh and the original version causes a bunch of X errors like this:

X Error: BadWindow (invalid Window parameter) 3
  Major opcode: 2 (X_ChangeWindowAttributes)
  Resource id:  0x3a0000d