QGraphicsItem 在更改 child 时不更改 parent 的笔
QGraphicsItem don't change pen of parent when chaning child
我有多个 QGraphicsItem
位于 parent-child 层次结构中。我正在尝试在鼠标悬停时突出显示一个项目以在项目基础上工作,这意味着如果鼠标悬停在一个项目上它应该突出显示。
突出显示效果很好,但如果我在 child 上执行突出显示,那么突出显示也会自动发生在 parent 上,这是不希望的。这是问题的代码示例
from PySide2.QtCore import Qt
from PySide2.QtGui import QPen
from PySide2.QtWidgets import QGraphicsItem, \
QGraphicsScene, QGraphicsView, QGraphicsLineItem, QGraphicsSceneHoverEvent, \
QGraphicsRectItem, QMainWindow, QApplication
class TextBox(QGraphicsRectItem):
def __init__(self, parent: QGraphicsItem, x: float, y: float, width: float, height: float):
super().__init__(parent)
self.setParentItem(parent)
self.setAcceptHoverEvents(True)
pen = QPen(
Qt.white,
2,
Qt.SolidLine,
Qt.RoundCap,
Qt.RoundJoin
)
self.setPen(pen)
self.setRect(x, y, width, height)
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverEnterEvent(event)
current_pen = self.pen()
current_pen.setWidth(5)
self.setPen(current_pen)
def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverLeaveEvent(event)
current_pen = self.pen()
current_pen.setWidth(2)
self.setPen(current_pen)
class LineItem(QGraphicsLineItem):
def __init__(
self,
x1_pos: float,
x2_pos: float,
y1_pos: float,
y2_pos: float,
parent: QGraphicsItem = None
):
super().__init__()
self.setParentItem(parent)
self.setAcceptHoverEvents(True)
pen = QPen(
Qt.white,
2,
Qt.SolidLine,
Qt.RoundCap,
Qt.RoundJoin
)
self.setPen(pen)
self.setLine(
x1_pos,
y1_pos,
x2_pos,
y2_pos
)
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverEnterEvent(event)
current_pen = self.pen()
current_pen.setWidth(5)
self.setPen(current_pen)
def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverLeaveEvent(event)
current_pen = self.pen()
current_pen.setWidth(2)
self.setPen(current_pen)
class DiagramScene(QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
self.setBackgroundBrush(Qt.black)
line_item = LineItem(0, 200, 0, 0)
box = TextBox(line_item, 0, 0, 20, 20)
self.addItem(line_item)
class GraphicsView(QGraphicsView):
def __init__(self):
self.scene = DiagramScene()
super().__init__(self.scene)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setCentralWidget(GraphicsView())
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.setGeometry(100, 100, 800, 500)
mainWindow.show()
sys.exit(app.exec_())
将鼠标悬停在该行(即 parent)上时,只会突出显示该行。但是,当将鼠标悬停在矩形上时,两者都会突出显示。我认为这与矩形是订单项的 child 有关。
我想保留 parent-child 层次结构,因为我必须根据 parent 位置计算 child 位置,这样更容易。
有没有办法不将 child 项目的突出显示级联到 parent 为?
一种可能的解决方案是检查事件位置是否确实在项目的 boundingRect()
内,但这仅适用于仅垂直和水平延伸的项目。由于线条也可以有一定的角度,因此最好检查 shape()
而不是:
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverEnterEvent(event)
if self.shape().contains(event.pos()):
current_pen = self.pen()
current_pen.setWidth(5)
self.setPen(current_pen)
不过,“离开”部分有点棘手:parent 悬停在 child 上时不会收到离开事件,它需要添加 所有 children.
上的场景 事件过滤器
由于场景事件过滤器只能在项目出现在场景中时安装,因此您必须在添加 child 或 时尝试安装过滤器当 parent 添加到场景时。
为了简单起见,我创建了两个不同宽度的类似笔,这样你只需要使用setPen()
而不是不断地获取当前的笔并更改它。
class LineItem(QGraphicsLineItem):
def __init__(
self,
x1_pos: float,
x2_pos: float,
y1_pos: float,
y2_pos: float,
parent: QGraphicsItem = None
):
super().__init__()
self.setParentItem(parent)
self.setAcceptHoverEvents(True)
self.normalPen = QPen(
Qt.green,
2,
Qt.SolidLine,
Qt.RoundCap,
Qt.RoundJoin
)
self.hoverPen = QPen(self.normalPen)
self.hoverPen.setWidth(5)
self.setPen(self.normalPen)
self.setLine(
x1_pos,
y1_pos,
x2_pos,
y2_pos
)
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverEnterEvent(event)
if self.shape().contains(event.pos()):
self.setPen(self.hoverPen)
def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverLeaveEvent(event)
self.setPen(self.normalPen)
def itemChange(self, change, value):
if change == self.ItemChildAddedChange and self.scene():
value.installSceneEventFilter(self)
elif change == self.ItemChildRemovedChange:
value.removeSceneEventFilter(self)
elif change == self.ItemSceneHasChanged and self.scene():
for child in self.childItems():
child.installSceneEventFilter(self)
return super().itemChange(change, value)
def sceneEventFilter(self, child, event):
if event.type() == event.GraphicsSceneHoverEnter:
self.setPen(self.normalPen)
elif (event.type() == event.GraphicsSceneHoverLeave and
self.shape().contains(child.mapToParent(event.pos()))):
self.setPen(self.hoverPen)
return super().sceneEventFilter(child, event)
我有多个 QGraphicsItem
位于 parent-child 层次结构中。我正在尝试在鼠标悬停时突出显示一个项目以在项目基础上工作,这意味着如果鼠标悬停在一个项目上它应该突出显示。
突出显示效果很好,但如果我在 child 上执行突出显示,那么突出显示也会自动发生在 parent 上,这是不希望的。这是问题的代码示例
from PySide2.QtCore import Qt
from PySide2.QtGui import QPen
from PySide2.QtWidgets import QGraphicsItem, \
QGraphicsScene, QGraphicsView, QGraphicsLineItem, QGraphicsSceneHoverEvent, \
QGraphicsRectItem, QMainWindow, QApplication
class TextBox(QGraphicsRectItem):
def __init__(self, parent: QGraphicsItem, x: float, y: float, width: float, height: float):
super().__init__(parent)
self.setParentItem(parent)
self.setAcceptHoverEvents(True)
pen = QPen(
Qt.white,
2,
Qt.SolidLine,
Qt.RoundCap,
Qt.RoundJoin
)
self.setPen(pen)
self.setRect(x, y, width, height)
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverEnterEvent(event)
current_pen = self.pen()
current_pen.setWidth(5)
self.setPen(current_pen)
def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverLeaveEvent(event)
current_pen = self.pen()
current_pen.setWidth(2)
self.setPen(current_pen)
class LineItem(QGraphicsLineItem):
def __init__(
self,
x1_pos: float,
x2_pos: float,
y1_pos: float,
y2_pos: float,
parent: QGraphicsItem = None
):
super().__init__()
self.setParentItem(parent)
self.setAcceptHoverEvents(True)
pen = QPen(
Qt.white,
2,
Qt.SolidLine,
Qt.RoundCap,
Qt.RoundJoin
)
self.setPen(pen)
self.setLine(
x1_pos,
y1_pos,
x2_pos,
y2_pos
)
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverEnterEvent(event)
current_pen = self.pen()
current_pen.setWidth(5)
self.setPen(current_pen)
def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverLeaveEvent(event)
current_pen = self.pen()
current_pen.setWidth(2)
self.setPen(current_pen)
class DiagramScene(QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
self.setBackgroundBrush(Qt.black)
line_item = LineItem(0, 200, 0, 0)
box = TextBox(line_item, 0, 0, 20, 20)
self.addItem(line_item)
class GraphicsView(QGraphicsView):
def __init__(self):
self.scene = DiagramScene()
super().__init__(self.scene)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setCentralWidget(GraphicsView())
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.setGeometry(100, 100, 800, 500)
mainWindow.show()
sys.exit(app.exec_())
将鼠标悬停在该行(即 parent)上时,只会突出显示该行。但是,当将鼠标悬停在矩形上时,两者都会突出显示。我认为这与矩形是订单项的 child 有关。
我想保留 parent-child 层次结构,因为我必须根据 parent 位置计算 child 位置,这样更容易。
有没有办法不将 child 项目的突出显示级联到 parent 为?
一种可能的解决方案是检查事件位置是否确实在项目的 boundingRect()
内,但这仅适用于仅垂直和水平延伸的项目。由于线条也可以有一定的角度,因此最好检查 shape()
而不是:
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverEnterEvent(event)
if self.shape().contains(event.pos()):
current_pen = self.pen()
current_pen.setWidth(5)
self.setPen(current_pen)
不过,“离开”部分有点棘手:parent 悬停在 child 上时不会收到离开事件,它需要添加 所有 children.
上的场景 事件过滤器由于场景事件过滤器只能在项目出现在场景中时安装,因此您必须在添加 child 或 时尝试安装过滤器当 parent 添加到场景时。
为了简单起见,我创建了两个不同宽度的类似笔,这样你只需要使用setPen()
而不是不断地获取当前的笔并更改它。
class LineItem(QGraphicsLineItem):
def __init__(
self,
x1_pos: float,
x2_pos: float,
y1_pos: float,
y2_pos: float,
parent: QGraphicsItem = None
):
super().__init__()
self.setParentItem(parent)
self.setAcceptHoverEvents(True)
self.normalPen = QPen(
Qt.green,
2,
Qt.SolidLine,
Qt.RoundCap,
Qt.RoundJoin
)
self.hoverPen = QPen(self.normalPen)
self.hoverPen.setWidth(5)
self.setPen(self.normalPen)
self.setLine(
x1_pos,
y1_pos,
x2_pos,
y2_pos
)
def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverEnterEvent(event)
if self.shape().contains(event.pos()):
self.setPen(self.hoverPen)
def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
super().hoverLeaveEvent(event)
self.setPen(self.normalPen)
def itemChange(self, change, value):
if change == self.ItemChildAddedChange and self.scene():
value.installSceneEventFilter(self)
elif change == self.ItemChildRemovedChange:
value.removeSceneEventFilter(self)
elif change == self.ItemSceneHasChanged and self.scene():
for child in self.childItems():
child.installSceneEventFilter(self)
return super().itemChange(change, value)
def sceneEventFilter(self, child, event):
if event.type() == event.GraphicsSceneHoverEnter:
self.setPen(self.normalPen)
elif (event.type() == event.GraphicsSceneHoverLeave and
self.shape().contains(child.mapToParent(event.pos()))):
self.setPen(self.hoverPen)
return super().sceneEventFilter(child, event)