一直没深入了解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 之间的关联和交互主要通过以下方式实现:
- 事件循环管理:
• 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);