我使用PyQT5和VTK来显示信息,并一直使用观察者通过交互器风格注册鼠标单击事件。由于某种原因,当单击多个按钮时,释放事件会丢失。我修改了 VTK QT 示例之一来执行此操作:
# Contributed by Eric E Monson
import sys
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import QApplication
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera,vtkInteractorStyleUser
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderer
)
class MyInteractorStyle(vtkInteractorStyleTrackballCamera):
def __init__(self, parent=None):
self.AddObserver('LeftButtonPressEvent', self.left_button_press_event)
self.AddObserver('LeftButtonReleaseEvent', self.left_button_release_event)
self.AddObserver('MiddleButtonPressEvent', self.middle_button_press_event)
self.AddObserver('MiddleButtonReleaseEvent', self.middle_button_release_event)
def middle_button_press_event(self, obj, event):
print('Middle Button pressed')
#self.OnMiddleButtonDown()
return
def middle_button_release_event(self, obj, event):
print('Middle Button released')
#self.OnMiddleButtonUp()
return
def left_button_press_event(self, obj, event):
print('Left Button pressed')
#self.OnLeftButtonDown()
return
def left_button_release_event(self, obj, event):
print('Left Button released')
#self.OnLeftButtonUp()
return
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(603, 553)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.gridlayout = QtWidgets.QGridLayout(self.centralWidget)
self.vtkWidget = QVTKRenderWindowInteractor(self.centralWidget)
self.gridlayout.addWidget(self.vtkWidget, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralWidget)
class SimpleView(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ren = vtkRenderer()
self.ui.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
self.iren = self.ui.vtkWidget.GetRenderWindow().GetInteractor()
self.iren.SetInteractorStyle(MyInteractorStyle())
# 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)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SimpleView()
window.show()
window.iren.Initialize() # Need this line to actually show the render inside Qt
sys.exit(app.exec_())
执行以下操作:
产生以下输出:
最后一个事件显然是不正确的。任何按下和按住的排列似乎都能做到这一点。似乎事件注册不起作用,因为按下的最后一个按钮是“释放”的。当在 pyqt 之外使用 VTK 时,这种行为不会持续存在,这让我相信 QVTKRenderWindowInteractor 存在问题。
我创建了一个继承自
CustomQVTKRenderWindowInteractor
QVTKRenderWindowInteractor
类。这使我们能够直接处理 Qt 鼠标事件,而不是依赖 VTK 的事件系统。我重写了自定义类中的 mousePressEvent
mouseReleaseEvent
方法。这些方法现在直接处理 Qt 鼠标事件,确保准确捕获每个按钮的按下和释放。我删除了之前用于观察VTK事件的MyInteractorStyle
vtkInteractorStyleTrackballCamera
进行 3D 导航,同时通过 Qt 处理鼠标按钮事件。在 SimpleView
QVTKRenderWindowInteractor
替换了标准 CustomQVTKRenderWindowInteractor
。
import sys
from typing import Optional
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
class CustomQVTKRenderWindowInteractor(QVTKRenderWindowInteractor):
def __init__(self, parent: Optional[QWidget] = None):
super().__init__(parent)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
def mousePressEvent(self, event: QtCore.QEvent):
super().mousePressEvent(event)
self._print_button_event(event, "pressed")
def mouseReleaseEvent(self, event: QtCore.QEvent):
super().mouseReleaseEvent(event)
self._print_button_event(event, "released")
def _print_button_event(self, event: QtCore.QEvent, action: str):
button_map = {
QtCore.Qt.LeftButton: "Left",
QtCore.Qt.MiddleButton: "Middle",
QtCore.Qt.RightButton: "Right"
}
button = button_map.get(event.button(), "Unknown")
print(f"{button} Button {action}")
class Ui_MainWindow:
def setupUi(self, MainWindow: QMainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(603, 553)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.gridlayout = QtWidgets.QGridLayout(self.centralWidget)
self.vtkWidget = CustomQVTKRenderWindowInteractor(self.centralWidget)
self.gridlayout.addWidget(self.vtkWidget, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralWidget)
class SimpleView(QMainWindow):
def __init__(self, parent: Optional[QWidget] = None):
super().__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ren = vtkRenderer()
self.ui.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
self.iren = self.ui.vtkWidget.GetRenderWindow().GetInteractor()
self.iren.SetInteractorStyle(vtkInteractorStyleTrackballCamera())
# Create and set up the actor
source = vtkSphereSource()
source.SetCenter(0, 0, 0)
source.SetRadius(5.0)
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
actor = vtkActor()
actor.SetMapper(mapper)
self.ren.AddActor(actor)
def initialize(self):
try:
self.iren.Initialize()
except Exception as e:
print(f"Error initializing render window: {e}")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SimpleView()
window.show()
window.initialize()
sys.exit(app.exec_())
通过 Qt 的事件系统而不是 VTK 的事件系统处理鼠标事件,我们确保正确捕获所有鼠标按钮的按下和释放,即使涉及多个按钮也是如此。这种方法利用了 Qt 强大的事件处理系统,该系统与 PyQt5 应用程序框架更好地集成
执行以下操作:
中按并按住
按下中间按钮