鼠标悬停在 PySide QGraphicsPathItem 上
Mouse hover over a PySide QGraphicsPathItem
当我尝试在 QGraphicsPathItem 上注册鼠标悬停事件时,我遇到了奇怪的行为。
在示例代码中(我从 qt5 弹性节点示例中破解)我画了一条简单的曲线。悬停在曲线上会改变曲线的颜色,但悬停事件不会记录在曲线的右下半部分。此外,实际上只有曲线的左上半部分会改变颜色。
我添加了一个 QPainterPathStroker,认为悬停事件会在此区域内注册,但事实并非如此。
我的目标是让鼠标移动进入、进入和离开 QPainterPathStroker 区域注册为 QGraphicsPathItem 上的悬停事件。
感谢任何帮助。谢谢
from PySide import QtCore, QtGui
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):
return QtCore.QRectF(-100,-100,120,120)
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()
本例的问题是boundingRect()
没有覆盖完整的path()
,而这个是paint()
方法使用的,解决方法是return self.shape().boundingRect()
:
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 = a + QtCore.QPointF(dx, 0)
d = QtCore.QPointF(x2, y2)
c = d - QtCore.QPointF(dy, 0)
path.moveTo(a)
path.cubicTo(b,c,d)
self.setPath(path)
self.hover = False
def hoverEnterEvent(self, event):
QtGui.QGraphicsPathItem.hoverEnterEvent(self, event)
self.hover = True
self.update()
def hoverMoveEvent(self, event):
# print(event)
QtGui.QGraphicsPathItem.hoverMoveEvent(self, event)
def hoverLeaveEvent(self, event):
QtGui.QGraphicsPathItem.hoverLeaveEvent(self, event)
self.hover = False
self.update()
def boundingRect(self):
return self.shape().boundingRect()
def paint(self, painter, option, widget):
c = QtCore.Qt.red if self.hover else 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
使用 PySide2,上面的代码有两个问题:
在Edge.__init__
而不是
self.setAcceptsHoverEvents(True)
使用:
self.setAcceptHoverEvents(True)
(方法名称中没有 's')。
在GraphWidget.drawBackground
,而不是
painter.fillRect(rect.intersects(sceneRect), QBrush(gradient))
使用其中之一
painter.fillRect(rect.intersected(sceneRect), QBrush(gradient))
('ed' 而不是方法名称中的 's')或
painter.fillRect(rect & sceneRect, QBrush(gradient))
当我尝试在 QGraphicsPathItem 上注册鼠标悬停事件时,我遇到了奇怪的行为。
在示例代码中(我从 qt5 弹性节点示例中破解)我画了一条简单的曲线。悬停在曲线上会改变曲线的颜色,但悬停事件不会记录在曲线的右下半部分。此外,实际上只有曲线的左上半部分会改变颜色。
我添加了一个 QPainterPathStroker,认为悬停事件会在此区域内注册,但事实并非如此。
我的目标是让鼠标移动进入、进入和离开 QPainterPathStroker 区域注册为 QGraphicsPathItem 上的悬停事件。
感谢任何帮助。谢谢
from PySide import QtCore, QtGui
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):
return QtCore.QRectF(-100,-100,120,120)
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()
本例的问题是boundingRect()
没有覆盖完整的path()
,而这个是paint()
方法使用的,解决方法是return self.shape().boundingRect()
:
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 = a + QtCore.QPointF(dx, 0)
d = QtCore.QPointF(x2, y2)
c = d - QtCore.QPointF(dy, 0)
path.moveTo(a)
path.cubicTo(b,c,d)
self.setPath(path)
self.hover = False
def hoverEnterEvent(self, event):
QtGui.QGraphicsPathItem.hoverEnterEvent(self, event)
self.hover = True
self.update()
def hoverMoveEvent(self, event):
# print(event)
QtGui.QGraphicsPathItem.hoverMoveEvent(self, event)
def hoverLeaveEvent(self, event):
QtGui.QGraphicsPathItem.hoverLeaveEvent(self, event)
self.hover = False
self.update()
def boundingRect(self):
return self.shape().boundingRect()
def paint(self, painter, option, widget):
c = QtCore.Qt.red if self.hover else 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
使用 PySide2,上面的代码有两个问题:
在Edge.__init__
而不是
self.setAcceptsHoverEvents(True)
使用:
self.setAcceptHoverEvents(True)
(方法名称中没有 's')。
在GraphWidget.drawBackground
,而不是
painter.fillRect(rect.intersects(sceneRect), QBrush(gradient))
使用其中之一
painter.fillRect(rect.intersected(sceneRect), QBrush(gradient))
('ed' 而不是方法名称中的 's')或
painter.fillRect(rect & sceneRect, QBrush(gradient))