You need to enable JavaScript to run this app. 如何使用PySide实现可交互QAction并完成事件捕获功能?
优惠活动
大模型
产品
解决方案
定价
更多

如何使用PySide实现可交互QAction并完成事件捕获功能?

QGraphicsScene实现切割多边形交互动作的方案

问题说明

这是一个基于Qt Graphics View框架的交互功能实现问题,核心诉求是点击工具栏按钮后,可在画布上点击绘制临时线条完成多边形切割。
预期效果如下:
预期实现效果

原有思路问题

你之前尝试让QAction继承多类重写事件函数的思路不可行,原因是QAction本身是动作的抽象载体,不接收任何界面交互事件,所有鼠标、键盘事件都会发送给当前激活的画布控件(QGraphicsView/QGraphicsScene)。

正确实现逻辑

你只需要按以下步骤调整即可:

  1. 将切割动作设为可勾选状态,用于标记当前是否处于切割交互模式
  2. 自定义QGraphicsScene子类,重写鼠标、键盘事件处理函数,事件触发时判断当前是否处于切割模式,执行对应的临时线条绘制、多边形切割逻辑
  3. 如果有多个交互动作,可以单独封装每个动作的事件处理方法,激活对应动作时直接转发事件即可。

可运行示例代码

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PySide2.QtCore import Qt, QPointF
from PySide2.QtGui import QPen
from PySide2.QtWidgets import (QApplication, QMainWindow, QWidget,
                               QGraphicsView, QGraphicsScene,
                               QToolBar, QAction, QGraphicsLineItem)

class CustomGraphicsScene(QGraphicsScene):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.cut_mode_active = False
        self.temp_line = None
        self.start_point = None

    def mousePressEvent(self, event):
        if self.cut_mode_active and event.button() == Qt.LeftButton:
            click_pos = event.scenePos()
            if not self.start_point:
                # 第一次点击记录起点
                self.start_point = click_pos
                self.temp_line = QGraphicsLineItem()
                self.temp_line.setPen(QPen(Qt.red, 2, Qt.DashLine))
                self.addItem(self.temp_line)
            else:
                # 第二次点击完成切割线,此处可补充多边形切割逻辑
                self.temp_line.setLine(self.start_point.x(), self.start_point.y(), click_pos.x(), click_pos.y())
                # 重置状态,需要连续切割可注释下方两行
                self.start_point = None
                self.cut_mode_active = False
            return
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if self.cut_mode_active and self.start_point and self.temp_line:
            # 移动过程中更新临时线
            current_pos = event.scenePos()
            self.temp_line.setLine(self.start_point.x(), self.start_point.y(), current_pos.x(), current_pos.y())
        super().mouseMoveEvent(event)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape and self.cut_mode_active:
            # 按ESC取消切割
            self.cut_mode_active = False
            self.start_point = None
            if self.temp_line:
                self.removeItem(self.temp_line)
                self.temp_line = None
        super().keyPressEvent(event)

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.centralwidget = QWidget()
        self.graphicsView = QGraphicsView(self.centralwidget)
        self.setCentralWidget(self.centralwidget)
        # 使用自定义的Scene
        self.scene = CustomGraphicsScene(0,0,1000,1000)
        self.graphicsView.setScene(self.scene)
        # 开启鼠标追踪才能触发无按下状态的mouseMoveEvent
        self.graphicsView.setMouseTracking(True)
        
        toolbar = QToolBar("My toolbar")
        self.addToolBar(toolbar)
        
        self.knife_action = QAction("Knife", self)
        self.knife_action.setShortcut("K")
        self.knife_action.setCheckable(True)
        self.knife_action.triggered.connect(self.toggle_cut_mode)
        toolbar.addAction(self.knife_action)
    
    def toggle_cut_mode(self, is_checked):
        self.scene.cut_mode_active = is_checked
        if not is_checked:
            # 取消勾选时重置切割状态
            self.scene.start_point = None
            if self.scene.temp_line:
                self.scene.removeItem(self.scene.temp_line)
                self.scene.temp_line = None

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    w.resize(800,600)
    w.show()
    app.exec_()

内容的提问来源于stack exchange,提问作者Simon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 16:36:01