QGraphicsScene changes objects when selected

I have QGraphicsSceneone containing a few simple objects (in this simplified example of circles) that I want to change to other objects (here squares) when I select. More specifically, I would like to have parent objects that are not drawn by myself, they are drawn by their child objects and under various circumstances, but, in particular, when the parent objects are selected, I would like the set of child objects to change. This is a good conceptual framework for the general application I'm working on.

So, I implemented this in PySide, and I thought it was working fine: the circles are typed into squares when you click on them.

While I do not use the choice RubberBandDragin the view. This leads to instant segfault when the rubber band selection reaches the parent object and the selection changes. Presumably, this is because the selection of the rubber band in QT somehow holds the pointer to the child element, which disappears before the completion of the rubber band selection action.

The simplified code is below - check it by first clicking on the object (it changes beautifully), then dragging the object - 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_()

So the questions are:

I just made a mistake that can be directly fixed?

Or is it not allowed to delete objects from the scene when processing an event ItemSelectedHasChanged?

? ? QGraphicsRectItem , , , . , , , , .

. :

, , - . :

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

    ...

. QGraphicsScene.selectionChanged, this.

+4
1

. 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- -Src-4.8.6/SRC/GUI/GraphicsView/qgraphicsscene.cpp: 2381

void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode,
                                      const QTransform &deviceTransform)
{
...
    // Set all items in path to selected.
    foreach (QGraphicsItem *item, items(path, mode, Qt::DescendingOrder, deviceTransform)) {
        if (item->flags() & QGraphicsItem::ItemIsSelectable) { // item is invalid here
            if (!item->isSelected()) 
                changed = true;
            unselectItems.remove(item);
            item->setSelected(true);
        }
    }

items() . -, . item->flags() .

QGraphicsScene::selectionChanged. .

, Qt itemChange


prepareGeometryChange().

boundingRect. 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
0

Source: https://habr.com/ru/post/1684744/


All Articles