PySide QGraphicsPathItem鼠标悬停事件异常问题求助
解决QGraphicsPathItem悬停事件仅部分生效的问题
我来帮你搞定这个悬停异常的问题!你的代码里核心问题出在手动硬编码的boundingRect范围太小,导致曲线的右下部分根本不在事件检测的范围内——哪怕你设置了QPainterPathStroker也没用,因为Qt的图形项事件触发有个前提:鼠标必须先落在boundingRect定义的区域内,才会进一步检查shape的范围。
问题分析
你当前的boundingRect返回的是QRectF(-100,-100,120,120),这个矩形的右下角坐标是(-100+120, -100+120) = (20,20),但你的曲线终点是(120,120),远远超出了这个范围。所以鼠标在右下部分时,连第一步的boundingRect检测都过不了,自然触发不了悬停事件。
解决方案
只需要修改boundingRect方法,让它自动适配你的shape范围(也就是你用QPainterPathStroker生成的宽笔触区域),而不是手动写死数值。
修改后的关键代码片段:
def boundingRect(self): # 基于shape自动计算包围盒,确保覆盖所有需要检测事件的区域 return self.shape().boundingRect()
完整修改后的代码
from PySide import QtCore, QtGui import math # 补上原代码缺失的math模块导入 class Edge(QtGui.QGraphicsPathItem): def __init__(self): QtGui.QGraphicsPathItem.__init__(self) self.setAcceptsHoverEvents(True) path = QtGui.QPainterPath() x1 = -100 x2 = 120 y1 = -100 y2 = 120 dx = abs(x1-x2)/2 dy = abs(y1-y2)/2 a = QtCore.QPointF(x1, y1) b = QtCore.QPointF(x1+dx, y1) c = QtCore.QPointF(x2-dy, y2) d = QtCore.QPointF(x2, y2) path.moveTo(a) path.cubicTo(b,c,d) self.setPath(path) self.hover = False def hoverEnterEvent(self, event): self.hover = True QtGui.QGraphicsPathItem.hoverEnterEvent(self, event) def hoverMoveEvent(self, event): QtGui.QGraphicsPathItem.hoverMoveEvent(self, event) def hoverLeaveEvent(self, event): self.hover = False QtGui.QGraphicsPathItem.hoverLeaveEvent(self, event) def boundingRect(self): # 修复:基于shape自动计算包围盒,覆盖所有可交互区域 return self.shape().boundingRect() def paint(self, painter, option, widget): if self.hover: c = QtCore.Qt.red else: c = QtCore.Qt.black painter.setPen(QtGui.QPen(c, 10, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) painter.drawPath(self.path()) painter.setPen(QtGui.QPen(QtCore.Qt.blue, 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) painter.drawPath(self.shape()) def shape(self): s = QtGui.QPainterPathStroker() s.setWidth(30) s.setCapStyle(QtCore.Qt.RoundCap) path = s.createStroke(self.path()) return path class GraphWidget(QtGui.QGraphicsView): def __init__(self): QtGui.QGraphicsView.__init__(self) scene = QtGui.QGraphicsScene(self) scene.setItemIndexMethod(QtGui.QGraphicsScene.NoIndex) scene.setSceneRect(-200, -200, 400, 400) self.setScene(scene) self.setRenderHint(QtGui.QPainter.Antialiasing) self.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QtGui.QGraphicsView.AnchorViewCenter) self.edge = Edge() scene.addItem(self.edge) self.scale(0.8, 0.8) self.setMinimumSize(400, 400) self.setWindowTitle(self.tr("Elastic Nodes")) def wheelEvent(self, event): self.scaleView(math.pow(2.0, -event.delta() / 240.0)) def drawBackground(self, painter, rect): sceneRect = self.sceneRect() rightShadow = QtCore.QRectF(sceneRect.right(), sceneRect.top() + 5, 5, sceneRect.height()) bottomShadow = QtCore.QRectF(sceneRect.left() + 5, sceneRect.bottom(), sceneRect.width(), 5) if rightShadow.intersects(rect) or rightShadow.contains(rect): painter.fillRect(rightShadow, QtCore.Qt.darkGray) if bottomShadow.intersects(rect) or bottomShadow.contains(rect): painter.fillRect(bottomShadow, QtCore.Qt.darkGray) gradient = QtGui.QLinearGradient(sceneRect.topLeft(), sceneRect.bottomRight()) gradient.setColorAt(0, QtCore.Qt.white) gradient.setColorAt(1, QtCore.Qt.lightGray) painter.fillRect(rect.intersect(sceneRect), QtGui.QBrush(gradient)) painter.setBrush(QtCore.Qt.NoBrush) painter.drawRect(sceneRect) def scaleView(self, scaleFactor): factor = self.matrix().scale(scaleFactor, scaleFactor).mapRect(QtCore.QRectF(0, 0, 1, 1)).width() if factor < 0.07 or factor > 100: return self.scale(scaleFactor, scaleFactor) def mouseMoveEvent(self, event): self.edge.update() QtGui.QGraphicsView.mouseMoveEvent(self, event) widget = GraphWidget() widget.show()
额外说明
- 我补上了原代码缺失的
import math——因为wheelEvent里用到了math.pow,不导入会直接报错。 - 现在
boundingRect会自动跟随shape的范围,不管你怎么调整曲线路径或者stroke宽度,都能保证所有可交互区域被覆盖,悬停事件就能在整个30px宽的笔触范围内正常触发了。
内容的提问来源于stack exchange,提问作者chengxudude
相关产品推荐
相关产品推荐

