[vtkusers] Segfault in vtkOpenGLRenderWindow.OpenGLInitState and advice about render window in/out events

Elvis Stansvik elvis.stansvik at orexplore.com
Sun Jun 26 07:11:39 EDT 2016


2016-06-26 13:06 GMT+02:00 Elvis Stansvik <elvis.stansvik at orexplore.com>:

> Hi,
>
> In my quest to create a PyQt5 widget based on QOpenGLWidget for
> showing/interacting with VTK, I now have something that seems to work
> (including the whole thing as a runnable example below).
>
> I do however have two problems (indicated by "FIXME:"s in the code below):
>
> 1. When I try to call vtkOpenGLRenderWindow.OpenGLInitState when handling
> the render window StartEvent (commented out below), similar to how
> QVTKWidget2 does, I get a segmentation fault:
>
> (gdb) bt
> #0  0x0000000000000000 in ?? ()
> #1  0x00007fffd8ad13b9 in vtkOpenGLRenderWindow::OpenGLInitState() () from
> /usr/lib/libvtkRenderingOpenGL2.so.1
> #2  0x00007fffd8de8479 in ?? () from
> /usr/lib/libvtkRenderingOpenGL2Python35D.so.1
> #3  0x00007ffff79ba5e9 in PyCFunction_Call () from
> /usr/lib/libpython3.5m.so.1.0
> #4  0x00007ffff7a325f1 in PyEval_EvalFrameEx () from
> /usr/lib/libpython3.5m.so.1.0
> #5  0x00007ffff7a339e2 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #6  0x00007ffff7a33ac3 in PyEval_EvalCodeEx () from
> /usr/lib/libpython3.5m.so.1.0
> #7  0x00007ffff799db98 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #8  0x00007ffff79730da in PyObject_Call () from
> /usr/lib/libpython3.5m.so.1.0
> #9  0x00007ffff7989d64 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #10 0x00007ffff79730da in PyObject_Call () from
> /usr/lib/libpython3.5m.so.1.0
> #11 0x00007ffff7a2a457 in PyEval_CallObjectWithKeywords () from
> /usr/lib/libpython3.5m.so.1.0
> #12 0x00007fffe8d905a0 in vtkPythonCommand::Execute(vtkObject*, unsigned
> long, void*) () from /usr/lib/libvtkWrappingPython35Core.so.1
> #13 0x00007fffe89ac196 in ?? () from /usr/lib/libvtkCommonCore.so.1
> #14 0x00007fffdf022a0f in vtkRenderWindow::Render() () from
> /usr/lib/libvtkRenderingCore.so.1
> #15 0x00007fffd8ad4461 in vtkOpenGLRenderWindow::Render() () from
> /usr/lib/libvtkRenderingOpenGL2.so.1
> #16 0x00007fffdf027498 in vtkRenderWindowInteractor::Start() () from
> /usr/lib/libvtkRenderingCore.so.1
> #17 0x00007fffdf3ee789 in ?? () from
> /usr/lib/libvtkRenderingCorePython35D.so.1
> #18 0x00007ffff79ba5e9 in PyCFunction_Call () from
> /usr/lib/libpython3.5m.so.1.0
> #19 0x00007ffff7a325f1 in PyEval_EvalFrameEx () from
> /usr/lib/libpython3.5m.so.1.0
> #20 0x00007ffff7a339e2 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #21 0x00007ffff7a33ac3 in PyEval_EvalCodeEx () from
> /usr/lib/libpython3.5m.so.1.0
> #22 0x00007ffff799db98 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #23 0x00007ffff79730da in PyObject_Call () from
> /usr/lib/libpython3.5m.so.1.0
> #24 0x00007ffff7989d64 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #25 0x00007ffff79730da in PyObject_Call () from
> /usr/lib/libpython3.5m.so.1.0
> #26 0x00007ffff79cfa10 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #27 0x00007ffff79cdde6 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #28 0x00007ffff79730da in PyObject_Call () from
> /usr/lib/libpython3.5m.so.1.0
> #29 0x00007ffff7a2c4bc in PyEval_EvalFrameEx () from
> /usr/lib/libpython3.5m.so.1.0
> #30 0x00007ffff7a32942 in PyEval_EvalFrameEx () from
> /usr/lib/libpython3.5m.so.1.0
> #31 0x00007ffff7a339e2 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #32 0x00007ffff7a33ac3 in PyEval_EvalCodeEx () from
> /usr/lib/libpython3.5m.so.1.0
> #33 0x00007ffff7a33aeb in PyEval_EvalCode () from
> /usr/lib/libpython3.5m.so.1.0
> #34 0x00007ffff7a2802d in ?? () from /usr/lib/libpython3.5m.so.1.0
> #35 0x00007ffff79ba5e9 in PyCFunction_Call () from
> /usr/lib/libpython3.5m.so.1.0
> #36 0x00007ffff7a325f1 in PyEval_EvalFrameEx () from
> /usr/lib/libpython3.5m.so.1.0
> #37 0x00007ffff7a339e2 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #38 0x00007ffff7a306e0 in PyEval_EvalFrameEx () from
> /usr/lib/libpython3.5m.so.1.0
> #39 0x00007ffff7a339e2 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #40 0x00007ffff7a33ac3 in PyEval_EvalCodeEx () from
> /usr/lib/libpython3.5m.so.1.0
> #41 0x00007ffff799db98 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #42 0x00007ffff79730da in PyObject_Call () from
> /usr/lib/libpython3.5m.so.1.0
> #43 0x00007ffff7a6af01 in ?? () from /usr/lib/libpython3.5m.so.1.0
> #44 0x00007ffff7a6b7dc in Py_Main () from /usr/lib/libpython3.5m.so.1.0
> #45 0x0000000000400ae7 in main ()
> (gdb)
>
> Sorry for the lack of debugging symbols, but before I recompile VTK with
> debugging symbols, I thought I'd ask if anyone has an idea what may be the
> reason for the crash? I haven't seen any adverse effects from not calling
> this function, but I suspect something is broken? (how can I test?)
>
> 2. I can't seem to find a way to properly implement the
> WindowIsCurrentEvent, WindowIsDirectEvent and WindowSupportsOpenGLEvent
> render window events. E.g. (from below):
>
>     def _onWindowIsCurrentEvent(self, sender, event, callData):
>         callData = True  # FIXME: This won't work...
>
>     def _onWindowIsDirectEvent(self, sender, event, callData):
>         callData = True  # FIXME: This won't work...
>
>     def _onWindowSupportsOpenGLEvent(self, sender, event, callData):
>         callData = True  # FIXME: This won't work...
>
> The callData is a bool* and the handler is expected to write a bool to the
> pointed memory. I can't see that there's any way to do this from Python (is
> it possible with ctypes somehow?).
>
> So far I haven't seen any adverse effects from not implementing these
> three events correctly, but expect something to be broken..?
>

In fact, so far I haven't seen any of these three events being emitted by
the render window at all. When are they emitted? How can I provoke
emissions of them to test how broken my widget is from not handling them
correctly?

Elvis


>
> Thankful for any advice on these two issues, and it would be great if
> someone could try out the example, with the
>
>     # self.renderWindow.OpenGLInitState()  # FIXME: Segfaults :(
>
> line uncommented, to confirm the segfault I'm seeing.
>
> Cheers,
> Elvis
>
>
> from PyQt5.QtCore import Qt, QTimer, QEvent, QSize
> from PyQt5.QtGui import QSurfaceFormat
> from PyQt5.QtWidgets import QWidget, QOpenGLWidget
>
> from vtk import vtkGenericOpenGLRenderWindow
> from vtk import vtkGenericRenderWindowInteractor
> from vtk import vtkCommand
> from vtk import vtkRenderer
>
> from vtk import VTK_CURSOR_DEFAULT, VTK_CURSOR_ARROW, VTK_CURSOR_SIZENE
> from vtk import VTK_CURSOR_SIZENW, VTK_CURSOR_SIZESW, VTK_CURSOR_SIZESE
> from vtk import VTK_CURSOR_SIZENS, VTK_CURSOR_SIZEWE, VTK_CURSOR_SIZEALL
> from vtk import VTK_CURSOR_HAND, VTK_CURSOR_CROSSHAIR
>
>
> class VTKWidget(QOpenGLWidget):
>
>     def __init__(self, surfaceFormat=QSurfaceFormat.defaultFormat(),
> *args, **kwargs):
>         super().__init__(*args, **kwargs)
>
>         # Request OpenGL 3.2 compatibility profile and enable
> multi-sampling
>         surfaceFormat.setMajorVersion(3)
>         surfaceFormat.setMinorVersion(2)
>         surfaceFormat.setProfile(QSurfaceFormat.CompatibilityProfile)
>         surfaceFormat.setSamples(8)
>         self.setFormat(surfaceFormat)
>
>         # Create render window
>         self.renderWindow = vtkGenericOpenGLRenderWindow()
>         self.renderWindow.SetSize(self.width(), self.height())
>         self.renderWindow.SetPosition(self.x(), self.y())
>
>         self.renderWindow.AddObserver(vtkCommand.StartEvent,
>                                       self._onStartEvent)
>         self.renderWindow.AddObserver(vtkCommand.EndEvent,
>                                       self._onEndEvent)
>         self.renderWindow.AddObserver(vtkCommand.CursorChangedEvent,
>                                       self._onCursorChangedEvent)
>         self.renderWindow.AddObserver(vtkCommand.WindowMakeCurrentEvent,
>                                       self._onWindowMakeCurrentEvent)
>         self.renderWindow.AddObserver(vtkCommand.WindowIsCurrentEvent,
>                                       self._onWindowIsCurrentEvent)
>         self.renderWindow.AddObserver(vtkCommand.WindowFrameEvent,
>                                       self._onWindowFrameEvent)
>         self.renderWindow.AddObserver(vtkCommand.WindowIsDirectEvent,
>                                       self._onWindowIsDirectEvent)
>         self.renderWindow.AddObserver(vtkCommand.WindowSupportsOpenGLEvent,
>                                       self._onWindowSupportsOpenGLEvent)
>
>         # Create interactor
>         self.interactor = vtkGenericRenderWindowInteractor()
>         self.interactor.SetRenderWindow(self.renderWindow)
>         self.interactor.SetSize(self.width(), self.height())
>         self.interactor.AddObserver(vtkCommand.CreateTimerEvent,
>                                     lambda *_: self._timer.start(10))
>         self.interactor.AddObserver(vtkCommand.DestroyTimerEvent,
>                                     lambda *_: self._timer.stop())
>         self.interactor.Start()
>
>         # Create renderer
>         self.renderer = vtkRenderer()
>         self.renderWindow.AddRenderer(self.renderer)
>
>         # Timer for the interactor
>         self._timer = QTimer(self)
>         self._timer.timeout.connect(self.interactor.TimerEvent)
>
>         # Some tracked input state
>         self._pressedButton = Qt.NoButton
>         self._lastMouseX = 0
>         self._lastMouseY = 0
>         self._lastModifiers = Qt.NoModifier
>         self._lastButtons = Qt.NoButton
>         self._wheelDelta = 0
>
>         # Finalize render window before we're destroyed
>         self._hidden = QWidget(self)
>         self._hidden.hide()
>         self._hidden.destroyed.connect(self.renderWindow.Finalize)
>
>     #
>     # Handle VTK render window events
>     #
>
>     def _onStartEvent(self, *_):
>         self.makeCurrent()
>         self.renderWindow.PushState()
>         # self.renderWindow.OpenGLInitState()  # FIXME: Segfaults :(
>
>     def _onEndEvent(self, *_):
>         self.renderWindow.PopState()
>
>     def _onWindowMakeCurrentEvent(self, *_):
>         pass  # Handled automatically by QOpenGLWidget
>
>     def _onWindowIsCurrentEvent(self, sender, event, callData):
>         callData = True  # FIXME: This won't work...
>
>     def _onWindowIsDirectEvent(self, sender, event, callData):
>         callData = True  # FIXME: This won't work...
>
>     def _onWindowSupportsOpenGLEvent(self, sender, event, callData):
>         callData = True  # FIXME: This won't work...
>
>     def _onWindowFrameEvent(self, *_):
>         if self.renderWindow.GetSwapBuffers():
>             self.update()
>
>     def _onCursorChangedEvent(self):
>
>         def showCursor():
>             vtkCursor = self.renderWindow.GetCurrentCursor()
>             qtCursor = VTKWidget._cursors.get(vtkCursor, Qt.ArrowCursor)
>             self.setCursor(qtCursor)
>
>         # Defer until cursor has actually changed
>         QTimer.singleShot(0, showCursor)
>
>     #
>     # Implement QOpenGLWidget functions
>     #
>
>     def initializeGL(self):
>         self.renderWindow.OpenGLInitContext()
>
>     def paintGL(self):
>         self.interactor.Render()
>
>     def resizeGL(self, width, height):
>         self.renderWindow.SetSize(width, height)
>         self.interactor.SetSize(width, height)
>         self.interactor.ConfigureEvent()
>
>     #
>     # Handle QWidget events
>     #
>
>     def closeEvent(self, _):
>         self.renderWindow.Finalize()
>
>     def sizeHint(self):
>         return QSize(400, 400)
>
>     def moveEvent(self, event):
>         super().moveEvent(event)
>         self.renderWindow.SetPosition(self.x(), self.y())
>
>     def enterEvent(self, event):
>         ctrl, shift = self._modifiers(event)
>         self.interactor.SetEventInformationFlipY(self._lastMouseX,
> self._lastMouseY,
>                                                  ctrl, shift, chr(0), 0,
> None)
>         self.interactor.EnterEvent()
>
>     def leaveEvent(self, event):
>         ctrl, shift = self._modifiers(event)
>         self.interactor.SetEventInformationFlipY(self._lastMouseX,
> self._lastMouseY,
>                                                  ctrl, shift, chr(0), 0,
> None)
>         self.interactor.LeaveEvent()
>
>     def mousePressEvent(self, event):
>         ctrl, shift = self._modifiers(event)
>         repeat = 1 if event.type() == QEvent.MouseButtonDblClick else 0
>         self.interactor.SetEventInformationFlipY(event.x(), event.y(),
> ctrl, shift,
>                                                  chr(0), repeat, None)
>
>         self._pressedButton = event.button()
>
>         if self._pressedButton == Qt.LeftButton:
>             self.interactor.LeftButtonPressEvent()
>         elif self._pressedButton == Qt.RightButton:
>             self.interactor.RightButtonPressEvent()
>         elif self._pressedButton == Qt.MidButton:
>             self.interactor.MiddleButtonPressEvent()
>
>     def mouseReleaseEvent(self, event):
>         ctrl, shift = self._modifiers(event)
>         self.interactor.SetEventInformationFlipY(event.x(), event.y(),
> ctrl, shift,
>                                                  chr(0), 0, None)
>
>         if self._pressedButton == Qt.LeftButton:
>             self.interactor.LeftButtonReleaseEvent()
>         elif self._pressedButton == Qt.RightButton:
>             self.interactor.RightButtonReleaseEvent()
>         elif self._pressedButton == Qt.MidButton:
>             self.interactor.MiddleButtonReleaseEvent()
>
>         self._pressedButton = Qt.NoButton
>
>     def mouseMoveEvent(self, event):
>         self._lastModifiers = event.modifiers()
>         self._lastButtons = event.buttons()
>         self._lastMouseX = event.x()
>         self._lastMouseY = event.y()
>
>         ctrl, shift = self._modifiers(event)
>         self.interactor.SetEventInformationFlipY(event.x(), event.y(),
> ctrl, shift,
>                                                  chr(0), 0, None)
>         self.interactor.MouseMoveEvent()
>
>     def keyPressEvent(self, event):
>         ctrl, shift = self._modifiers(event)
>         key = str(event.text()) if event.key() < 256 else chr(0)
>         keySym = VTKWidget._qtKeyToKeySymbol(event.key())
>
>         if shift and len(keySym) == 1 and keySym.isalpha():
>             keySym = keySym.upper()
>
>         self.interactor.SetEventInformationFlipY(self._lastMouseX,
> self._lastMouseY,
>                                                  ctrl, shift, key, 0,
> keySym)
>         self.interactor.KeyPressEvent()
>         self.interactor.CharEvent()
>
>     def keyReleaseEvent(self, event):
>         ctrl, shift = self._modifiers(event)
>         key = chr(event.key()) if event.key() < 256 else chr(0)
>
>         self.interactor.SetEventInformationFlipY(self._lastMouseX,
> self._lastMouseY,
>                                                  ctrl, shift, key, 0, None)
>         self.interactor.KeyReleaseEvent()
>
>     def wheelEvent(self, event):
>         self._wheelDelta += event.angleDelta().y()
>
>         if self._wheelDelta >= 120:
>             self.interactor.MouseWheelForwardEvent()
>             self._wheelDelta = 0
>         elif self._wheelDelta <= -120:
>             self.interactor.MouseWheelBackwardEvent()
>             self._wheelDelta = 0
>
>     def _modifiers(self, event):
>         ctrl = shift = False
>
>         if hasattr(event, 'modifiers'):
>             if event.modifiers() & Qt.ShiftModifier:
>                 shift = True
>             if event.modifiers() & Qt.ControlModifier:
>                 ctrl = True
>         else:
>             if self._lastModifiers & Qt.ShiftModifier:
>                 shift = True
>             if self._lastModifiers & Qt.ControlModifier:
>                 ctrl = True
>
>         return ctrl, shift
>
>     @classmethod
>     def _qtKeyToKeySymbol(cls, key):
>         if key not in VTKWidget._keySymbols:
>             return None
>         return VTKWidget._keySymbols[key]
>
>     # Maps VTK cursors to Qt cursors
>     _cursors = {
>         VTK_CURSOR_DEFAULT: Qt.ArrowCursor,
>         VTK_CURSOR_ARROW: Qt.ArrowCursor,
>         VTK_CURSOR_SIZENE: Qt.SizeBDiagCursor,
>         VTK_CURSOR_SIZENW: Qt.SizeFDiagCursor,
>         VTK_CURSOR_SIZESW: Qt.SizeBDiagCursor,
>         VTK_CURSOR_SIZESE: Qt.SizeFDiagCursor,
>         VTK_CURSOR_SIZENS: Qt.SizeVerCursor,
>         VTK_CURSOR_SIZEWE: Qt.SizeHorCursor,
>         VTK_CURSOR_SIZEALL: Qt.SizeAllCursor,
>         VTK_CURSOR_HAND: Qt.PointingHandCursor,
>         VTK_CURSOR_CROSSHAIR: Qt.CrossCursor
>     }
>
>     # Maps Qt keys to VTK key symbols
>     _keySymbols = {
>         Qt.Key_Backspace: 'BackSpace',
>         Qt.Key_Tab: 'Tab',
>         Qt.Key_Backtab: 'Tab',
>         # Qt.Key_Clear : 'Clear',
>         Qt.Key_Return: 'Return',
>         Qt.Key_Enter: 'Return',
>         Qt.Key_Shift: 'Shift_L',
>         Qt.Key_Control: 'Control_L',
>         Qt.Key_Alt: 'Alt_L',
>         Qt.Key_Pause: 'Pause',
>         Qt.Key_CapsLock: 'Caps_Lock',
>         Qt.Key_Escape: 'Escape',
>         Qt.Key_Space: 'space',
>         # Qt.Key_Prior : 'Prior',
>         # Qt.Key_Next : 'Next',
>         Qt.Key_End: 'End',
>         Qt.Key_Home: 'Home',
>         Qt.Key_Left: 'Left',
>         Qt.Key_Up: 'Up',
>         Qt.Key_Right: 'Right',
>         Qt.Key_Down: 'Down',
>         Qt.Key_SysReq: 'Snapshot',
>         Qt.Key_Insert: 'Insert',
>         Qt.Key_Delete: 'Delete',
>         Qt.Key_Help: 'Help',
>         Qt.Key_0: '0',
>         Qt.Key_1: '1',
>         Qt.Key_2: '2',
>         Qt.Key_3: '3',
>         Qt.Key_4: '4',
>         Qt.Key_5: '5',
>         Qt.Key_6: '6',
>         Qt.Key_7: '7',
>         Qt.Key_8: '8',
>         Qt.Key_9: '9',
>         Qt.Key_A: 'a',
>         Qt.Key_B: 'b',
>         Qt.Key_C: 'c',
>         Qt.Key_D: 'd',
>         Qt.Key_E: 'e',
>         Qt.Key_F: 'f',
>         Qt.Key_G: 'g',
>         Qt.Key_H: 'h',
>         Qt.Key_I: 'i',
>         Qt.Key_J: 'j',
>         Qt.Key_K: 'k',
>         Qt.Key_L: 'l',
>         Qt.Key_M: 'm',
>         Qt.Key_N: 'n',
>         Qt.Key_O: 'o',
>         Qt.Key_P: 'p',
>         Qt.Key_Q: 'q',
>         Qt.Key_R: 'r',
>         Qt.Key_S: 's',
>         Qt.Key_T: 't',
>         Qt.Key_U: 'u',
>         Qt.Key_V: 'v',
>         Qt.Key_W: 'w',
>         Qt.Key_X: 'x',
>         Qt.Key_Y: 'y',
>         Qt.Key_Z: 'z',
>         Qt.Key_Asterisk: 'asterisk',
>         Qt.Key_Plus: 'plus',
>         Qt.Key_Minus: 'minus',
>         Qt.Key_Period: 'period',
>         Qt.Key_Slash: 'slash',
>         Qt.Key_F1: 'F1',
>         Qt.Key_F2: 'F2',
>         Qt.Key_F3: 'F3',
>         Qt.Key_F4: 'F4',
>         Qt.Key_F5: 'F5',
>         Qt.Key_F6: 'F6',
>         Qt.Key_F7: 'F7',
>         Qt.Key_F8: 'F8',
>         Qt.Key_F9: 'F9',
>         Qt.Key_F10: 'F10',
>         Qt.Key_F11: 'F11',
>         Qt.Key_F12: 'F12',
>         Qt.Key_F13: 'F13',
>         Qt.Key_F14: 'F14',
>         Qt.Key_F15: 'F15',
>         Qt.Key_F16: 'F16',
>         Qt.Key_F17: 'F17',
>         Qt.Key_F18: 'F18',
>         Qt.Key_F19: 'F19',
>         Qt.Key_F20: 'F20',
>         Qt.Key_F21: 'F21',
>         Qt.Key_F22: 'F22',
>         Qt.Key_F23: 'F23',
>         Qt.Key_F24: 'F24',
>         Qt.Key_NumLock: 'Num_Lock',
>         Qt.Key_ScrollLock: 'Scroll_Lock'
>     }
>
> def example():
>     #
>     # Try out VTKWidget by showing a couple of cones
>     #
>     from sys import argv, exit
>     from PyQt5.QtWidgets import QApplication, QVBoxLayout
>     from vtk import vtkConeSource, vtkPolyDataMapper, vtkActor
>
>     app = QApplication(argv)
>
>     layout = QVBoxLayout()
>
>     for i in range(2):
>         widget = VTKWidget()
>
>         coneSource = vtkConeSource()
>         coneSource.SetResolution(8)
>
>         coneMapper = vtkPolyDataMapper()
>         coneMapper.SetInputConnection(coneSource.GetOutputPort())
>
>         coneActor = vtkActor()
>         coneActor.SetMapper(coneMapper)
>
>         widget.renderer.AddActor(coneActor)
>
>         layout.addWidget(widget)
>
>     container = QWidget()
>     container.setLayout(layout)
>     container.show()
>
>     exit(app.exec_())
>
>
> if __name__ == '__main__':
>     example()
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://public.kitware.com/pipermail/vtkusers/attachments/20160626/77201e4d/attachment.html>


More information about the vtkusers mailing list