【问题标题】:Deleting QGraphicsItem with QGraphicsEffect leads to segfault使用 QGraphicsEffect 删除 QGraphicsItem 会导致段错误
【发布时间】:2017-06-11 06:05:38
【问题描述】:

以下程序绘制红色背景。按下鼠标左键会在其上绘制一个白色矩形。该矩形有一个子矩形和一个QGraphicsDropShadowEffect。 (QGraphicsOpacityEffectQGraphicsColorizeEffect 也会导致问题,但频率较低。)

按鼠标右键删除白色矩形。

有时在删除矩形时会导致分段错误。

如果未应用 QGraphicsDropShadowEffect,则不会发生这种情况。当MyRect 中没有子项或背景被省略时,也不会发生这种情况。

(在搜索此问题时,我发现了一些提示,表明此类段错误可能与更改项目的 boundingRect() 而不调用 prepareGeometryChange() 有关。)

我在这里真的很茫然。这是一个更大项目的一部分,我将其归结为以下示例:

Main.cc:

#include<QApplication>
#include<QGraphicsView>
#include<QGraphicsScene>
#include<QGraphicsSceneMouseEvent>
#include<QGraphicsRectItem>
#include<QGraphicsDropShadowEffect>
#include<QScreen>

class MyRect: public QGraphicsRectItem
{
    public:
    MyRect(QGraphicsItem* parent = nullptr):
        QGraphicsRectItem{QRectF{0.0f, 0.0f, 100.0f, 100.0f}, parent}
    {
        setPen(QPen{Qt::white});
        setBrush(QBrush{Qt::white, Qt::SolidPattern});
        my_child_=new QGraphicsRectItem{this};
    }

    private:
    QGraphicsRectItem* my_child_=nullptr;
};

class MyScene: public QGraphicsScene
{
    public:
    MyScene()
    {
        background_=new QGraphicsRectItem{0.0f, 0.0f, 500.0f, 500.0f};
        background_->setPen(QPen{Qt::white});
        background_->setBrush(QBrush{Qt::red, Qt::SolidPattern});
        addItem(background_);
    }

    void mouseReleaseEvent(QGraphicsSceneMouseEvent* me) override
    {
        if (me->button()==Qt::LeftButton)
        {
            if (my_rect_==nullptr)
            {
                my_rect_=new MyRect{};
                shadow_=new QGraphicsDropShadowEffect{};
                shadow_->setBlurRadius(15.0f);
                my_rect_->setGraphicsEffect(shadow_);
                addItem(my_rect_);
                my_rect_->setPos(me->scenePos());
            }
        }
        else if (me->button()==Qt::RightButton)
        {
            if (my_rect_!=nullptr)
            {
                removeItem(my_rect_);
                my_rect_->setGraphicsEffect(0);
                shadow_=nullptr;
                delete my_rect_;
                my_rect_=nullptr;
            }
        }
        else
        {
            QGraphicsScene::mouseReleaseEvent(me);
        }
    }

    private:
    QGraphicsRectItem* background_=nullptr;
    MyRect* my_rect_=nullptr;
    QGraphicsDropShadowEffect* shadow_=nullptr;
};

int
main(int argc, char** argv)
{
    QApplication qapp{argc, argv};

    QGraphicsView view;
    MyScene scene;

    QRect rect=QGuiApplication::primaryScreen()->geometry();
    scene.setSceneRect(0.0f, 0.0f, rect.width(), rect.height());
    view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view.setFrameShape(QFrame::NoFrame);
    view.setBackgroundBrush(QBrush(Qt::black, Qt::SolidPattern));
    view.setScene(&scene);
    view.showFullScreen();

    return qapp.exec();
}

编译器调用:

g++ --std=c++14 -fPIC -Wall -Woverloaded-virtual -Werror -pedantic -g -O0 -fPIC -I/usr/include/x86_64-linux-gnu/qt5/QtGui -I/usr/include/x86_64-linux-gnu/qt5/QtWidgets -I/usr/include/x86_64-linux-gnu/qt5/QtCore -I/usr/include/x86_64-linux-gnu/qt5 -o test Main.cc -lQt5Gui -lQt5Core -lQt5Widgets

回溯:

#0  0x0000555555a9a840 in ?? ()
#1  0x00007ffff71e017c in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#2  0x00007ffff71e0a3a in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#3  0x00007ffff720289a in QGraphicsView::paintEvent(QPaintEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#4  0x00007ffff6f10278 in QWidget::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#5  0x00007ffff6ff89fe in QFrame::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#6  0x00007ffff72013ab in QGraphicsView::viewportEvent(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#7  0x00007ffff7650701 in QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#8  0x00007ffff6ec8b65 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#9  0x00007ffff6ed0341 in QApplication::notify(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#10 0x00007ffff76509a0 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#11 0x00007ffff6f08fda in QWidgetPrivate::sendPaintEvent(QRegion const&) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#12 0x00007ffff6f09646 in QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) ()
   from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#13 0x00007ffff6ed8f1e in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#14 0x00007ffff6ed9147 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#15 0x00007ffff6ef7f8f in QWidgetPrivate::syncBackingStore() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#16 0x00007ffff6f10348 in QWidget::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#17 0x00007ffff6ff89fe in QFrame::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#18 0x00007ffff7081de3 in QAbstractScrollArea::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#19 0x00007ffff6ec8b8c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#20 0x00007ffff6ed0341 in QApplication::notify(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#21 0x00007ffff76509a0 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#22 0x00007ffff765312d in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#23 0x00007ffff71d2a22 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#24 0x00007ffff71d8299 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#25 0x00007ffff767d459 in QObject::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#26 0x00007ffff71e4e6b in QGraphicsScene::event(QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#27 0x00007ffff6ec8b8c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#28 0x00007ffff6ed0341 in QApplication::notify(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#29 0x00007ffff76509a0 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#30 0x00007ffff765312d in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#31 0x00007ffff76a4c03 in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#32 0x00007ffff446f7f7 in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#33 0x00007ffff446fa60 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#34 0x00007ffff446fb0c in g_main_context_iteration () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#35 0x00007ffff76a500f in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#36 0x00007ffff764e98a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#37 0x00007ffff76570fc in QCoreApplication::exec() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#38 0x000055555555768b in main (argc=1, argv=0x7fffffffe5b8) at Main.cc:89

编辑:我重写了代码示例以获得更好的可读性。

【问题讨论】:

  • 两个问题:(1)你为什么在MyRect构造函数中做new QGraphicsRectItem{this}?由于 Qt 的对象层次结构,我不认为这是内存泄漏,但它似乎没有做任何事情。 (2) 没有线程,对吧?更新图形对象必须从主线程完成。
  • 当 Qt 仍然有计划在已删除对象上执行的事件时,有时会发生类似的事情 - 由于某种原因,它的删除并不总是刷新该事件队列。作为一种解决方法,您可以尝试而不是删除 do global_rect-&gt;hide(); global_rect-&gt;deleteLater(); global_rect = nullptr; 这将让 Qt 的事件循环在它认为合适时删除对象本身。
  • @bnaecker @(1):MyRect 需要有孩子才能触发问题。这是一个例子,所以我只是保持最小。它没有内存泄漏,因为它是父级的。 @(2) 有qapp.exec(),所以主循环正在运行。
  • @Dmitry 不幸的是,QGraphicsRectItem 只是一个QGraphicsItem 而没有QObject,所以没有deleteLater()
  • @Dmitry 我刚刚意识到,QGraphicsEffectQObject,问题可能与此有关吗?但是我该如何处理呢?调用QGraphicsItem::setGraphicsEffect(effect) 使该项目获得效果的所有权,QGraphicsItem::setGraphicsEffect(0) 删除任何现有效果。我无法将效果与项目分离以调用 deleteLater() 效果。

标签: c++ qt qgraphicsitem


【解决方案1】:

因为我接受过 on the Qt forums 的教育,这不是预期的行为,可能是 Qt 中的一个错误。

最好的解决方法是调用QGraphicsItem::prepareGeometryChange() on my_rect_从场景中移除它之前。这可以通过暴露受保护的prepareGeometryChange() 或通过在~MyRect() 中调用它然后简单地删除my_rect_ 而不调用QGraphicsScene::removeItem() 来完成。 ~QGraphicsItem(),当然是在~MyRect()之后调用的,会自动从场景中移除该项目。

【讨论】:

  • 这里同样的问题,在QGraphicsItem::itemChange() 函数的QGraphicsItem::ItemSceneChange 案例中调用prepareGeometryChange() 有帮助。谢谢。
  • 顺便说一句:我去年报道过这个,bugreports.qt.io/browse/QTBUG-58501
猜你喜欢
  • 2020-03-15
  • 2013-12-12
  • 2019-08-31
  • 1970-01-01
  • 1970-01-01
  • 2021-07-25
  • 2021-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多