qt5pyqt5python-3.6vtkqvtkwidget

VTKWidget in Qt is not updated as expected


I would like to display a 3D-Animation in my Qt5-Gui. Everything works as expected, but unfortunately the scene is not getting updated when I don't interact with the vtkWidget. In other words: When I want to see the animation, I need to click continously with the mouse on the widget. I'd be greatful for any help.

import sys
import vtk
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import Qt
import numpy as np

from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor

class mainWindow(Qt.QMainWindow):
def __init__(self, parent = None):
    Qt.QMainWindow.__init__(self, parent)

    self.frame = Qt.QFrame()
    self.vl = Qt.QVBoxLayout()
    self.button = QtWidgets.QPushButton("TestButton")
    self.label = QtWidgets.QLabel("This is a label")
    self.vtkWidget = QVTKRenderWindowInteractor(self.frame)

    #Create Source
    self.source = vtk.vtkCylinderSource()
    self.source.SetCenter(0, 0, 0)
    self.source.SetRadius(5.0)
    #Create Mapper
    self.mapper = vtk.vtkPolyDataMapper()
    self.mapper.SetInputConnection(self.source.GetOutputPort())
    #Create Actor
    self.actor = vtk.vtkActor()
    self.actor.SetMapper(self.mapper)

    #Create poke matrix for cylinder
    self.pMatrix = vtk.vtkMatrix4x4()

    self.vl.addWidget(self.vtkWidget)
    self.vl.addWidget(self.button)
    self.vl.addWidget(self.label)

    self.ren = vtk.vtkRenderer()
    self.ren.AddActor(self.actor)
    self.renWin = self.vtkWidget.GetRenderWindow()
    self.renWin.AddRenderer(self.ren)
    self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()

    #Settings
    self.ren.SetBackground(0.2, 0.2, 0.2)
    self.timeStep = 20 #ms
    self.total_t = 0

    #Inititalize Window, Interactor, Renderer, Layout
    self.frame.setLayout(self.vl)
    self.setCentralWidget(self.frame)
    self.ren.ResetCamera()
    self.show()
    self.iren.Initialize()

    # Create Timer
    self.timer = QtCore.QTimer()
    self.timer.timeout.connect(self.timerCallback)
    self.timer.start(self.timeStep)

def timerCallback(self, *args):
    self.total_t += self.timeStep / 1000
    #Rotate Cylinder
    angle = 2 * np.pi * self.total_t
    rotMatrix = np.array([[np.cos(angle), -np.sin(angle), 0],
                          [np.sin(angle), np.cos(angle), 0],
                          [0, 0, 1]])
    for i in range(3):
        for j in range(3):
            self.pMatrix.SetElement(i, j, rotMatrix[i, j])

    self.actor.PokeMatrix(self.pMatrix)
    self.ren.Render()


if __name__ == "__main__":
    app = Qt.QApplication(sys.argv)
    window = mainWindow()
    sys.exit(app.exec_())

Solution

  • After reading the paintEvent()-Method of this file, I managed to find out, that one needs to call the Render()-Method of the Interactor-object. So instead of self.ren.Render() one needs to call self.iren.Render(). Then everything works. Complete example code:

    import sys
    import vtk
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5 import Qt
    import numpy as np
    
    from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
    
    class mainWindow(Qt.QMainWindow):
        def __init__(self, parent = None):
            Qt.QMainWindow.__init__(self, parent)
    
            self.frame = Qt.QFrame()
            self.vl = Qt.QVBoxLayout()
            self.button = QtWidgets.QPushButton("TestButton")
            self.label = QtWidgets.QLabel("This is a label")
            self.vtkWidget = QVTKRenderWindowInteractor(self.frame)
    
            #Create Source
            self.source = vtk.vtkCylinderSource()
            self.source.SetCenter(0, 0, 0)
            self.source.SetRadius(5.0)
            #Create Mapper
            self.mapper = vtk.vtkPolyDataMapper()
            self.mapper.SetInputConnection(self.source.GetOutputPort())
            #Create Actor
            self.actor = vtk.vtkActor()
            self.actor.SetMapper(self.mapper)
    
            #Create poke matrix for cylinder
            self.pMatrix = vtk.vtkMatrix4x4()
    
            self.vl.addWidget(self.vtkWidget)
            self.vl.addWidget(self.button)
            self.vl.addWidget(self.label)
    
            self.ren = vtk.vtkRenderer()
            self.ren.AddActor(self.actor)
            self.renWin = self.vtkWidget.GetRenderWindow()
            self.renWin.AddRenderer(self.ren)
            self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
    
            #Settings
            self.ren.SetBackground(0.2, 0.2, 0.2)
            self.timeStep = 20 #ms
            self.total_t = 0
    
            #Inititalize Window, Interactor, Renderer, Layout
            self.frame.setLayout(self.vl)
            self.setCentralWidget(self.frame)
            self.ren.ResetCamera()
            self.show()
            self.iren.Initialize()
    
            # Create Timer
            self.timer = QtCore.QTimer()
            self.timer.timeout.connect(self.timerCallback)
            self.timer.start(self.timeStep)
    
        def timerCallback(self, *args):
            self.total_t += self.timeStep / 1000
            #Rotate Cylinder
            angle = 2 * np.pi * self.total_t
            rotMatrix = np.array([[np.cos(angle), -np.sin(angle), 0],
                                  [np.sin(angle), np.cos(angle), 0],
                                      [0, 0, 1]])
            for i in range(3):
                for j in range(3):
                    self.pMatrix.SetElement(i, j, rotMatrix[i, j])
    
            self.actor.PokeMatrix(self.pMatrix)
            self.iren.Render() #NOT: self.ren.Render()
    
    
    if __name__ == "__main__":
        app = Qt.QApplication(sys.argv)
        window = mainWindow()
        sys.exit(app.exec_())