Reusing a QVTKRenderWindowInteractor Widget after closing the window using close/X button

Hello all,

I have a PyQt application with a QMainWindow that opens a subwindow/QWidget. The subwindow contains a QVTKRenderWindowInteractor widget. I want to be able to call the subwindow, close it, and then re-open it again as needed. However, if the window is closed using the close/X button, the QVTK Widget no longer renders. If the window is instead closed by calling the close() method, the QVTK Widget still works. The code below replicates the issue.

Why does this occur? What does Qt or VTK do differently between closing the window with “X” button and calling .close()?

Is there a way to restart the QVTK widget after closing? Or is it actually better practice to just create another instance with each button press and closure?

Thanks.

import os, sys

from PyQt5 import QtGui, QtWidgets, QtCore

import vtk
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor

#%% Parameters
wd = '...'
filename = '...'

#%% Defined functions and classes

def read_vtp(filepath):
    reader = vtk.vtkXMLPolyDataReader()
    reader.SetFileName(filepath)
    reader.Update()
    return reader.GetOutput()

class ViewSurface(QtWidgets.QWidget):
    
    def __init__(self, surface):
        super().__init__()
        
        self.resize(1000, 1000)
        
        self.Layout = QtWidgets.QVBoxLayout()
        self.setLayout(self.Layout)
        
        self.Surface = surface
        self.Mapper = vtk.vtkPolyDataMapper()
        self.Mapper.SetInputData(self.Surface)
        self.Actor = vtk.vtkActor()
        self.Actor.SetMapper(self.Mapper)
        
        self.QVTKRWI = QVTKRenderWindowInteractor(parent=self)

        self.Layout.addWidget(self.QVTKRWI)

        self.Renderer = vtk.vtkRenderer()
        self.Renderer.AddActor(self.Actor)

        self.RenderWindow = self.QVTKRWI.GetRenderWindow()
        self.RenderWindow.AddRenderer(self.Renderer)
        self.Interactor = self.RenderWindow.GetInteractor()
        self.Interactor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

        self.CloseButton = QtWidgets.QPushButton(text='Close')
        self.Layout.addWidget(self.CloseButton)
        self.CloseButton.clicked.connect(self.close)

    def Show(self):
        
        self.show()
        self.Interactor.Initialize()
        self.RenderWindow.Render()
        

class MainWindow(QtWidgets.QMainWindow):
    
    def __init__(self):
        super().__init__()
    
        self.PushButton = QtWidgets.QPushButton(text='Open Widget')
        self.setCentralWidget(self.PushButton)
        
        self.PushButton.clicked.connect(self.OpenWidget)
        
        self.Surface = None
        self.ViewSurface = None
        
    def OpenWidget(self):
        
        if not self.ViewSurface:
            self.ViewSurface = ViewSurface(self.Surface)
        self.ViewSurface.Show()
        
#%% Script

filepath = os.path.join(wd, filename)
surface = read_vtp(filepath)

app = QtWidgets.QApplication(sys.argv)

window = MainWindow()
window.Surface = surface
window.show()

app.exec()

The main window is a single button “Open Widget”:

image

Pressing the button calls a new window with a QVTK widget:

If the window is called using the Close button and the “Open Widget” button is pressed again, the QVTK widget appears normally. However, if the window is closed using the “X” button and “Open Widget” is pressed, the QVTK widget doesn’t render:

Hello,

Maybe the window is being destroyed when closing it with the “X” button. Try calling setAttribute(Qt.WA_DeleteOnClose, False) on the window in question to change such behavior. If you want, you can also try setAttribute(Qt.WindowCloseButtonHint, False) to hide the “X” button.

take care,

PC

Hello Paulo,

I appreciate the help.

  • In the above example, I tried self.setAttribute(Qt.WA_DeleteOnClose, False) in ViewSurface._init__(), but the QVTK Widget was still un-rendered. Maybe the problem is with VTK instead of Qt here?

  • Disabling the “X” button worked (Note: in PyQt5, it has to be done through setWindowFlag() instead of setAttribute()).

Going forward, that works fine, but if anyone knows, I would still like to know what’s going behind the scenes in VTK and how to “restart” a VTK Widget or avoid its destruction if that’s the case.

Hi,

In Qt, if you give a QWidget a parent, then it is managed by Qt. That is, if the parent widget is destroyed, all its chidren are too. Note that the default behavior of close() is to just hide the widget. The “X” button is supposed to function just like calling close(). Getting different results is unexpected.

I’m unsure what is supposed to happen in OpenWidget(self). Maybe self.ViewSurface refered object is being de-allocated but the member variable itself is still there, returning true.

regards,

PC