[qt5学习笔记]用vs2022(msvc2017)+copilot进行QtWidgetsApplication源码解析

发布于:2025-02-15 ⋅ 阅读:(18) ⋅ 点赞:(0)

一直没深入了解qt,又一段时间没写qt,重新捡起来。

开发环境

本地vs2022(msvc2017, v14.30)先升级到最新版本,方便使用copilot。
参考 VS2022+QT5环境搭建 下载 qt5.14.2

用vs的qt插件设置qt5.14.2x86路径,x64版本未安装。
创建一个默认的QtWidgetsApplication程序。

工程文件及生成关系见编译输出

1>------ 已启动全部重新生成: 项目: QtWidgetsApplication1, 配置: Debug Win32 ------
1> Qt/MSBuild: 3.3.1.1
1>Reading Qt configuration (C:/Qt/Qt5.14.2/5.14.2/msvc2017/bin/qmake)
1> Qt: 5.14.2
1>uic QtWidgetsApplication1.ui
1>rcc QtWidgetsApplication1.qrc
1>moc QtWidgetsApplication1.h
1>stdafx.cpp
1>QtWidgetsApplication1.cpp
1>main.cpp
1>qrc_QtWidgetsApplication1.cpp
1>moc_QtWidgetsApplication1.cpp
1>QtWidgetsApplication1.vcxproj -> C:\code\cpp_test\QtWidgetsApplication1\Debug\QtWidgetsApplication1.exe
========== 全部重新生成: 1 成功,0 失败,0 已跳过 ==========
========== 重新生成 于 23:35 完成,耗时 05.139==========

uic rcc moc三种自动生成的qt文件

QtWidgetsApplication1.h(有Q_OBJECT) 对应

QtWidgetsApplication1\Debug\qt\moc\moc_QtWidgetsApplication1.cpp
QtWidgetsApplication1是唯一一个界面类,头文件里面的Q_OBJECT会触发生成moc_*.cpp文件。这个moc文件需要展开说。
QtWidgetsApplication1.h关联了自动生成的ui_QtWidgetsApplication1.h文件,Ui::QtWidgetsApplication1Class类在此定义。
QtWidgetsApplication1 界面类继承QMainWindow直接获得主窗口所有技能。Q_OBJECT宏放后面展开描述。

#include <QtWidgets/QMainWindow>
#include "ui_QtWidgetsApplication1.h"

class QtWidgetsApplication1 : public QMainWindow
{
    Q_OBJECT

public:
    QtWidgetsApplication1(QWidget *parent = nullptr);
    ~QtWidgetsApplication1();

private:
    Ui::QtWidgetsApplication1Class ui;
};

moc_QtWidgetsApplication1.cpp代码如下:

#include "…/…/…/QtWidgetsApplication1.h"确保被前置定义了。并通过编译条件自检,存在错误时,编译期会输出错误信息。
struct qt_meta_stringdata_QtWidgetsApplication1_t 存储类名的元数据。
QT_MOC_LITERAL 宏用于初始化 QByteArrayData 结构体.

moc代码使得 Qt 的信号和槽机制能够在运行时正确工作。我觉得关键点就是利用QObject派生形成界面数据树,用各种静态函数、元数据等形成自描述信息,方便qt代码在界面树中检索信号槽。

#include <memory>
#include "stdafx.h"
#include "../../../QtWidgetsApplication1.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'QtWidgetsApplication1.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.14.2. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif

QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_QtWidgetsApplication1_t {
    QByteArrayData data[1];
    char stringdata0[22];//存储类名的元数据
};
//初始化 QByteArrayData 结构体
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_QtWidgetsApplication1_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )

//静态常量,包含了类名 QtWidgetsApplication1 的元数据。
static const qt_meta_stringdata_QtWidgetsApplication1_t qt_meta_stringdata_QtWidgetsApplication1 = {
    {
QT_MOC_LITERAL(0, 0, 21) // "QtWidgetsApplication1"

    },
    "QtWidgetsApplication1"
};
#undef QT_MOC_LITERAL

//静态常量数组 qt_meta_data_QtWidgetsApplication1,用于存储类的元数据。
//这个数组包含了类的修订版本、类名、类信息、方法、属性、枚举、构造函数、标志和信号的数量。
static const uint qt_meta_data_QtWidgetsApplication1[] = {

 // content:
       8,       // revision
       0,       // classname
       0,    0, // classinfo
       0,    0, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       0,       // signalCount

       0        // eod
};

//静态成员函数,用于处理元对象系统的调用
void QtWidgetsApplication1::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    Q_UNUSED(_o);
    Q_UNUSED(_id);
    Q_UNUSED(_c);
    Q_UNUSED(_a);
}

//静态常量 QMetaObject,用于存储类的元对象信息。
//它包含了父类的元对象(QMainWindow::staticMetaObject)、类名的元数据、类的元数据、静态元调用函数和其他一些指针。
QT_INIT_METAOBJECT const QMetaObject QtWidgetsApplication1::staticMetaObject = { {
    QMetaObject::SuperData::link<QMainWindow::staticMetaObject>(),
    qt_meta_stringdata_QtWidgetsApplication1.data,
    qt_meta_data_QtWidgetsApplication1,
    qt_static_metacall,
    nullptr,
    nullptr
} };

//metaObject 函数返回类的元对象。
//如果对象有动态元对象,则返回动态元对象;
//否则,返回静态元对象。
const QMetaObject *QtWidgetsApplication1::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

//qt_metacast 函数用于在运行时进行类型转换。
//如果传入的类名与 QtWidgetsApplication1 匹配,则返回当前对象的指针;
//否则,调用父类的 qt_metacast 函数。
void *QtWidgetsApplication1::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_QtWidgetsApplication1.stringdata0))
        return static_cast<void*>(this);
    return QMainWindow::qt_metacast(_clname);
}

//qt_metacall 函数用于处理元对象系统的调用。
//在这个例子中,它调用父类的 qt_metacall 函数,并返回调用的 ID。
int QtWidgetsApplication1::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QMainWindow::qt_metacall(_c, _id, _a);
    return _id;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

QtWidgetsApplication1.qrc 对应

QtWidgetsApplication1\Debug\qt\rcc\qrc_QtWidgetsApplication1.cpp
QtWidgetsApplication1.qrc 是一个xml片段文件,用于描述资源文件数据集,方便程序访问资源文件。

<RCC>
    <qresource prefix="/QtWidgetsApplication1">
        <file>QtWidgetsApplication1.ico</file>
    </qresource>
</RCC>

生成的主要代码如下:
关键点是当前qrc cpp编译单元中有一个全局对象struct initializer dummy;这个对象会利用cpp的构造析构函数调用全局init和cleanup函数,避免手工编码管理资源文件相关的内存。

int QT_RCC_MANGLE_NAMESPACE(qInitResources_QtWidgetsApplication1)();
int QT_RCC_MANGLE_NAMESPACE(qInitResources_QtWidgetsApplication1)()
{
    return 1;
}

int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_QtWidgetsApplication1)();
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_QtWidgetsApplication1)()
{
    return 1;
}

namespace {
   struct initializer {
       initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources_QtWidgetsApplication1)(); }
       ~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources_QtWidgetsApplication1)(); }
   } dummy;
}

QtWidgetsApplication1.ui 对应

QtWidgetsApplication1\Debug\qt\uic\ui_QtWidgetsApplication1.h
QtWidgetsApplication1.ui 也是一个xml片段文件,用来描述界面,例如尺寸、内容、布局等。
需要注意的是,ui文件关联了qrc resources文件。

<UI version="4.0" >
 <class>QtWidgetsApplication1Class</class>
 <widget class="QMainWindow" name="QtWidgetsApplication1Class" >
  <property name="objectName" >
   <string notr="true">QtWidgetsApplication1Class</string>
  </property>
  <property name="geometry" >
   <rect>
    <x>0</x>
    <y>0</y>
    <width>600</width>
    <height>400</height>
   </rect>
  </property>
  <property name="windowTitle" >
   <string>QtWidgetsApplication1</string>
  </property>  <widget class="QMenuBar" name="menuBar" />
  <widget class="QToolBar" name="mainToolBar" />
  <widget class="QWidget" name="centralWidget" />
  <widget class="QStatusBar" name="statusBar" />
 </widget>
 <layoutDefault spacing="6" margin="11" />
 <pixmapfunction></pixmapfunction>
 <resources>
   <include location="QtWidgetsApplication1.qrc"/>
 </resources>
 <connections/>
</UI>

ui文件对应的ui h文件内容如下:
setupUi 方法设置用户界面
retranslateUi 方法翻译用户界面
Ui::QtWidgetsApplication1Class 类继承自 Ui_QtWidgetsApplication1Class,简化了用户代码的使用。

class Ui_QtWidgetsApplication1Class
{
public:
    QMenuBar *menuBar;
    QToolBar *mainToolBar;
    QWidget *centralWidget;
    QStatusBar *statusBar;

    void setupUi(QMainWindow *QtWidgetsApplication1Class)
    {
        if (QtWidgetsApplication1Class->objectName().isEmpty())
            QtWidgetsApplication1Class->setObjectName(QString::fromUtf8("QtWidgetsApplication1Class"));
        QtWidgetsApplication1Class->resize(600, 400);
        menuBar = new QMenuBar(QtWidgetsApplication1Class);
        menuBar->setObjectName(QString::fromUtf8("menuBar"));
        QtWidgetsApplication1Class->setMenuBar(menuBar);
        mainToolBar = new QToolBar(QtWidgetsApplication1Class);
        mainToolBar->setObjectName(QString::fromUtf8("mainToolBar"));
        QtWidgetsApplication1Class->addToolBar(mainToolBar);
        centralWidget = new QWidget(QtWidgetsApplication1Class);
        centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
        QtWidgetsApplication1Class->setCentralWidget(centralWidget);
        statusBar = new QStatusBar(QtWidgetsApplication1Class);
        statusBar->setObjectName(QString::fromUtf8("statusBar"));
        QtWidgetsApplication1Class->setStatusBar(statusBar);

        retranslateUi(QtWidgetsApplication1Class);

        QMetaObject::connectSlotsByName(QtWidgetsApplication1Class);
    } // setupUi

    void retranslateUi(QMainWindow *QtWidgetsApplication1Class)
    {
        QtWidgetsApplication1Class->setWindowTitle(QCoreApplication::translate("QtWidgetsApplication1Class", "QtWidgetsApplication1", nullptr));
    } // retranslateUi

};

namespace Ui {
    class QtWidgetsApplication1Class: public Ui_QtWidgetsApplication1Class {};
} // namespace Ui

编译生成的qt环境信息:

QT_SYSROOT:
QT_INSTALL_PREFIX:C:/Qt/Qt5.14.2/5.14.2/msvc2017
QT_INSTALL_ARCHDATA:C:/Qt/Qt5.14.2/5.14.2/msvc2017
QT_INSTALL_DATA:C:/Qt/Qt5.14.2/5.14.2/msvc2017
QT_INSTALL_DOCS:C:/Qt/Qt5.14.2/Docs/Qt-5.14.2
QT_INSTALL_HEADERS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/include
QT_INSTALL_LIBS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/lib
QT_INSTALL_LIBEXECS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/bin
QT_INSTALL_BINS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/bin
QT_INSTALL_TESTS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/tests
QT_INSTALL_PLUGINS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/plugins
QT_INSTALL_IMPORTS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/imports
QT_INSTALL_QML:C:/Qt/Qt5.14.2/5.14.2/msvc2017/qml
QT_INSTALL_TRANSLATIONS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/translations
QT_INSTALL_CONFIGURATION:
QT_INSTALL_EXAMPLES:C:/Qt/Qt5.14.2/Examples/Qt-5.14.2
QT_INSTALL_DEMOS:C:/Qt/Qt5.14.2/Examples/Qt-5.14.2
QT_HOST_PREFIX:C:/Qt/Qt5.14.2/5.14.2/msvc2017
QT_HOST_DATA:C:/Qt/Qt5.14.2/5.14.2/msvc2017
QT_HOST_BINS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/bin
QT_HOST_LIBS:C:/Qt/Qt5.14.2/5.14.2/msvc2017/lib
QMAKE_SPEC:win32-msvc
QMAKE_XSPEC:win32-msvc
QMAKE_VERSION:3.1
QT_VERSION:5.14.2

在stdafx.h中只有#include
默认加载的qt库:QT = core gui widgets

main.cpp

Qt 应用程序的入口点。
main之前的初始化过程由编译期生成。

#include "stdafx.h"
#include "QtWidgetsApplication1.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
    //QApplication 对象负责管理应用程序的控制流和主要设置。
    //它处理窗口系统的初始化、事件循环的启动和管理、以及应用程序级别的设置。
    QApplication a(argc, argv);//Qt 应用程序的基础对象
    //应用程序的主窗口类.调用 setupUi 方法来初始化用户界面组件
    QtWidgetsApplication1 w;
    w.show();//将窗口显示在屏幕上,并使其可见。
    //a.exec()启动应用程序的事件循环
    //事件循环会一直运行,直到调用 QApplication::quit() 或关闭所有窗口。
    return a.exec();
}

QApplication 对象 a 和 QtWidgetsApplication1 对象 w 之间的关联和交互主要通过以下方式实现:

  1. 事件循环管理:
    • QApplication 对象 a 负责管理整个应用程序的事件循环。所有的用户事件(如按键、鼠标点击等)都会通过 QApplication 对象传递给相应的窗口和控件。
    • 当用户与 QtWidgetsApplication1 窗口 w 进行交互时,这些事件会被 QApplication 对象捕获并分发给 w 进行处理。
  2. 窗口管理:
    • QApplication 对象 a 负责管理应用程序中的所有窗口,包括 QtWidgetsApplication1 窗口 w。它确保窗口的创建、显示和关闭都能正确处理。
    • QApplication 对象还负责处理窗口之间的焦点切换、窗口的最小化和最大化等操作。
  3. 信号和槽机制:
    • QApplication 对象 a 负责管理信号和槽的连接和分发。当某个信号被触发时,QApplication 对象会调用相应的槽函数来处理该信号。

qApp 是一个全局指针,指向当前应用程序的唯一实例 QApplication 对象。

QApplication 的构造函数会将 a 的指针存储在 QCoreApplication 的静态成员变量中,这样 QCoreApplication::instance() 就可以返回这个指针。

通过qApp 就可以遍历整个当前app进程的界面数据

通过 qApp 可以遍历整个当前应用程序的界面数据。
qApp 是一个全局指针,指向当前应用程序的唯一实例 QApplication 或 QGuiApplication 对象。
通过 qApp,你可以访问和操作应用程序的许多全局属性和方法,包括所有窗口和控件。

访问所有窗口
QGuiApplication 提供了几个静态方法,可以用来获取应用程序中的所有窗口:
• QGuiApplication::allWindows(): 返回应用程序中所有窗口的列表。
• QGuiApplication::topLevelWindows(): 返回应用程序中所有顶级窗口的列表。

访问焦点窗口和对象
• QGuiApplication::focusWindow(): 返回当前具有焦点的窗口。
• QGuiApplication::focusObject(): 返回当前具有焦点的对象。

访问屏幕信息
• QGuiApplication::primaryScreen(): 返回主屏幕对象。
• QGuiApplication::screens(): 返回所有屏幕的列表。
• QGuiApplication::screenAt(const QPoint &point): 返回指定点所在的屏幕。

用QObject构成类定义树,用qApp构成进程数据树

个人觉得,用QObject构成类定义树,用qApp构成进程数据树,这是qt的核心设计理念。
信号和槽机制 (Signals and Slots),通过信号(字符串或其它注册key)查找调用函数,实现了松耦合(解耦合)。直接调用函数属于紧耦合关系。

connect(senderObject, &SenderClass::signalName, receiverObject, &ReceiverClass::slotName);

Qt 的元对象系统支持运行时类型信息、信号和槽机制、属性系统等。QObject 类及其子类都支持元对象系统。
本质是通过moc自动生成自描述信息,挂接到qt类体系和运行时数据体系中。

Qt 是一个事件驱动的框架,所有的用户交互(如按键、鼠标点击等)都会生成事件,并通过事件循环进行处理。QApplication 对象负责管理事件循环。界面程序似乎都具备这个特性。

Qt 支持插件机制,允许开发者动态加载和使用插件。QPluginLoader 类用于加载插件,
QFactoryInterface 类用于定义插件接口。

Qt 提供了强大的国际化和本地化支持,允许开发者为应用程序提供多语言支持。QTranslator 类用于加载翻译文件,tr() 函数用于标记需要翻译的字符串。

模型-视图-控制器 (Model-View-Controller, MVC) 模式

Qt 提供了丰富的模型-视图框架,用于实现数据与用户界面之间的分离。QAbstractItemModel 是模型的基类,QListView、QTableView 等是视图的基类。然后通过信号和槽进行控制。

QStandardItemModel *model = new QStandardItemModel();
QListView *view = new QListView();
view->setModel(model);

// 添加控制逻辑,例如信号和槽的连接
connect(view, &QListView::clicked, this, &QtWidgetsApplication1::onItemClicked);