【问题标题】:How can I be alerted when Qt signal/slot connection fails?Qt 信号/槽连接失败时如何提醒我?
【发布时间】:2014-06-10 18:44:14
【问题描述】:

当使用connect 从/到不存在的信号/插槽时,我们会浪费很多时间,因为 Qt 仅在控制台日志中的某个位置在运行时向我们发出警告。

除了演变到使用类型系统报告这些问题的 Qt5 以及从系统中所有 connect 调用的 changing code 之外,还有其他方法可以拥有 Qt 运行时例如当连接错误时抛出,或者干脆崩溃,或者大声提醒我

【问题讨论】:

  • 你还在使用 Qt 4 吗?你有 C++11 支持吗?因为,您可以在 Qt 5 中使用正确的 SIGNAL/SLOT 语法,这会产生运行时错误。还有,当你在控制台看到错误信息时,为什么会崩溃?
  • @LaszloPapp:我有一个充满connect 调用的“大型代码库”,但没有时间调整它们。控制台中的调试消息隐藏在所有其他输出之间。我正在寻找不需要更改代码的非常直观且具有破坏性的东西 - 只需更改编译器/运行时设置。
  • 您的意思是大型代码库,但使用 Qt 4,并且需要大量移植到其他地方的 Qt 5?因为如果您可以切换到 Qt 5,您将需要以任何一种方式更改连接线,那么在这种情况下为什么不将其更改为正确的呢?如果您仍在使用 Qt 4,我会听到您的声音。 :)
  • 一个好处:Qt Creator 有代码补全功能。

标签: c++ qt debugging qt5 signals-slots


【解决方案1】:

您可以在连接上使用包装器,当某些连接失败时停止程序:

inline void CHECKED_CONNECT( const QObject * sender, const char * signal,
             const QObject * receiver,  const char * method,
             Qt::ConnectionType type = Qt::AutoConnection )
{
  if(!QObject::connect(sender, signal, receiver, method, type))
   qt_assert_x(Q_FUNC_INFO, "CHECKED_CONNECT failed", __FILE__, __LINE__);
}

【讨论】:

  • 似乎 qt_assert_x 没有记录在案的公共 API,所以我认为使用 Q_ASSERT 或 Q_ASSERT_X 更安全,而且:Q_ASSERT_X 已经解决了您的 _ _ FILE _ _ 和 _ _ LINE _ 和noop 样板。这也可以避免额外的功能:#define Q_ASSERT_X(cond, where, what) ((!(cond)) ? qt_assert_x(where, what,__FILE__,__LINE__) : qt_noop())
【解决方案2】:

最简单的解决方案是:

bool ok = QObject::connect(sender, SIGNAL(mySignal()), receiver, SLOT(mySlot());
Q_ASSERT_X(ok, Q_FUNC_INFO, "connect mySignal to mySlot");

不要不要陷入“缩短”的诱惑。下面的变体是一个错误,在发布模式下变成了无操作:

Q_ASSERT_X(QObject::connect(sender, SIGNAL(mySignal()),
                            receiver, SLOT(mySlot()),
                            Q_FUNC_INFO, "connect mySignal to mySlot");

如果没有定义相应的调试宏,此表单将在发布模式下完全删除。

如果你想扔,那么你可以从这里开始:

try {
    if (!QObject::connect(sender, SIGNAL(mySignal()), receiver, SLOT(mySlot()))
        throw ...;
} catch ( .. )
    qDebug() << "Could not connect ...";
    qApp->exit(1);
}

您确实应该考虑使用 Qt 5 和 C++11 支持的新信号/槽语法,它会生成编译时警告。

这将导致类似:

connect(sender, &Sender::mySignal, mySlot);

由于相关代码的局部性,您甚至可以使用 lambda 使其简短易懂:

connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
    receiver->updateValue("senderValue", newValue);
} );

【讨论】:

  • 重要提示:对于 lambda 变体,只要 sender 存在,receiver 就必须保持活动状态。因此,明智的做法是将其包含在调用中:connect(sender, &amp;Sender::valueChanged, _receiver_, [=]()...)。我们忘记了这一点造成了很多问题。
【解决方案3】:

QObject::connect 返回QMetaObject::Connection 可以通过其bool 运算符进行测试:

如果连接有效,则返回 true。

另一种选择可能是“重新路由”并解析自动生成的调试消息以查找连接错误。

【讨论】:

  • Qt 对你来说很强大。但是,在我的问题的第二段中,我要求一种方法无需更改代码。导致 qt 大声崩溃的各种运行时参数。
  • 它简洁地回答了标题问题,很容易找到并将Qt5指定为标签。另外,它阐明了在其他答案中隐含使用的返回值的用法。我提供了当前文档的链接。这对其他人会有所帮助。
  • 另外,我的另一个想法是 potential solution 解决您的问题。
【解决方案4】:

当连接失败时,Qt 会发送一个 QWarning 消息。

您可以使用使用 qInstallMessageHandler 的帮助程序类来捕获此消息,并在开发模式下使您的应用程序因 QWarnings 而崩溃,或者解析警告并仅因连接错误而崩溃。
(见How to redirect qDebug, qWarning, qCritical etc output?

【讨论】:

    【解决方案5】:

    我的紧凑变体如下:

    // BoolVerifier.h
    #include <cassert>    
    
    class BoolVerifier
    {
    public:
        BoolVerifier() = default;
        inline BoolVerifier(bool b) { assert(b); (void)(b); }
        inline BoolVerifier& operator=(bool b) { assert(b); (void)(b); return *this; }
    };
    

    及用法:

    BoolVerifier b;
    b = connect(objectFrom, SIGNAL(mySignal1(int)), objectTo, SLOT(mySlot1(int)));
    b = connect(objectFrom, SIGNAL(mySignal2(int)), objectTo, SLOT(mySlot2(int)));
    ...
    

    【讨论】:

    • 构造函数中的 inline 关键字是多余的。但也许explicit 会很好,如果可能不那么符合人体工程学。很好地应用了 C++ 原则!
    • 我喜欢这种技术;它几乎没有侵入性并且可以正常工作,并且可以扩展到其他返回错误代码。但是,只有在每次出现 connect 语句时都更改它才能起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-15
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 2016-11-10
    相关资源
    最近更新 更多