In the last post, I discussed how to get started with VTK on Python. In this post, I will show how to add support to show frames per second (FPS). The idea to calculate FPS is straight forward: keep track of the number of frames (N) that were rendered in last T seconds. Then fps defined a N/T fps.
To calculate FPS we will add an observer to the EndEvent
command of the vtkRenderer
. In the callback function, we will count the number of frames rendered in the last T seconds and calculate FPS. Here is the complete code of the FpsObserver
:
import vtk from timeit import default_timer as timer class FpsObserver: def __init__(self, renderer, x=0, y=0): self.mRenderer = renderer self.mRenderer.AddObserver(vtk.vtkCommand.EndEvent, self) self.ActorPosX = x self.ActorPosY = y self.mFrameCount = 0 # Number of frames collected since last FPS was calculated. self.mStartTime = timer() # The last time FPS was calculated. self.mFpsUpdateRate = 1 # How often to update FPS in seconds. self._createFpsTextActor() def setPosition(self, x, y): self.ActorPosX = x self.ActorPosY = y self.mFpsActor.SetPosition(self.ActorPosX, self.ActorPosY) def __call__(self, caller, event): if event == "EndEvent": self.mFrameCount = self.mFrameCount + 1 if timer() - self.mStartTime > self.mFpsUpdateRate: _currentTime = timer() _duration = _currentTime - self.mStartTime _fps = self.mFrameCount/_duration print("fps={:.3f}".format(_fps)) self.mFpsActor.SetInput("FPS: {:.2f}".format(_fps)) self.mStartTime = _currentTime self.mFrameCount = 0 def _createFpsTextActor(self): self.mFpsActor = vtk.vtkTextActor() self.mFpsActor.GetTextProperty().SetFontFamilyAsString("Georgia") self.mFpsActor.GetTextProperty().SetFontSize(20) self.mFpsActor.GetTextProperty().SetColor([1, 1, 1]) self.mFpsActor.SetPosition(self.ActorPosX, self.ActorPosY) self.mRenderer.AddActor(self.mFpsActor)
To use FpsObserver
, we just need to initialize it as self.mFpsObserver = FpsObserver.FpsObserver(self.mRenderer)
. That’s it, this will display the FPS for last one seconds!
The visualization toolkit (VTK) is a open source library displaying scientific data. VTK is maintained by Kitware, the same company which gave us CMake. VTK is written in C/C++ but it comes with Python bindings and can be installed from https://pypi.org/project/vtk/. In this post, I am going to show how to start using VTK from Python using PyQt5.
Qt has two package for using with Python: PySide2 and PyQt5. PySide2 is the official module for Python but for a long time there was no official module and only PyQt5 was available. You can refer to https://www.learnpyqt.com/blog/pyqt5-vs-pyside2/ to understand the differences (they are mostly same) between two modules. I am going to use PyQt5 but the VTK module itself supports both Qt modules.
VTK provides a
class which inherits from QVTKRenderWindowInteractor
QWidget
, QGLWidget, or any other custom class inherited from QWidget
. We will add QVTKRenderWindowInteractor
to a QMainWindow
and use vtkRenderer
to render a Hello, World sphere. To decouple user interface (Qt) and rendering (VTK) I will create a VtkWindow
class and use it from a MainWindow
which is purely for VTK.
Lets first create the MainWindow:
from PyQt5 import QtCore, QtWidgets import sys import VtkWindow class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setWindowState(QtCore.Qt.WindowMaximized) self.mVtkWindow = VtkWindow.VtkWindow() self.setCentralWidget(self.mVtkWindow) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) window = MainWindow() window.show() app.exec_()
If we comment lines 10 and 11 and run the MainWindow.py, it will display a blank Qt Window. Now lets see how to add VTK support to it by adding a VtkWindow
class:
from PyQt5 import QtWidgets import vtk import vtkmodules.qt vtkmodules.qt.QVTKRWIBase = "QGLWidget" from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor # VtkWindow must be derived from QFrame: https://vtk.org/Wiki/VTK/Examples/Python/Widgets/EmbedPyQt class VtkWindow(QtWidgets.QFrame): def __init__(self, parent=None): super(QtWidgets.QWidget, self).__init__(parent) # Create a VTK widget and add it to the QFrame. self.setLayout(QtWidgets.QVBoxLayout()) self.mVtkWidget = QVTKRenderWindowInteractor(self) self.layout().addWidget(self.mVtkWidget) self.layout().setContentsMargins(0, 0, 0, 0) # Get the render window and set an interactor. self.mRenderWindow = self.mVtkWidget.GetRenderWindow() self.mInteractor = self.mRenderWindow.GetInteractor() self.mInteractor.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera()) self.mInteractor.Initialize() # Create a new renderer and set the background color. self.mRenderer = vtk.vtkRenderer() self.setBackgroundColor([0.5, 0.5, 0.5]) self.mRenderWindow.AddRenderer(self.mRenderer) # Set the Vtk Window title. self.mTitleActor = None self.setTitle("pyVtkLib Demo") # Called when QFrame is resized. def resizeEvent(self, newSize): textSize = [0, 0] self.mTitleActor.GetSize(self.mRenderer, textSize) width = int( (self.width() - textSize[0]) / 2.0) height = self.height() - textSize[1] self.mTitleActor.SetPosition(width, height - 10) def setBackgroundColor(self, color): self.mRenderer.SetBackground(color) def setTitle(self, title): if not self.mTitleActor: self.mTitleActor = vtk.vtkTextActor() self.mTitleActor.GetTextProperty().SetFontFamilyAsString("Georgia") self.mTitleActor.GetTextProperty().SetFontSize(30) self.mTitleActor.GetTextProperty().SetColor([1, 0, 0]) self.mTitleActor.SetInput(title) self.mTitleActor.SetPosition(0, 0) self.mRenderer.AddActor(self.mTitleActor) else: self.mTitleActor.SetInput(title)
VTK module for Python comes with a QVTKRenderWindowInteractor
class which by default inherits from QWidget
for PyQt5. In lines 4-5, we first change it to to use QGLWidget
so that rendering will be done using OpenGL instead of software renderer. Next, we create a class called VtkWindow
which inherits from QWidget
so that it can be use from Qt UI. Note, that it is recommended to inherit from QFrame
and not QWidget
as QVTKRenderWindowInteractor
cannot be reparented. More discussion on this topic can be found at EmbedPyQt example on VTK website. Next, we create an instance of QVTKRenderWindowInteractor
and add it to VtkWindow
class through a QVBoxLayout
.
After that it is usual VTK stuff of creating a vtkRenderingWindow
, vtkRenderWindowInteractor
, and vtkRenderer
. I prefer to use vtkInteractorStyleTrackballCamera
which I find far more intuitive than the default vtkInteractorStyleJoystickCamera
.
I render scene title at the top-middle of the screen and in order to place it here I listen to QFrame::resizeEvent to determine te current width and height of the QFrame.
Run the MainWindow.py from a terminal and it will display a windows with text pyVtkLib Demo printed in the middle-center of the window. In the next tutorial I will show how to measure and show frames per second to the VtkWindow
.
The code from this tutorial and any other future enhancements I will do will be available from saurabhg17/pyVtkLib repository at GitHub.