一直没深入了解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_OBJECTpublic: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.)"
#endifQT_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, // revision0, // classname0, 0, // classinfo0, 0, // methods0, 0, // properties0, 0, // enums/sets0, 0, // constructors0, // flags0, // signalCount0 // 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);} // setupUivoid 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 之间的关联和交互主要通过以下方式实现:
- 事件循环管理:
• QApplication 对象 a 负责管理整个应用程序的事件循环。所有的用户事件(如按键、鼠标点击等)都会通过 QApplication 对象传递给相应的窗口和控件。
• 当用户与 QtWidgetsApplication1 窗口 w 进行交互时,这些事件会被 QApplication 对象捕获并分发给 w 进行处理。 - 窗口管理:
• QApplication 对象 a 负责管理应用程序中的所有窗口,包括 QtWidgetsApplication1 窗口 w。它确保窗口的创建、显示和关闭都能正确处理。
• QApplication 对象还负责处理窗口之间的焦点切换、窗口的最小化和最大化等操作。 - 信号和槽机制:
• 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);