QGraphicsItem 在组删除时从场景中消失

QGraphicsItem disappearing from scene on group remove

我遇到了 QGraphicsItem 和 QGraphicsItemGroup 的问题。我可以添加到组并四处移动组,但是当我尝试从组中删除该项目时,它会被删除。我根本找不到场景中的对象。 如果我使用标准 class (QGraphicsRectItem)

同样的问题

从组中删除项目同时将其保留在现场的正确方法是什么。 代码如下:

from PyQt5.QtCore import Qt,QRect
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsItem,QApplication, QGraphicsItemGroup, QGraphicsEllipseItem,QPushButton , QVBoxLayout

from PyQt5 import QtCore, QtGui, QtWidgets



class Rectangle(QtWidgets.QGraphicsItem):
    def __init__(self,rect):
        super(Rectangle, self).__init__(parent=None)
        self.rect = QtCore.QRectF(rect[0], rect[1], rect[2], rect[3])
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)
        self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable)
        self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges)
    def boundingRect(self):
        return self.rect

    def paint(self, painter, option, widget=None):
        painter.save()
        painter.setRenderHints(
            QtGui.QPainter.Antialiasing
            | QtGui.QPainter.TextAntialiasing
            | QtGui.QPainter.SmoothPixmapTransform
            | QtGui.QPainter.HighQualityAntialiasing,
            True,
        )
        painter.drawEllipse(self.rect)
        painter.restore()

class Group(QGraphicsItemGroup):
    def __init__(self):
        super(Group, self).__init__()
        self.setFlag(QGraphicsItemGroup.ItemIsMovable)

    def boundingRect(self):
        #if self.childItems():
        #    return self.childrenBoundingRect()
        return QtCore.QRectF(200,200,20,20)

    def paint(self,
              painter: QtGui.QPainter,
              option: QtWidgets.QStyleOptionGraphicsItem,
              widget: QtWidgets.QWidget = None):
        painter.setPen(QtGui.QPen(QtCore.Qt.NoPen))
        painter.setBrush(QtGui.QBrush(QColor(0, 0, 255, 127)))
        painter.drawRect(self.boundingRect())

class MyView(QGraphicsView):
    def __init__(self):
        super().__init__()
        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        self.setWindowTitle('Test')
        self.setSceneRect(0, 0, 450, 450)
        #add items
        nodeItem = Rectangle([20,200,20,20])
        self.scene.addItem(nodeItem)
        nodeItem2 = Rectangle([40,300,20,20])
        self.scene.addItem(nodeItem2)
        #rect_item = QtWidgets.QGraphicsRectItem(QtCore.QRectF(0, 0, 100, 100))
        #rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
        #self.scene.addItem(rect_item)
        #add buttons
        self.b1 = QPushButton("ungroup")
        self.b1.clicked.connect(self.ungroup)
        self.b1.setGeometry(QRect(10,10,100,100))
        self.b2 = QPushButton("group")
        self.b2.clicked.connect(self.cgroup)
        self.b2.setGeometry(QRect(150,10,100,100))

        self.b3 = QPushButton("print obj")
        self.b3.clicked.connect(self.printobj)
        self.b3.setGeometry(QRect(250,10,100,100))

        self.scene.addWidget(self.b1)
        self.scene.addWidget(self.b2)
        self.scene.addWidget(self.b3)

    def printobj(self):
        for item in self.scene.items():
            print(item)

    def ungroup(self):
        for item in self.group.childItems():
            self.group.removeFromGroup(item)
            print(f'item {item} removed from group {self.group}')
        self.scene.destroyItemGroup(self.group)
        #self.scene.update()


    def cgroup(self):
        self.group = Group()
        self.scene.addItem(self.group)
        for n in self.scene.items():
            if isinstance(n,Rectangle):
                print('n')
                n.setParentItem(self.group)
        #self.scene.update()


if __name__ == '__main__':
    app = QApplication([])
    f = MyView()
    f.show()
    sys.exit(app.exec_())


 

问题在于,循环遍历项目会为它们创建一个 python 包装器,并且作为本地引用,当函数 returns.

时,它会自动被垃圾回收

python 包装器 尝试 检查图形项目的所有权,但从组中删除项目不会自动将所有权重新转移到场景,因为它会通过 addItem() 发生。结果是删除了对项目的本地引用,垃圾回收调用了项目的析构函数,并将它们从场景中删除。

保持对已删除项目的持久引用可以解决问题:

    def ungroup(self):
        self.items = []
        for item in self.group.childItems():
            self.group.removeFromGroup(item)
            self.items.append(item)

但考虑到如果列表被删除或清除,这些项目也将被删除,除非它们被重新设置父级。

或者,您可以明确地从场景中删除项目并再次添加它们(您不应将项目添加到同一场景):

    def ungroup(self):
        for item in self.group.childItems():
            self.group.removeFromGroup(item)
            self.scene.removeItem(item)
            self.scene.addItem(item)

但是,既然你无论如何都要销毁该组,解决方案是只删除(并正确地删除)其他项目,然后销毁该组,这样剩余物品将被转回现场:

    def ungroup(self):
        for item in self.group.childItems():
            if not isinstance(item, Rectangle):
                self.group.removeFromGroup(item)
                del item
        self.scene.destroyItemGroup(self.group)

注意事项: 1.如果要绘制椭圆,直接使用QGraphicsEllipseItem即可,无需重写paint; 2. 当项目是组的一部分时,ItemIsSelectable 标志会导致递归移动;