QGroundControl Source Code Learning Series - 2

QGroundControl Source Code Learning Series - 2

AppMessages

AppMessages 类主要用来处理程序运行过程中的消息及日志记录。

在使用 Qt 进行开发时,难以避免会使用 QDebug (QInfo | QWarning | QCritical) 等以及相应的宏(qDebug 等)来输出变量的值或特定消息。一般使用 QtCreator 开发 Qt 程序时,qDebug 会将调试信息输出到 QtCreator 的控制台中,我们在开发时用起来非常方便。但是一般将程序发布后,往往看不到输出的信息了,因此我们需要将输出信息重定向到将界面或者文件中以便查看。毫无疑问,AppMessages 就是实现这样的功能的。

AppMessages 对外的接口只有两个,并且这两个方法都是静态的方法:

class AppMessages
{
public:
    static void installHandler();
    static AppLogModel* getModel();
};

在我看来,AppMessage 这个类更多的是充当一个 namespace 的功能。installHandler 方法中会调用 Qt 提供的 qInstallMessageHandler 注册自定义的消息处理器。而 getModel 方法则提供了外部访问 AppLogModel 实例的途径。

QGroundControl 中定义的 msgHandler :

static void msgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    const char symbols[] = { 'D', 'E', '!', 'X', 'I' };
    QString output = QString("[%1] at %2:%3 - \"%4\"").arg(symbols[type]).arg(context.file).arg(context.line).arg(msg);

    // Avoid recursion
    if (!QString(context.category).startsWith("qt.quick")) {
        debug_model->log(output);
    }

    if (old_handler != nullptr) {
        old_handler(type, context, msg);
    }
    if( type == QtFatalMsg ) abort();
}

它的功能有:1. 将来自系统的消息按照消息类型添加不同的前缀;2. 将 qtquick 组件中的消息转发到原消息处理器中,而其他的消息则转发到 AppLogModel。

注意前面的消息处理器函数,它被 static 所修饰(C 语言中的 static function 的作用域仅在其所在的文件中),因此在该函数中用到的所有变量都使用了 static 进行修饰:

Q_GLOBAL_STATIC(AppLogModel, debug_model)

static QtMessageHandler old_handler;

Q_GLOBAL_STATIC 的宏定义如下:

#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS)                         \
    namespace { namespace Q_QGS_ ## NAME {                                  \
        typedef TYPE Type;                                                  \
        QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
        Q_GLOBAL_STATIC_INTERNAL(ARGS)                                      \
    } }                                                                     \
    static QGlobalStatic<TYPE,                                              \
                         Q_QGS_ ## NAME::innerFunction,                     \
                         Q_QGS_ ## NAME::guard> NAME;

#define Q_GLOBAL_STATIC(TYPE, NAME)                                         \
    Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())

这两个宏可以用来创建全局唯一的静态对象。在执行 Q_GLOBAL_STATIC(AppLogModel, debug_model) 之后,就创建了一个全局唯一的 AppLogModel 对象的实例 debug_model。debug_model 可以像指针一样使用。(有点像 Qt 中的智能指针,不过这里声明的是静态的全局对象)

QGlobalStatic 是一个模板结构体:

template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
struct QGlobalStatic
{
    typedef T Type;
    ...
}

在 c++ 中,struct 中也可以定义方法,与 class 非常类似。
struct 和 class 关键字的差别在于,struct 声明的结构体所有成员变量和方法都是 public 的,而 class 可以自定义访问控制权限(默认 private)。

当 QGroundControl 完成了自定义消息处理器的定义后,所有的消息都会通过 emitLog 信号,发送到 AppLogModel::threadsafeLog 方法中进行进一步处理。当一切正常运行的时候,所有的消息最终都会通过 textstream 写入到文件中:

QTextStream out(&_logFile);
out << message << "\n";
_logFile.flush();

其他

  1. 禁用 Qt 程序中的调试信息,在项目中添加 CONFIG += QT_NO_DEBUG_OUTPUT 即可。
  2. 在大型应用中,日志功能是一个非常重要的功能。平常我们使用 Qt 开发应用程序时,最常用的就是包含 QDebug 头文件再用 qDebug 等宏输出到 qtcreator 的控制台中观察。但大型应用中每一个模块都需要有独立的日志对象用于记录模块自身的日志,以便过滤和检查日志,为此需要用到 QLogginCategory 对日志输出进行分类。

参考

  1. https://www.tutorialspoint.com/static-functions-in-c

A static function in C is a function that has a scope that is limited to its object file. This means that the static function is only visible in its object file.

  1. https://doc.qt.io/qt-5/qglobalstatic.html
posted @ 2019-10-16 16:23  brifuture  阅读(287)  评论(1编辑  收藏  举报