main.cc文件分析

本节是QGC源码分析的第一节,从main.cc开始。main.cc文件中包含了main函数,是程序的入口。

QGC源码剖析-main.cc

代码分析

main.cc文件中有两个函数,一个是WindowsCrtReportHook钩子,一个是main主函数。

WindowsCrtReportHook钩子

/*main.cc*/
/// @brief CRT Report Hook installed using _CrtSetReportHook. We install this hook when
/// we don't want asserts to pop a dialog on windows.
/// CRT报告挂钩运用 _CrtSetReportHook装置。不让断语在 Windows上弹出对话框。
int WindowsCrtReportHook(int reportType, char* message, int* returnValue)
{
    Q_UNUSED(reportType);
    std::cerr << message << std::endl;  // Output message to stderr
    *returnValue = 0;                   // Don't break into debugger
    return true;                        // We handled this fully ourselves
}

main主函数

主函数流程图如下:

graph TD;
    A[Start] --> B{Is guard running?};
    B -- Yes --> C[Show error message and exit];
    B -- No --> D[Initialize timer];
    D --> E[Install message handler];
    E --> F[Set OpenGL buglist];
    F --> G[Check command line arguments];
    G -- UseOpenGLES --> H[Set attribute to use OpenGLES];
    G -- UseSoftwareOpenGL --> I[Set attribute to use software OpenGL];
    H --> J[Register QSerialPort::SerialPortError];
    J --> K[Register QAbstractSocket::SocketError];
    K --> L[Register QGCSerialPortInfo];
    L --> M[Import QGeoServiceProviderFactoryQGC];
    M --> N[Set attribute to use high DPI pixmaps];
    N --> O[Create QGCApplication];
    O -- Error state --> P[Execute error state and exit];
    O -- No error state --> Q[Register problematic type];
    Q --> R[Initialize common QML objects];
    R --> S[Initialize cache system];
    S --> T[Initialize for normal app boot];
    T -- Initialization failed --> U[Exit with error code];
    T -- Initialization succeeded --> V[Execute application];
    V --> W[Shutdown all QML];
    W --> X[Delete application];
    X --> Y[Shutdown cache system];
    Y --> Z[End];

代码及关键注释:

/*main.cc*/
int main(int argc, char *argv[])
{
#ifndef __mobile__
    //RunGuard类确保同一时刻只运转一个程序
    RunGuard guard("QGroundControlRunGuardKey");
    if (!guard.tryToRun()) {
        // QApplication is necessary to use QMessageBox
        QApplication errorApp(argc, argv);
        QMessageBox::critical(nullptr, QObject::tr("Error"),
            QObject::tr("A second instance of %1 is already running. Please close the other instance and try again.").arg(QGC_APPLICATION_NAME)
        );
        return -1;
    }
#endif
    //-- Record boot time:记录发动时刻
    QGC::initTimer();
    // install the message handler:装置消息处理回调函数
    AppMessages::installHandler();
    // Set our own OpenGL buglist:设置咱们自己的 OpenGL 错误列表
    qputenv("QT_OPENGL_BUGLIST", ":/opengl/resources/opengl/buglist.json");
    // Allow for command line override of renderer:允许命令行覆盖渲染器
    for (int i = 0; i < argc; i++) {
        const QString arg(argv[i]);
        if (arg == QStringLiteral("-angle")) {
            QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
            break;
        } else if (arg == QStringLiteral("-swrast")) {
            QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
            break;
        }
    }
    // The following calls to qRegisterMetaType are done to silence debug output which warns
    // that we use these types in signals, and without calling qRegisterMetaType we can't queue
    // these signals. In general we don't queue these signals, but we do what the warning says
    // anyway to silence the debug output.
    //
    qRegisterMetaType<QSerialPort::SerialPortError>();
    qRegisterMetaType<QAbstractSocket::SocketError>();
    qRegisterMetaType<QGCSerialPortInfo>();
    // We statically link our own QtLocation plugin
    // 静态链接QtLocation 插件
    //这一块代码是一个预处理器指令,用于禁用 Windows 编译器中的两个特定正告。 
    //具体来说,它禁用正告 4930(正告函数调用中的参数数量与函数界说中的参数数量之间可能不匹配)和正告 4101(正告未运用的局部变量)。
    //只要在 Windows 操作体系上编译代码时才会履行此代码块,如 Q_OS_WIN 宏所示。
#ifdef Q_OS_WIN
    // In Windows, the compiler doesn't see the use of the class created by Q_IMPORT_PLUGIN
#pragma warning( disable : 4930 4101 )
#endif
    Q_IMPORT_PLUGIN(QGeoServiceProviderFactoryQGC)
    bool runUnitTests = false;          // Run unit tests:运转单元测试
    QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
    //承继自QApplication
    QGCApplication* app = new QGCApplication(argc, argv, runUnitTests);
    Q_CHECK_PTR(app);
    if(app->isErrorState()) {
        app->exec();
        return -1;
    }
    // There appears to be a threading issue in qRegisterMetaType which can cause it to throw a qWarning
    // about duplicate type converters. This is caused by a race condition in the Qt code. Still working
    // with them on tracking down the bug. For now we register the type which is giving us problems here
    // while we only have the main thread. That should prevent it from hitting the race condition later
    // on in the code.
    //qRegisterMetaType 中好像存在线程问题,这可能导致它抛出有关重复类型转换器的 qWarning。 
    //这是由 Qt 代码中的竞赛条件引起的。 仍在与他们协作追寻错误。 
    //现在咱们注册了给咱们带来问题的类型,而咱们只要主线程。 
    //这应该能够防止它稍后在代码中遇到竞赛条件。
    qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >();
    app->_initCommon();//注册Qml目标
    //-- Initialize Cache System:初始化缓存体系
    getQGCMapEngine()->init();
    int exitCode = 0;
    if (!app->_initForNormalAppBoot()) {//程序正常发动初始化
        return -1;
    }
    exitCode = app->exec();
    app->_shutdown();//封闭所有的qml
    delete app;
    //-- Shutdown Cache System:封闭缓存体系
    destroyMapEngine();
    qDebug() << "After app delete";
    return exitCode;
}

关键功用

首要用到了qRegisterMetaType自界说类型和QtPlugin插件体系

qRegisterMetaType

如果想要咱们自己自界说的类型也能够有 Qt 自己类型的功用的话,就有必要注册咱们的类型到 Qt 中,这样才能够在信号和槽的通讯机制中运用咱们的自界说的类型。

不跨线程的话,运用自界说的类型运用signal/slot来传递,没有什么问题。但如果是跨线程的运用,则没有这么简略。

直接运用的话,会产生下面这种错误:(假定自界说类为MyClass) QObject::connect: Cannot queue arguments of type ‘MyClass’ (Make sure ‘MyClass’ is registed using qRegisterMetaType().) 实际运转中也会发现,该信号槽没有起作用。其实解决方法在错误提示中现已给出了:Make sure ‘MyClass’ is registed using qRegisterMetaType().
即运用qRegisterMetaType()将自界说类型进行注册。

在Qt中,想要运用signal/slot来传递自界说的类型时,需求运用qRegisterMetaType来注册。其原因是:当一个signal被放到行列中(queued)时,它的参数(arguments)也会被一同一同放到行列中(queued起来),这就意味着参数在被传送到slot之前需求被复制、存储在行列中(queue)中;为了能够在行列中存储这些参数(argument),Qt需求去construct、destruct、copy这些目标。用qRegisterMetaType对自界说的类型进行注册,就是为了告知Qt怎么去做这些工作。

过程:(以自界说MyDataType类型为例)

  1. 自定MyDataType 类型,在这个类型的顶部包含:#include
  2. 在类型界说完成后,参加声明:Q_DECLARE_METATYPE(MyDataType);
  3. 在main()函数中注册这种类型:qRegisterMetaType(“MyDataType”);
  4. 如果还期望运用这种类型的引用,可同样要注册:qRegisterMetaType(“MyDataType&”);
#include <QMetaType>       
class MyDataType
  {     
   public:     
   MyDataType();     
   MyDataType(int, double);        
private:      
};     
Q_DECLARE_METATYPE(MyDataType);     
int main(int argc, char *argv[])     
{     
    QApplication app(argc, argv); 
    qRegisterMetaType<MyDataType>("MyDataType");     
    qRegisterMetaType<MyDataType>("MyDataType&");     
}

以代码中的qRegisterMetaType() 为例,能够看到在main.cc中进行了注册。

#ifndef __mobile__
#ifndef NO_SERIAL_LINK
    Q_DECLARE_METATYPE(QGCSerialPortInfo)
#endif
#endif

Q_IMPORT_PLUGIN

Q_IMPORT_PLUGIN 宏通常在 Qt 应用程序中用于为特定类导入插件。 导入插件时,它会为其相关的类供给附加功用或自界说。

Q_IMPORT_PLUGIN 的一些常见用例包含为与多媒体、位置服务和数据库连接相关的类导入插件。 例如,在供给的代码块中,Q_IMPORT_PLUGIN(QGeoServiceProviderFactoryQGC) 正在为 QGeoServiceProviderFactoryQGC 类导入一个插件,该插件用于在 Qt 应用程序中供给位置服务。

要运用 Q_IMPORT_PLUGIN,您有必要首先为所需的类创建一个插件。 这能够经过子类化适当的插件接口类并完成必要的功用来完成。 创建插件后,能够运用 Q_IMPORT_PLUGIN 将其加载到应用程序中。

需求留意的是,插件有必要与主应用程序分隔构建,并放置在特定目录中以便应用程序找到它们。 插件的目录结构和命名约好由 Qt 界说,能够在 Qt 文档中找到。doc.qt.io/qt-5/plugin…

制作应用程序插件的扩展能够经过如下的过程:

  1. 界说接口的调集(类的纯虚函数)跟从这个插件plugins.
  2. 运用这个Q_DECLARE_INTERFACE()宏来告知Qt的元目标体系关于这个接口。
  3. 运用QPluginLoader在应用程序中来加载这些插件.
  4. 运用qobject_cast()来告知插件完成给定的接口。

写一个插件包含下面的过程:

  1. 声明一个插件类承继自QObject,或许从这个插件想供给的接口;
  2. 运用Q_INTERFACE()宏来告知Qt的元目标体系关于这个这个插件;
  3. 导出这个插件经过运用Q_PLUGIN_METADATA()宏;
  4. 编译插件经过运用合适的.pro 文件。

例如,这有一个界说的接口类:

class FilterInterface
{
public:
    virtual ~FilterInterface() {}
    virtual QStringList filters() const = 0;
    virtual QImage filterImage(const QString &filter, const QImage &image,
                               QWidget *parent) = 0;
};

这界说的插件类的完成接口:

#include <QObject>
#include <QtPlugin>
#include <QStringList>
#include <QImage>
#include <plugandpaint/interfaces.h>
class ExtraFiltersPlugin : public QObject, public FilterInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.FilterInterface" FILE "extrafilters.json")
    Q_INTERFACES(FilterInterface)
public:
    QStringList filters() const;
    QImage filterImage(const QString &filter, const QImage &image,
                       QWidget *parent);
};

setAttribute

设置 Qt::AA_UseHighDpiPixmaps 特点,为应用程序启用高 DPI 像素图支撑。 这意味着应用程序将在高 DPI 显示器上运用高分辨率图像作为图标和其他图形。此特点通常在应用程序的首要功用的开头设置。

运用到的自界说类

  • RunGuard
  • QGC
  • AppMessages
  • QGCSerialPortInfo
  • QGeoServiceProviderFactoryQGC
  • QGCApplication
  • QGCMapEngine