gpt4 book ai didi

c++ - Qt widget析构函数通过connected signal间接调用widget方法,崩溃

转载 作者:搜寻专家 更新时间:2023-10-31 02:06:25 25 4
gpt4 key购买 nike

我制作了一个基于 QGraphicsView 的小部件 (QDataflowCanvas),我将信号 QGraphicsScene::selectionChanged() 连接到插槽 MainWindow::onSelectionChanged 我的主窗口:

void MainWindow::onSelectionChanged()
{
// canvas is ptr to QDataflowCanvas, subclass of QGraphicsView
auto selNodes = canvas->selectedNodes();
auto selConns = canvas->selectedConnections();
...
}

问题发生在我关闭 MainWindow 并且在 QGraphicsView 中选择了一些项目时。

我认为我不需要提供完整的代码(尽管可以找到 here ),因为我已经隔离了崩溃的原因。

这是将要发生的事情(按因果顺序):

  • 主窗口的析构函数被调用
  • 调用了QDataflowCanvas的析构函数
  • 调用了 QGraphicsView 的析构函数
  • 调用 QGraphicsScene 的析构函数,触发所有项目的删除(使用 clear())
  • 调用 QGraphicsItem 的析构函数
  • 将触发 selectionChange 事件
  • MainWindow::onSelectionChanged 插槽被调用
  • 方法QDataflowCanvas::selectedNodes()被调用,但对象被销毁
  • 崩溃!

从崩溃的堆栈跟踪中可以更详细地看到:

stack trace from Qt Creator

我找到了这个解决方法:如果我断开 MainWindow::~MainWindow 中的信号,它当然不会崩溃:

MainWindow::~MainWindow()
{
QObject::disconnect(canvas->scene(), &QGraphicsScene::selectionChanged, this, &MainWindow::onSelectionChanged);
}

但这似乎是一种非常不典型的做法:我从来没有发现自己必须手动严格的信号槽连接,否则程序会崩溃。

必须有更合适的解决方案。

最佳答案

首先,你的项目名称是错误的。采用 Q 前缀的命名空间。在任何使用 Qt 的项目中,您都不应该有任何以 Q 为前缀的类。例如,您应该将项目重命名为 DataflowCanvas

三种解决方案:

  1. 按值保存所有子项,根据它们的依赖项对子项进行排序。从 QDataFlowCanvas 调用的 QWidgetPrivate::deleteChildren 将是空操作,或者至少不会触及您关心的对象。

  2. 连接到 MainWindow::onSelectionChanged 插槽时使用旧的 connect 语法。请注意,当您的插槽被调用时,主窗口对象是 QWidget 动态类型,而不是 MainWindow 类型。使用旧连接语法建立的连接尊重对象的动态类型,并且与给定类的插槽建立的连接将保证该对象动态地属于该类,即在运行时。

  3. 在析构函数中清除选择 - 然后将不会处理任何进一步的选择更改。

第一个解决方案使一切都变得明确,这就是我要使用的解决方案:

class DataFlowCanvas : public QGraphicsView {
...
private:
QDataflowModel *model_;
QDataflowTextCompletion *completion_;
QSet<QDataflowNode*> ownedNodes_;
QSet<QDataflowConnection*> ownedConnections_;
QMap<QDataflowModelNode*, QDataflowNode*> nodes_;
QMap<QDataflowModelConnection*, QDataflowConnection*> connections_;
bool showIOletsTooltips_;
bool showObjectHoverFeedback_;
bool showConnectionHoverFeedback_;
qreal gridSize_;
bool drawGrid_;
QGraphicsSecene scene_;
};

场景在任何其他字段之前被破坏。问题解决了。你也应该按值(value)持有其他一切。例如。 completion_ 等。指针间接寻址没有用。

第二个解决方案突出了一个不幸的 Qt 错误。也就是说,在下面的代码中,旧的连接语法永远不会调用 Derived2::aSlot2,因为在调用插槽时,对象不属于 Derived2 再输入:

#include <QtCore>

int ctr1, ctr2;

struct Derived1 : QObject {
Q_SLOT void aSlot1() { ctr1++; qDebug() << __FUNCTION__; }
Q_SIGNAL void aSignal();
~Derived1() { Q_EMIT aSignal(); }
Q_OBJECT
};

struct Derived2 : Derived1 {
Q_SLOT void aSlot2() { ctr2++; qDebug() << __FUNCTION__ << qobject_cast<Derived2*>(this); }
Q_OBJECT
};

int main() {
{
Derived2 d;
QObject::connect(&d, &Derived2::aSignal, &d, &Derived2::aSlot2);
QObject::connect(&d, SIGNAL(aSignal()), &d, SLOT(aSlot2()));
QObject::connect(&d, SIGNAL(aSignal()), &d, SLOT(aSlot1()));
}
Q_ASSERT(ctr1 == 1);
Q_ASSERT(ctr2 == 1);
}
#include "main.moc"

输出清楚地说明了问题:

aSlot2 QObject(0x0)   <-- aSlot2 called but `this` is of `Derived1*` type!
aSlot1

关于c++ - Qt widget析构函数通过connected signal间接调用widget方法,崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50318413/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com