You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 16:44:57