加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

python – 选择时更改对象的QGraphicsScene

发布时间:2020-12-20 13:19:32 所属栏目:Python 来源:网络整理
导读:我有一个 QGraphicsScene 包含一些简单的对象(在这个简化的例子中为圆圈),我想在选中时将其更改为其他对象(此处为正方形).更具体地说,我希望父对象不会自己绘制,它们是由子对象绘制的,并且在各种情况下,但特别是在选择父对象时,我希望子对象集合为更改.这是
我有一个 QGraphicsScene包含一些简单的对象(在这个简化的例子中为圆圈),我想在选中时将其更改为其他对象(此处为正方形).更具体地说,我希望父对象不会自己绘制,它们是由子对象绘制的,并且在各种情况下,但特别是在选择父对象时,我希望子对象集合为更改.这是我正在开发的整个应用程序的一个很好的概念框架.

所以我在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,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,可以将其绘制为方形或圆形,但不能方便地覆盖我的所有用例.我可以看到我可以做到这一点,但肯定不会那么简单.

编辑 – 解决方法:

通过暂时保留即将删除的对象可以防止这种失败.这可以通过以下方式完成:

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

    ...

但是,这是丑陋的代码,增加了内存使用量,没有真正的好处.相反,我已经开始使用0700答案中建议的QGraphicsScene.selectionChanged信号.

解决方法

我调试了它.在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之前调用.当prepareGeometryChange被调用时,绑定的rect应该是旧的,并且紧接着是新的.

所以这可能发生:

在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,10))
    shape_item.setParentItem(self) 

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

def boundingRect(self):
    return self._childRect

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读