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
标志会导致递归移动;
我遇到了 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
标志会导致递归移动;