【问题标题】:QGraphicsItem don't change pen of parent when chaning childQGraphicsItem在改变孩子时不改变父母的笔
【发布时间】:2021-08-17 06:55:49
【问题描述】:

我有多个QGraphicsItems,它们位于父子层次结构中。我试图在鼠标悬停时突出显示一个项目以在一个项目的基础上工作,这意味着如果鼠标悬停在一个项目上它应该突出显示。

突出显示效果很好,但如果我在孩子身上执行突出显示,那么突出显示也会自动发生在这样的父母身上,这是不希望的。这是问题的代码示例

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_())

将鼠标悬停在该行(即父行)上时,只有该行被突出显示。但是,当将鼠标悬停在矩形上时,两者都会突出显示。我认为这与矩形是订单项的子项有关。

我想保留父子层次结构,因为我必须根据父位置计算子位置,这样更容易。

有没有办法不将子项的突出显示级联到父项?

【问题讨论】:

    标签: python python-3.x pyside2 qgraphicsitem


    【解决方案1】:

    一个可能的解决方案是检查事件位置是否实际上在项目的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)
    

    不过,“离开”部分有点棘手:当鼠标悬停在孩子身上时,父母不会收到离开事件,它需要在所有孩子身上添加 scene 事件过滤器。

    由于场景事件过滤器只能在项目位于场景中时安装,因此您必须尝试在添加子项时在将父项添加到场景时安装过滤器。

    为了简单起见,我创建了两支宽度不同的相似笔,这样你只需要使用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)
    

    【讨论】:

    • 我感到困惑的是其他事件,例如 mouseDoubleClickEvent 工作正常,它们只发送到相应的子项而不是父项。为什么悬停事件不同?
    • @wasp256 悬停事件与点击事件不同;考虑 QWidget 的进入/离开事件:当你进入一个孩子时,你也进入了父母,这很明显,因为孩子总是在父母的rect() 中。 QGraphicsItem 有点不同,事实上,子项的几何形状可以在父项的之外,但即使看起来有点违反直觉,这个概念仍然存在:如果鼠标进入子项,它也被认为是在父项中输入的。点击事件是不同的,因为如果它们被处理并且它们严格依赖,它们就不会传播 ->
    • -> 发生按钮事件的项目(小部件或图形项目)的形状/几何形状。您可以处理来自父级的点击事件,即使它们在父级形状之外,并且为了做到这一点,您必须通过包含其所有子级的事件来实现父级的shape()(通过创建子级的总和shape()递归)。
    猜你喜欢
    • 1970-01-01
    • 2022-12-26
    • 2014-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-25
    • 1970-01-01
    • 2016-04-27
    相关资源
    最近更新 更多