如何使用PySide实现可交互QAction并完成事件捕获功能?
QGraphicsScene实现切割多边形交互动作的方案
问题说明
这是一个基于Qt Graphics View框架的交互功能实现问题,核心诉求是点击工具栏按钮后,可在画布上点击绘制临时线条完成多边形切割。
预期效果如下:
原有思路问题
你之前尝试让QAction继承多类重写事件函数的思路不可行,原因是QAction本身是动作的抽象载体,不接收任何界面交互事件,所有鼠标、键盘事件都会发送给当前激活的画布控件(QGraphicsView/QGraphicsScene)。
正确实现逻辑
你只需要按以下步骤调整即可:
- 将切割动作设为可勾选状态,用于标记当前是否处于切割交互模式
- 自定义QGraphicsScene子类,重写鼠标、键盘事件处理函数,事件触发时判断当前是否处于切割模式,执行对应的临时线条绘制、多边形切割逻辑
- 如果有多个交互动作,可以单独封装每个动作的事件处理方法,激活对应动作时直接转发事件即可。
可运行示例代码
#! /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
相关产品推荐
相关产品推荐

