PyQt5与VTK集成时鼠标按钮释放事件不匹配问题
PyQt5 + VTK 多鼠标按钮点击时释放事件异常
问题重现
在PyQt5结合VTK开发时,通过自定义交互器样式注册鼠标点击事件,出现多按钮按压后释放事件错误的问题。按以下步骤操作:
- 按住中键
- 按下左键
- 释放左键
- 释放中键
输出结果为:
Middle Button pressed Left Button pressed Left Button released Left Button released
最后一个事件应为Middle Button released,但实际触发了左键释放事件。任意多按钮组合都会出现类似问题,总是最后按下的按钮的释放事件被重复触发,正确的释放事件丢失。该问题在纯VTK环境下不会出现,推测是QVTKRenderWindowInteractor的事件转发存在问题。
问题代码
# 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_())
解决方案
问题根源在于QVTKRenderWindowInteractor处理多鼠标按钮事件时,未正确跟踪每个按钮状态,导致释放事件映射错误。解决方法是放弃VTK的观察者模式,直接重写交互器样式的事件处理方法,覆盖VTK默认事件处理函数,而非通过AddObserver注册。
修改后的MyInteractorStyle类如下:
class MyInteractorStyle(vtkInteractorStyleTrackballCamera): def MiddleButtonPressEvent(self, obj, event): print('Middle Button pressed') # 如需保留原TrackballCamera的功能,取消注释下面一行 # super().MiddleButtonPressEvent(obj, event) return def MiddleButtonReleaseEvent(self, obj, event): print('Middle Button released') # 如需保留原TrackballCamera的功能,取消注释下面一行 # super().MiddleButtonReleaseEvent(obj, event) return def LeftButtonPressEvent(self, obj, event): print('Left Button pressed') # 如需保留原TrackballCamera的功能,取消注释下面一行 # super().LeftButtonPressEvent(obj, event) return def LeftButtonReleaseEvent(self, obj, event): print('Left Button released') # 如需保留原TrackballCamera的功能,取消注释下面一行 # super().LeftButtonReleaseEvent(obj, event) return
原理说明
VTK的交互器样式类(如vtkInteractorStyleTrackballCamera)本身提供了对应事件的虚函数(如LeftButtonPressEvent),重写这些函数可直接接管事件处理,避免QVTKRenderWindowInteractor事件转发的bug。这种方式符合VTK原生事件处理流程,在PyQt环境下能正确跟踪每个按钮状态,不会出现释放事件混淆的问题。
运行修改后的代码,重复操作步骤,输出将变为正确的:
Middle Button pressed Left Button pressed Left Button released Middle Button released
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

