PyQt6多边形边框内侧无法触发hover事件的解决咨询
问题描述
创建带边框的多边形,期望鼠标悬停时改变填充色。但当创建多个多边形且边框宽度大于4时,第一个多边形表现正常,其余多边形的边框内侧区域会触发hoverLeaveEvent(),被判定为不属于多边形范围。要求无需额外多边形或线条模拟边框的修复方法。
复现代码:
from PyQt6.QtWidgets import QMainWindow, QGraphicsScene, QGraphicsView, QGraphicsPolygonItem, QApplication from PyQt6.QtGui import QColor, QPolygonF, QBrush, QPen from PyQt6.QtCore import QPointF class Polygon(QGraphicsPolygonItem): def __init__(self, parent): super().__init__(parent) self.setBrush(QBrush(QColor(255, 0, 0, 120))) self.setPen(QPen(QColor(255, 0, 0), 10)) self.setAcceptHoverEvents(True) def hoverEnterEvent(self, event): self.setBrush(QBrush(QColor(255, 0, 0, 250))) def hoverLeaveEvent(self, event): self.setBrush(QBrush(QColor(255, 0, 0, 120))) class MyWindow(QMainWindow): def __init__(self): super(MyWindow, self).__init__() self.setGeometry(200, 0, 500, 600) self.canvas = QGraphicsView() self.canvas.setScene(QGraphicsScene(self)) polygon = QPolygonF([ QPointF(0, 0), QPointF(100, 0), QPointF(100, 100), QPointF(0, 100) ]) self.polygon_item = Polygon(polygon) self.canvas.scene().addItem(self.polygon_item) polygon = QPolygonF([ QPointF(110, 110), QPointF(150, 160), QPointF(200, 250), QPointF(200, 100) ]) self.polygon_item = Polygon(polygon) self.canvas.scene().addItem(self.polygon_item) polygon = QPolygonF([ QPointF(0, 200), QPointF(0, 300), QPointF(100, 300), QPointF(100, 200) ]) self.polygon_item = Polygon(polygon) self.canvas.scene().addItem(self.polygon_item) self.setCentralWidget(self.canvas) if __name__ == '__main__': app = QApplication([]) win = MyWindow() win.show() app.exec()
修复方案
问题根源是Qt的QGraphicsPolygonItem默认悬停检测的形状未将完整边框区域纳入,多item叠加时检测逻辑出现偏差。以下是无需额外元素的两种修复方法:
方法1:重写shape()方法扩展检测范围
重写shape(),将原多边形路径与边框描边路径合并,让悬停检测覆盖整个多边形+边框区域:
from PyQt6.QtWidgets import QMainWindow, QGraphicsScene, QGraphicsView, QGraphicsPolygonItem, QApplication from PyQt6.QtGui import QColor, QPolygonF, QBrush, QPen, QPainterPath, QPainterPathStroker from PyQt6.QtCore import QPointF class Polygon(QGraphicsPolygonItem): def __init__(self, polygon): super().__init__(polygon) self.setBrush(QBrush(QColor(255, 0, 0, 120))) self.pen = QPen(QColor(255, 0, 0), 10) self.setPen(self.pen) self.setAcceptHoverEvents(True) def shape(self): # 构建原多边形路径 path = QPainterPath() path.addPolygon(self.polygon()) # 创建描边器,宽度匹配边框宽度 stroker = QPainterPathStroker() stroker.setWidth(self.pen.width()) # 合并原路径与描边路径,得到完整检测形状 stroke_path = stroker.createStroke(path) path.addPath(stroke_path) return path def hoverEnterEvent(self, event): self.setBrush(QBrush(QColor(255, 0, 0, 250))) def hoverLeaveEvent(self, event): self.setBrush(QBrush(QColor(255, 0, 0, 120))) # MyWindow类保持不变,代码同复现代码部分 class MyWindow(QMainWindow): def __init__(self): super(MyWindow, self).__init__() self.setGeometry(200, 0, 500, 600) self.canvas = QGraphicsView() self.canvas.setScene(QGraphicsScene(self)) polygon = QPolygonF([ QPointF(0, 0), QPointF(100, 0), QPointF(100, 100), QPointF(0, 100) ]) self.polygon_item = Polygon(polygon) self.canvas.scene().addItem(self.polygon_item) polygon = QPolygonF([ QPointF(110, 110), QPointF(150, 160), QPointF(200, 250), QPointF(200, 100) ]) self.polygon_item = Polygon(polygon) self.canvas.scene().addItem(self.polygon_item) polygon = QPolygonF([ QPointF(0, 200), QPointF(0, 300), QPointF(100, 300), QPointF(100, 200) ]) self.polygon_item = Polygon(polygon) self.canvas.scene().addItem(self.polygon_item) self.setCentralWidget(self.canvas) if __name__ == '__main__': app = QApplication([]) win = MyWindow() win.show() app.exec()
方法2:重写contains()方法自定义检测逻辑
直接自定义鼠标点是否属于item的判断逻辑,先检测内部区域,再检测边框区域:
# 在Polygon类中添加以下方法,需导入Qt模块 from PyQt6.QtCore import Qt from PyQt6.QtGui import QPainterPath, QPainterPathStroker def contains(self, point): # 先判断是否在多边形内部 if self.polygon().containsPoint(point, Qt.FillRule.OddEvenFill): return True # 再判断是否在边框范围内 path = QPainterPath() path.addPolygon(self.polygon()) stroker = QPainterPathStroker() stroker.setWidth(self.pen().width()) stroke_path = stroker.createStroke(path) return stroke_path.contains(point)
内容的提问来源于stack exchange,提问作者Michal
相关产品推荐
相关产品推荐

