选定时更改对象

2024-09-29 00:15:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个^{}包含一些简单的对象(在这个简化的示例圆中),当选中这些对象时,我想将它们更改为其他对象(这里是正方形)。更具体地说,我希望父对象不自己绘制,它们是由子对象绘制的,在各种情况下,但特别是当父对象被选中时,我希望子对象集发生变化。对于我正在开发的整个应用程序来说,这是一个很好的概念框架。在

所以我在PySide中实现了这一点,我认为它工作得很好:当你点击圆圈时,它们会很好地变成正方形。在

直到我在视图中使用RubberBandDrag选择。当橡皮筋选择到达父对象并且选择发生更改时,这将导致即时分段错误。这可能是因为QT中的橡皮筋选择在某种程度上保持了指向子项的指针,该子项在橡皮筋选择操作完成之前就消失了。在

下面的简化代码-首先单击对象(它会很好地更改),然后在对象上拖动-segfault:

from PySide import QtCore,QtGui

class SceneObject(QtGui.QGraphicsItem):
    def __init__(self, scene):
        QtGui.QGraphicsItem.__init__(self, scene = scene)
        self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QtGui.QGraphicsItem.ItemHasNoContents, True)
        self.updateContents()

    def updateContents(self):
        self.prepareGeometryChange()
        for c in self.childItems():
            self.scene().removeItem(c)

        if self.isSelected():
            shape_item = QtGui.QGraphicsRectItem()
        else:
            shape_item = QtGui.QGraphicsEllipseItem()
        shape_item.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, False)
        shape_item.setFlag(QtGui.QGraphicsItem.ItemStacksBehindParent,True)
        shape_item.setPen(QtGui.QPen("green"))
        shape_item.setRect(QtCore.QRectF(0,0,10,10))
        shape_item.setParentItem(self)

    def itemChange(self, change, value):
        if self.scene() != None:
            if change == QtGui.QGraphicsItem.ItemSelectedHasChanged:
                self.updateContents()
                return
        return super(SceneObject,self).itemChange(change, value)

    def boundingRect(self):
        return self.childrenBoundingRect()


class Visualiser(QtGui.QMainWindow):

    def __init__(self):
        super(Visualiser,self).__init__()

        self.viewer = QtGui.QGraphicsView(self)
        self.viewer.setDragMode(QtGui.QGraphicsView.RubberBandDrag)
        self.setCentralWidget(self.viewer)
        self.viewer.setScene(QtGui.QGraphicsScene())

        parent_item = SceneObject(self.viewer.scene())
        parent_item.setPos(50,50)



app = QtGui.QApplication([])
mainwindow = Visualiser()
mainwindow.show()
app.exec_()

所以问题:

我刚刚犯了一个可以直接纠正的错误吗?在

或者在处理ItemSelectedHasChanged事件时不允许从场景中移除对象?在

有没有一个方便的解决办法?或者什么是一个好的替代方法?我可以将QGraphicsRectItem替换为一个自定义项,该项可以绘制为正方形或圆形,但这并不能方便地涵盖我的所有用例。我知道我可以做到这一点,但肯定不会那么简单。在

编辑-解决方法:

可以通过将要删除的对象保留一段时间来防止这种失败。可以这样做:

^{pr2}$

然而,这是一个丑陋的代码,增加了内存的使用,却没有真正的好处。相反,我转而使用QGraphicsScene.selectionChanged信号,正如this答案中建议的那样。在


Tags: 对象selfinitdef绘制sceneitemviewer
1条回答
网友
1楼 · 发布于 2024-09-29 00:15:04

我已经调试过了。转载于Lunix

1  qFatal(const char *, ...) *plt                                                                                                  0x7f05d4e81c40 
2  qt_assert                                                                                     qglobal.cpp                  2054 0x7f05d4ea197e 
3  QScopedPointer<QGraphicsItemPrivate, QScopedPointerDeleter<QGraphicsItemPrivate>>::operator-> qscopedpointer.h             112  0x7f05d2c767ec 
4  QGraphicsItem::flags                                                                          qgraphicsitem.cpp            1799 0x7f05d2c573b8 
5  QGraphicsScene::setSelectionArea                                                              qgraphicsscene.cpp           2381 0x7f05d2c94893 
6  QGraphicsView::mouseMoveEvent                                                                 qgraphicsview.cpp            3257 0x7f05d2cca553 
7  QGraphicsViewWrapper::mouseMoveEvent                                                          qgraphicsview_wrapper.cpp    1023 0x7f05d362be83 
8  QWidget::event                                                                                qwidget.cpp                  8374 0x7f05d2570371 

qt-everywhere-opensource-src-4.8.6/src/gui/graphicsview/qgraphicscene.cpp:2381在

^{pr2}$

他们正在使用items()函数查找橡皮筋选择下的项目列表。如果一个指针在处理某个项目时变为无效,则删除该指针。{next}导致调用^崩溃。在

作为替代,您可以使用QGraphicsScene::selectionChanged信号。每次选择更改仅发射一次。在

看来Qt并不认为itemChange会有一些重大变化


在这后面是您在prepareGeometryChange()调用时的常见错误。在

它被设计为在更改boundingRect之前调用。Bounding rect应该是调用prepareGeometryChange时的旧矩形,然后是新的。在

所以这可能发生:

updateContents中:

self.prepareGeometryChange(); # calls boundingRect. old value returned
...
shape_item.setParentItem(self); # could call the boundingRect. but still old value returned!

添加子元素后,它再次调用boundingRect,但值意外不同。在

作为解决方案,您可以添加一个变量

def updateContents(self):
    for c in self.childItems():
        self.scene().removeItem(c)

    if self.isSelected():
        shape_item = QtGui.QGraphicsRectItem()
    else:
        shape_item = QtGui.QGraphicsEllipseItem()
    shape_item.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, False)

    shape_item.setFlag(QtGui.QGraphicsItem.ItemStacksBehindParent,True)
    shape_item.setPen(QtGui.QPen("green"))
    shape_item.setRect(QtCore.QRectF(0,0,10,10))
    shape_item.setParentItem(self) 

    self.prepareGeometryChange();
    self._childRect = self.childrenBoundingRect()

def boundingRect(self):
    return self._childRect

相关问题 更多 >