【问题标题】:What are some best practices for debugging Qt signals and slots?调试 Qt 信号和插槽的最佳实践是什么?
【发布时间】:2012-08-09 22:23:33
【问题描述】:

调试信号和槽可能很困难,因为调试器在发出信号时不会跳转到信号的槽。调试 Qt 信号和槽的最佳实践有哪些?

特别是

  1. 如何确保连接设置成功?
  2. 什么时候应该使用信号和槽,什么时候应该避免它们?
  3. 根据您的经验,最有效的调试技术是什么?

【问题讨论】:

    标签: qt debugging signals-slots connection


    【解决方案1】:

    除了已经说过的,这里还有一些额外的技巧。

    如果您使用 QTest 进行单元测试,那么您可以将 -vs 参数传递给可执行文件,所有信号都将显示在控制台中。

    我查看了 QTest 的工作原理,它注册了在使用 QSignalDumper 类执行信号和槽时触发的回调。但是,此 API 不会导出,并且可能随时中断。以下是我如何使用 GCC 使用 Qt 5.10 连接 Linux 上的所有信号和插槽。

    // QSignalSpyCallbackSet is defined in qt5/qtbase/src/corelib/kernel/qobject_p.h
    
    struct QSignalSpyCallbackSet
    {
        typedef void (*BeginCallback)(QObject *caller, int signal_or_method_index, void **argv);
        typedef void (*EndCallback)(QObject *caller, int signal_or_method_index);
        BeginCallback signal_begin_callback,
                        slot_begin_callback;
        EndCallback signal_end_callback,
                    slot_end_callback;
    };
    typedef void (*register_spy_callbacks)(const QSignalSpyCallbackSet &callback_set);
    
    static void showObject(QObject *caller, int signal_index, const QString &msg)
    {
       const QMetaObject *metaObject = caller->metaObject();
       QMetaMethod member = metaObject->method(signal_index);
       qDebug() << msg << metaObject->className() << qPrintable(member.name());
    }
    
    static void onSignalBegin(QObject *caller, int signal_index, void **argv)
    {
       showObject(caller, signal_index, "onSignalBegin");
    }
    
    static void onSlotBegin(QObject *caller, int signal_index, void **argv)
    {
       showObject(caller, signal_index, "onSlotBegin");
    }
    
    int main(int argc, char *argv[])
    {
       static QSignalSpyCallbackSet set = { onSignalBegin, onSlotBegin, 0, 0 };
       QLibrary qtcore("libQt5Core");
       register_spy_callbacks reg = (register_spy_callbacks)qtcore.resolve("_Z32qt_register_signal_spy_callbacksRK21QSignalSpyCallbackSet");
    
       if (reg) {
          reg(set);
       }
       ...
    }
    

    我认为 Qt 应该公开该 API,因为我们可以将它用于除调试之外的许多事情,例如监控在插槽中花费的时间、获取统计信息等。

    【讨论】:

      【解决方案2】:

      关于 #1,我将添加另一条我在上面或参考的博客文章中没有看到的信息。

      来自QObject::connect()上的文档:

      从发送者对象中的信号到接收者对象中的方法创建一个给定类型的连接。如果连接成功,则返回 true;否则返回 false。

      我更喜欢断言我的连接的返回值以确保连接成功,尤其是因为并非所有 Qt 程序都会有控制台输出。这也导致代码更易于维护,因为它会捕获以后对信号或插槽所做的更改,并强制进行更改的程序员也更新连接。

      【讨论】:

      • 我意识到这是一个古老的帖子,但以防它最近被使用。文档实际上指出 connect 返回 "handle" 。我相信检查回报的有效性——不管它是什么——更通用。
      • @JanHus 我引用的文档是 Qt 4.8 版本。在 Qt5 中返回了一个句柄,但更重要的是添加了一个新的基于仿函数的连接,它允许编译时检查,这更安全(参见doc.qt.io/qt-5/signalsandslots-syntaxes.html)。
      • 我一直在通过移动鼠标来使用 QtDesigner GUI ("edit slots/signals;")。它是有限的,并且“构建”了一个时髦的 GUI“字符串”。由于没有代码可以检查它可能还是旧的“连接”形式。不错的功能,但对用户不太友好。
      【解决方案3】:

      不久前有一篇名为20 ways to debug Qt signals and slots的博客文章
      它解决了我认为您的问题中的 #1 和 #3。

      对于#2,我不认为使用或不使用信号/插槽真的有一个硬性和快速的理由,因为它们是 GUI 框架的一个非常核心的概念。信号是将一个组件的知识与另一个组件解耦的完美方式,允许您设计可重用的小部件,这些小部件只需声明状态更改或通知。通过发出主线程可以看到的信号,这也是从非 GUI 线程循环传达 GUI 更改的一种非常好的方法。

      有时,您真正想要的不是信号/槽,而是使用事件,例如当父小部件应成为许多子小部件的事件过滤器时。孩子们仍然不需要知道父母,父母会得到更直接的事件而不是信号连接。

      在事件的同一主题上,有时您真正想要的是从孩子 -> 父母 -> 祖父母 -> 等处冒泡事件。信号在这里就没那么有意义了,因为它们并不意味着确定提议的事件是否应该导致动作的方法(显然可以这样使用它们)。事件允许您检查当前状态,决定此小部件是否应该执行任何操作,或者以其他方式将它们向上冒泡以供其他人检查。

      关于The Difference Between Signals/Slots and Events 有一个非常棒的答案。这是一个很好的sn-p:

      • 你“处理”事件
      • 您会“收到有关”信号发射的通知

      我喜欢这句话的地方在于它描述了不同的需求案例。如果您需要处理小部件中的操作,那么您可能需要一个事件。如果您想在发生的事情时收到通知,那么您可能需要一个信号。

      【讨论】:

      • 您提供的链接在实际调试技术方面相当平庸。要点基本上是:“避免编写错误,这里有 20 件事可能出错”。没有提到实际调试问题的工具或技术。当你得到一个崩溃转储时,你会遇到一个非常深的调用堆栈,大量的qt_static_metacall 调用,没有简单的方法来破译发出的信号,而且根本无法知道在哪里设置了任何特定的信号槽连接.
      【解决方案4】:

      如何确保连接设置成功?

      对于每个失败的连接,您都会在应用程序的控制台输出中看到警告。

      什么时候应该使用信号和槽,什么时候应该避免它们?

      在我看来,只要您想在类设计中保持关注点分离,就可以使用它们。您的班级可以发出一个信号,该信号可能会或可能不会被您的班级完全未知的另一个(或多个班级)回答。这样可以降低耦合度。

      根据您的经验,最有效的调试技术是什么?

      除了这篇博文中所说的之外,我真的无法再添加任何内容了。 20 ways to debug Qt signals and slots

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-28
        • 2010-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多