The issue is code with QVTKRenderWindowInteractor on macOS that worked with
VTK 9.4.2 and PySide6 6.9.3 fails to display anything after updating to PySide 6.10.1.
No error message is displayed–it just hangs. Pressing ctrl-c during the run yields the following
message:
Error calling Python override of QWidget::paintEvent(): Traceback (most recent call last):
File "/Users/nchampag/Documents/python/virtual/pyside6/lib/python3.13/site-packages/vtkmodules/qt/QVTKRenderWindowInteractor.py", line 465, in paintEvent
self._Iren.Render()
Note that we tried using VTK 9.5.2, but it yielded the same hanging result.
FYI, the example works on Windows 10 with VTK 9.4.2 and PySide6 6.9.3/6.10.1.
Here is an example script:
#!/usr/bin/env python3
import sys
import vtkmodules.vtkInteractionStyle
import vtkmodules.vtkRenderingUI
import vtkmodules.vtkRenderingOpenGL2
from PySide6 import QtGui, QtWidgets
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderer
)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.frame = QtWidgets.QFrame()
self.vl = QtWidgets.QVBoxLayout()
self.vtkWidget = QVTKRenderWindowInteractor(self.frame)
self.vl.addWidget(self.vtkWidget)
self.ren = vtkRenderer()
self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()
# Create source
source = vtkSphereSource()
source.SetCenter(0, 0, 0)
source.SetRadius(5.0)
# Create a mapper
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
# Create an actor
actor = vtkActor()
actor.SetMapper(mapper)
self.ren.AddActor(actor)
self.ren.ResetCamera()
self.frame.setLayout(self.vl)
self.setCentralWidget(self.frame)
self.show()
self.iren.Initialize()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec())
This hang sounds similar to the one in issue !19361, which was fixed in VTK 9.3.1. So it’s not the exact same issue, but it is probably related.
The call to Render() on macOS will briefly trigger the macOS event loop for the render window, in order to ensure that macOS maps the window to the screen. In your case, it sounds as if running the event loop like this causes a deadlock with Qt6.10.
So I can guess at a possible solution. The vtkCocoaRenderWindow::Render() code can be changed so that it doesn’t trigger the macOS event loop if Qt is managing the window, since Qt will take care of mapping the window.
I can work on this, but probably not this week because I’m swamped…
Thanks for the reply. A quick solution isn’t needed at this time, but I can see macOS users wondering what’s happening as Qt6.10 is adopted by VTK/Qt/Python users.
I looked into this in issue #19915 and it doesn’t seem related to VTK’s use of the macOS event loop.
It isn’t actually hanging self._Iren.Render(), the problem is that Qt is invoking paintEvent() over and over and this is causing Render() to be called repeatedly even though the window isn’t even showing on the screen. In other words, the issue seems to be an infinite loop in Qt, rather than a hang in VTK.
If you want to work on this issue yourself, start with the following code in QVTKRenderWindowInteractor.py. Try making it so that instead of calling Render() on every paintEvent(), it only calls Render() if certain conditions are met (I’m not sure myself what those conditions should be):
It does seem to fix the hang (on macOS at least, I didn’t try Linux or Windows), but it’s incorrect code because it doesn’t call the type() method in order to get the event type. The correct code for checking the event type is:
def paintEvent(self, ev):
if ev.type() == QEvent.Type.Paint:
self._Iren.Render()
For your fix, ev == QEvent.Type.Paint always evaluates to False, so it’s really no different from simply removing self._Iren.Render().
So I guess the question is, why does removing this Render() fix this macOS/PySide6.10 issue? And will removing the Render() have any side-effects, i.e. will it still render in all situations where it needs to?