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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 22:05:26