目录
二. 文件和目录 (File and Directory Handling)
四. 多线程和并发 (Multithreading and Concurrency)
五. 国际化和本地化 (Internationalization and Localization)
六. 进程和进程间通信 (Process and Inter-Process Communication - IPC)
九. 动态库加载 (Dynamic Library Loading)
一. 核心数据类型 (Core Datatypes)
请跳转章节,此处不再重复:QT核心模块QtCore功能详细说明,并给出测试代码(一)
二. 文件和目录 (File and Directory Handling)
请跳转章节,此处不再重复:QT核心模块QtCore功能详细说明,并给出测试代码(二)
三. 事件系统 (Event System)
QObject
请跳转章节,此处不再重复:QT核心模块QtCore功能详细说明,并给出测试代码(三)
QCoreApplication
QCoreApplication
是 Qt 应用程序的核心类,负责 管理应用程序的生命周期,并 提供事件循环 (Event Loop)。所有 Qt 程序(无论是 GUI 还是控制台应用)都必须有一个 QCoreApplication
或 QGuiApplication/QApplication
实例。唯一实例(Singleton)
- 启动和管理事件循环 (Event Loop): 持续监测过程
exec()
方法:启动事件循环- 一旦调用
exec()
,应用程序就会进入事件循环,开始监听和处理事件。 - 在
exec()
运行期间,应用程序会保持活动状态,响应各种事件。
- 一旦调用
- 事件循环的迭代过程:
- 等待事件: 事件循环首先会等待新的事件到达。这些事件可能来自操作系统、Qt 内部或其他部分的代码。
- 获取事件: 当一个或多个事件到达时,事件循环会获取它们。
- 处理事件: 事件循环会将获取到的事件分发给相应的接收者对象进行处理。
- 返回: 处理完所有待处理的事件后,事件循环会再次进入等待状态,等待新的事件。
- 停止事件循环:
quit()
和exit()
方法quit()
方法用于正常地退出事件循环。它会发出aboutToQuit()
信号,然后将事件循环的退出码设置为 0。exit(int returnCode)
方法也可以用于退出事件循环,并且可以指定一个自定义的退出码。- 当
exec()
方法因为调用了quit()
或exit()
而结束时,它会返回应用程序的退出码。
- 应用程序生命周期管理: 控制应用程序
- 启动 (Startup):
- 在任何 Qt 应用程序的
main()
函数中,你都需要创建QCoreApplication
(或QApplication
对于 GUI 程序)的实例。 - 构造函数会进行一些基本的初始化工作,例如设置应用程序的唯一标识、处理命令行参数等。
- 在任何 Qt 应用程序的
- 初始化 (Initialization):
QCoreApplication
会初始化 Qt 的核心功能,例如信号和槽机制、事件系统等。- 可以在创建
QCoreApplication
实例之后,但在调用exec()
之前执行一些应用程序特定的初始化操作。
- 运行 (Running):
- 调用
exec()
方法后,应用程序进入事件循环,开始正常运行,响应各种事件。
- 调用
- 关闭 (Shutdown):
- 当调用
quit()
或exit()
时,事件循环结束。 - 在事件循环结束之前,
QCoreApplication
会发出aboutToQuit()
信号。 main()
函数在exec()
返回后继续执行,通常会返回exec()
的返回值作为应用程序的退出码。
- 当调用
- 启动 (Startup):
- 处理应用程序级别的信号和槽:【QGuiApplication 或 QApplication】
aboutToQuit()
: 在事件循环即将结束时发出。这是进行最后清理工作的理想时机。applicationStateChanged()
: 当应用程序的状态发生改变时发出。
- 管理应用程序设置: 允许设置和获取应用程序名称、组织名称、组织域等全局信息。
setApplicationName(const QString &name)
/applicationName()
: 设置或获取应用程序的名称。setApplicationVersion(const QString &version)
/applicationVersion()
: 设置或获取应用程序的版本号。setOrganizationName(const QString &name)
/organizationName()
: 设置或获取组织的名称。setOrganizationDomain(const QString &domain)
/organizationDomain()
: 设置或获取组织的域名。
- 处理命令行参数:
arguments()
返回一个QStringList
,包含应用程序的名称以及所有传递给它的命令行参数。
- 应用程序状态管理:
applicationState()获取【
QGuiApplication 或 QApplication】
-
Qt::ApplicationState
枚举值Qt::ApplicationActive
,Qt::ApplicationInactive
Qt::ApplicationHidden
-
-
QCoreApplication 相关的静态函数
函数 作用 arguments()
获取命令行参数 applicationName()
/setApplicationName()
获取/设置应用程序名称 applicationVersion()
获取应用程序版本 quit()
退出应用程序(安全) exit(int code)
退出事件循环 processEvents()
手动处理事件队列 postEvent(QObject *receiver, QEvent *event)
向对象发送事件 -
QCoreApplication VS QApplication
类 适用场景 特点 QCoreApplication
控制台程序、非 GUI 程序 只提供 基本事件循环,不支持 GUI QGuiApplication
需要 GUI(但无 QWidget) 提供 事件循环 + GUI 相关功能(如 OpenGL, QML) QApplication
标准 GUI 应用 提供 完整 GUI 支持(包含 QGuiApplication
的所有功能)测试代码-QCoreApplication
#include <QCoreApplication>
#include <QDebug>
#include <QStringList>
#include <QTimer>
int main(int argc, char *argv[]) {
// 创建 QCoreApplication 实例
QCoreApplication app(argc, argv);
// ======================
// 1. 实例化 QCoreApplication
// ======================
qDebug() << "1. QCoreApplication instance created.";
// ======================
// 2. 生命周期管理 - aboutToQuit() 信号
// ======================
QObject::connect(&app, &QCoreApplication::aboutToQuit, []() {
qDebug() << "2. QCoreApplication::aboutToQuit() signal emitted before exiting.";
});
// ======================
// 3. 命令行参数
// ======================
qDebug() << "\n3. Command Line Arguments:";
QStringList args = app.arguments();
for (int i = 0; i < args.size(); ++i) {
qDebug() << " Argument" << i << ":" << args[i];
}
// ======================
// 4. 应用程序名称及其他全局信息
// ======================
app.setApplicationName("MyAppTest");
app.setApplicationVersion("1.0.0");
app.setOrganizationName("MyOrganization");
app.setOrganizationDomain("myorganization.com");
qDebug() << "\n4. Application Info:";
qDebug() << " Application Name:" << app.applicationName();
qDebug() << " Application Version:" << app.applicationVersion();
qDebug() << " Organization Name:" << app.organizationName();
qDebug() << " Organization Domain:" << app.organizationDomain();
// ======================
// 5. 获取 QCoreApplication 实例
// ======================
qDebug() << "\n5. QCoreApplication Instance:";
QCoreApplication *instancePtr = QCoreApplication::instance();
qDebug() << " Address of app object:" << &app;
qDebug() << " Address of QCoreApplication::instance():" << instancePtr;
qDebug() << " Are they the same instance?" << (&app == instancePtr);
// ======================
// 6. 退出应用程序
// ======================
qDebug() << "\n6. Exiting the application in 5 seconds...";
QTimer::singleShot(5000, &app, &QCoreApplication::quit);
// ======================
// 7. 启动事件循环
// ======================
qDebug() << "\n7. Calling app.exec() to start the event loop...";
int returnCode = app.exec();
qDebug() << "8. app.exec() finished with return code:" << returnCode;
return returnCode;
}
// ======================
// console
// ======================
//1. QCoreApplication instance created.
//3. Command Line Arguments:
// Argument 0 : "/home/user/QtProj/build-QtCore-Desktop-Debug/QtCore"
//4. Application Info:
// Application Name: "MyAppTest"
// Application Version: "1.0.0"
// Organization Name: "MyOrganization"
// Organization Domain: "myorganization.com"
//5. QCoreApplication Instance:
// Address of app object: QCoreApplication(0x7fffffffe120)
// Address of QCoreApplication::instance(): QCoreApplication(0x7fffffffe120)
// Are they the same instance? true
//6. Exiting the application in 5 seconds...
//7. Calling app.exec() to start the event loop...
//2. QCoreApplication::aboutToQuit() signal emitted before exiting.
//8. app.exec() finished with return code: 0
QEventLoop
QEventLoop 是 Qt 框架中的一个核心类,用于管理和运行事件循环。它是 QCoreApplication 事件循环机制的基础,提供了灵活的事件处理能力,适用于需要自定义事件循环或局部事件处理的场景。
- 启动和管理事件循环: 这是
QEventLoop
的核心功能,通过exec()
方法启动,持续监听和处理事件。-
通过 exec() 方法启动一个事件循环,类似于 QCoreApplication::exec() 的底层实现。
-
调用 exec() 后,线程进入阻塞状态,循环从事件队列中取出事件并分发,事件循环依赖平台特定的事件调度器(如 Linux 的 epoll、Windows 的消息循环)。
-
可通过信号或手动调用控制循环的启动和停止
-
- 处理来自各种来源的事件: 事件被加事件队列
-
操作系统事件: 在 GUI 应用中,处理窗口事件(如鼠标、键盘)。
-
定时器事件: QTimer 的 timeout 信号。
-
信号: 通过 QueuedConnection 传递的跨线程信号。
-
帖子事件: 通过 QCoreApplication::postEvent() 手动添加的事件。
-
Socket 和 File Notifiers: QSocketNotifier 和 QFileSystemWatcher 的事件。
-
自定义事件: 继承 QEvent 创建的用户定义事件。
-
- 事件过滤: 提供机制在事件到达目标对象之前进行拦截和处理
-
应用程序级别:使用 QCoreApplication::installEventFilter()。对象级别:使用 QObject::installEventFilter()。
-
过滤器对象在事件分发前接收事件,可选择处理或忽略。
-
事件流:产生->创建->投递/发送->检索->过滤->分发->处理->传播 (GUI)->消亡
-
- 事件分发:
-
QEventLoop 将事件分发给目标对象,通常通过调用目标的 event() 方法。
-
事件队列中的每个事件都有目标 QObject,调用 QObject::event(),由目标对象处理或转发到特定事件处理函数。
-
可通过重写 event() 方法自定义分发逻辑。
-
- 支持嵌套事件循环:
-
QEventLoop 支持嵌套运行,即在一个事件循环中启动另一个局部事件循环。
-
每个 QEventLoop 实例独立管理其退出条件,嵌套循环不会干扰外层循环。
-
在处理复杂操作(如模态对话框、同步等待)时使用。
-
- 提供非阻塞的事件处理:
-
通过 processEvents() 方法,QEventLoop 可以在不阻塞线程的情况下处理事件队列。
-
只处理当前队列中的事件,不会等待新事件。
-
用于刷新状态或避免 UI 冻结。
-
processEvents事件类型
QEventLoop::AllEvents
(默认值): 处理所有类型的挂起事件。QEventLoop::ExcludeUserInputEvents
: 处理所有挂起的事件,但不包括用户输入事件(例如,鼠标和键盘事件)。QEventLoop::ExcludeSocketNotifiers
: 处理所有挂起的事件,但不包括来自套接字通知器的事件。QEventLoop::ExcludeTimers
: 处理所有挂起的事件,但不包括定时器事件。QEventLoop::ExcludeDeferredDDeletions
: 处理所有挂起的事件,但不包括延迟删除事件(由deleteLater()
触发)。QEventLoop::ExcludeDeferredUpdates
: 处理所有挂起的事件,但不包括延迟样式更新事件。QEventLoop::ExcludeApplicationExiting
: 处理所有挂起的事件,但不包括应用程序退出事件。
-
- 线程关联性:
-
一个
QEventLoop
实例在哪个线程中运行,它就负责处理该线程中的事件。 -
每个线程可以独立地拥有自己的事件循环。
-
- 控制事件循环的生命周期:
-
quit(): 设置退出标志,返回值为 0。
-
exit(int): 设置退出标志并指定返回值。
-
- 唤醒事件循环:
-
功能: wakeUp() 方法从其他线程唤醒事件循环,确保处理新事件。
-
机制: 发送一个唤醒信号给事件循环线程,使其检查队列。
-
在多线程中通知事件循环处理新加入的事件。
-
-
QEventLoop 与 QCoreApplication 关系
-
QCoreApplication::exec()
:启动了应用程序的主事件循环。 -
QCoreApplication
隐式的 创建和管理QEventLoop
-
QEventLoop
对象是真正处理事件:等待事件、获取事件、事件过滤、事件分发。 -
QEventLoop
的生命周期通常与QCoreApplication
的exec()
调用绑定在一起。 -
主事件循环由
QCoreApplication::exec()
启动,可添加额外的QEventLoop
事件循环。
-
-
QEventLoop 子事件循环常见应用场景
应用场景 描述 模态对话框 ( QDialog
)进入子事件循环,阻塞主窗口交互,直到用户关闭对话框:
QDialog dialog;
dialog.exec(); // 进入子事件循环,阻塞主事件循环等待子线程任务完成 在主线程等待子线程完成,避免 UI 冻结
QEventLoop loop;
QObject::connect(&worker, &Worker::workFinished, &loop, &QEventLoop::quit);
loop.exec(); // 等待子线程完成避免 UI 冻结的耗时任务 在循环中使用
QEventLoop
处理事件,防止界面卡顿:QEventLoop loop;
QTimer::singleShot(50, &loop, &QEventLoop::quit);
loop.exec(); // UI 仍然响应同步等待网络请求完成 QEventLoop
等待QNetworkReply::finished()
,避免阻塞QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec(); // 等待网络请求完成消息框 ( QMessageBox
)QMessageBox::exec()
进入子事件循环,阻塞主线程QMessageBox msgBox;
msgBox.exec(); // 进入子事件循环
测试代码-事件循环
#include <QCoreApplication>
#include <QEventLoop>
#include <QTimer>
#include <QThread>
#include <QSocketNotifier>
#include <QDebug>
#include <unistd.h>
// ======================
// CustomEvent 类定义
// ======================
class CustomEvent : public QEvent {
public:
CustomEvent() : QEvent(QEvent::Type(QEvent::User + 1)) {}
};
// ======================
// EventHandler 类定义
// ======================
class EventHandler : public QObject {
Q_OBJECT
public:
EventHandler() {}
protected:
// 重写 event 方法,处理自定义事件和定时器事件
bool event(QEvent *e) override {
if (e->type() == QEvent::Type(QEvent::User + 1)) {
qDebug() << "Custom event received in event().";
return true;
} else if (e->type() == QEvent::Timer) {
qDebug() << "Timer event handled in event().";
return true;
}
return QObject::event(e);
}
// 重写 eventFilter 方法,过滤定时器事件
bool eventFilter(QObject *obj, QEvent *event) override {
if (event->type() == QEvent::Timer) {
qDebug() << "Timer event filtered before reaching target.";
return true; // 过滤掉定时器事件
}
return QObject::eventFilter(obj, event);
}
};
// ======================
// WorkerThread 类定义
// ======================
class WorkerThread : public QThread {
Q_OBJECT
public:
WorkerThread() : loop(nullptr) {}
QEventLoop *loop;
protected:
// 重写 run 方法,启动线程的事件循环
void run() override {
loop = new QEventLoop();
QTimer timer;
timer.start(1000);
QObject::connect(&timer, &QTimer::timeout, []() {
qDebug() << "Thread timer triggered in:" << QThread::currentThreadId();
});
qDebug() << "Thread event loop starting in:" << QThread::currentThreadId();
loop->exec();
qDebug() << "Thread event loop exited.";
delete loop;
}
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
qDebug() << "Main thread ID:" << QThread::currentThreadId();
// ======================
// 1. 启动和管理事件循环
// ======================
qDebug() << "\n1. Starting and Managing Event Loop";
QEventLoop mainLoop;
QTimer exitTimer;
exitTimer.setSingleShot(true);
QObject::connect(&exitTimer, &QTimer::timeout, &mainLoop, &QEventLoop::quit);
exitTimer.start(10000); // 10秒后退出主循环
// ======================
// 2. 处理来自各种来源的事件
// ======================
qDebug() << "\n2. Handling Events from Various Sources";
QTimer timer;
timer.start(1000); // 定时器事件
QObject::connect(&timer, &QTimer::timeout, []() {
qDebug() << "Timer event triggered.";
});
QSocketNotifier notifier(STDIN_FILENO, QSocketNotifier::Read); // Socket Notifier 事件
QObject::connect(¬ifier, &QSocketNotifier::activated, []() {
// qDebug() << "Input detected on stdin.";
});
EventHandler handler;
QCoreApplication::postEvent(&handler, new CustomEvent()); // 帖子事件(自定义事件)
// ======================
// 3. 事件过滤
// ======================
qDebug() << "\n3. Event Filtering";
timer.installEventFilter(&handler); // 安装事件过滤器
// ======================
// 4. 事件分发
// ======================
qDebug() << "\n4. Event Dispatching";
// 通过 handler 的 event() 方法处理事件(见上文 CustomEvent)
// ======================
// 5. 支持嵌套事件循环
// ======================
qDebug() << "\n5. Nested Event Loop";
QTimer nestedTrigger;
nestedTrigger.start(2000);
QObject::connect(&nestedTrigger, &QTimer::timeout, [&mainLoop]() {
qDebug() << "Starting nested event loop...";
QEventLoop nestedLoop;
QTimer nestedTimer;
nestedTimer.setSingleShot(true);
QObject::connect(&nestedTimer, &QTimer::timeout, []() {
qDebug() << "Nested timer triggered.";
});
QObject::connect(&nestedTimer, &QTimer::timeout, &nestedLoop, &QEventLoop::quit);
nestedTimer.start(500);
nestedLoop.exec();
qDebug() << "Nested event loop finished.";
});
// ======================
// 6. 非阻塞的事件处理
// ======================
qDebug() << "\n6. Non-blocking Event Processing";
QTimer nonBlockingTimer;
nonBlockingTimer.start(1500);
QObject::connect(&nonBlockingTimer, &QTimer::timeout, []() {
qDebug() << "Non-blocking timer triggered.";
});
for (int i = 0; i < 3; ++i) {
qDebug() << "Processing events non-blocking...";
mainLoop.processEvents(QEventLoop::AllEvents, 1000); // 处理 1 秒内的事件
}
// ======================
// 7. 线程关联性
// ======================
qDebug() << "\n7. Thread Affinity";
WorkerThread thread;
thread.start();
// ======================
// 8. 控制事件循环的生命周期
// ======================
qDebug() << "\n8. Controlling Event Loop Lifecycle";
QEventLoop controlLoop;
QTimer controlTimer;
controlTimer.setSingleShot(true);
QObject::connect(&controlTimer, &QTimer::timeout, [&controlLoop]() {
qDebug() << "Exiting control loop with code 42.";
controlLoop.exit(42);
});
controlTimer.start(1000);
int result = controlLoop.exec();
qDebug() << "Control loop exited with code:" << result;
// ======================
// 9. 唤醒事件循环
// ======================
qDebug() << "\n9. Waking Up Event Loop";
QTimer wakeTimer;
wakeTimer.setSingleShot(true);
QObject::connect(&wakeTimer, &QTimer::timeout, [&thread]() {
if (thread.loop && thread.loop->isRunning()) {
qDebug() << "Waking up thread event loop...";
thread.loop->wakeUp();
QCoreApplication::postEvent(thread.loop, new CustomEvent());
}
});
wakeTimer.start(3000);
// ======================
// 启动主事件循环
// ======================
qDebug() << "\nStarting main event loop...";
mainLoop.exec();
thread.quit();
thread.wait();
return 0;
}
#include "main.moc" // 包含 moc 生成的文件
// ======================
// console
// ======================
//Main thread ID: 0x7ffff50757c0
//1. Starting and Managing Event Loop
//2. Handling Events from Various Sources
//3. Event Filtering
//4. Event Dispatching
//5. Nested Event Loop
//6. Non-blocking Event Processing
//Processing events non-blocking...
//Custom event received in event().
//Processing events non-blocking...
//Processing events non-blocking...
//7. Thread Affinity
//8. Controlling Event Loop Lifecycle
//Thread event loop starting in: 0x7ffff5066640
//Timer event filtered before reaching target.
//Exiting control loop with code 42.
//Control loop exited with code: 42
//9. Waking Up Event Loop
//Starting main event loop...
//Thread timer triggered in: 0x7ffff5066640
//Non-blocking timer triggered.
//Starting nested event loop...
//Timer event filtered before reaching target.
//Thread timer triggered in: 0x7ffff5066640
//Nested timer triggered.
//Nested event loop finished.
//Non-blocking timer triggered.
//Timer event filtered before reaching target.
//Thread timer triggered in: 0x7ffff5066640
//Starting nested event loop...
//Timer event filtered before reaching target.
//Waking up thread event loop...
//Thread timer triggered in: 0x7ffff5066640
//Non-blocking timer triggered.
//Nested timer triggered.
//Nested event loop finished.
//Thread timer triggered in: 0x7ffff5066640
//Timer event filtered before reaching target.
//Thread timer triggered in: 0x7ffff5066640
//Starting nested event loop...
//Non-blocking timer triggered.
//Timer event filtered before reaching target.
//Nested timer triggered.
//Nested event loop finished.
//Timer event filtered before reaching target.
//Thread timer triggered in: 0x7ffff5066640
//Non-blocking timer triggered.
//Starting nested event loop...
//Timer event filtered before reaching target.
//Thread timer triggered in: 0x7ffff5066640
//Nested timer triggered.
//Nested event loop finished.
//Non-blocking timer triggered.
//Timer event filtered before reaching target.
//Thread timer triggered in: 0x7ffff5066640
//Starting nested event loop...
//Timer event filtered before reaching target.
//Thread timer triggered in: 0x7ffff5066640
//Non-blocking timer triggered.
//Nested timer triggered.
//Nested event loop finished.
//Thread event loop exited.
QEvent
QEvent 是 Qt 框架中所有事件类的基类。在 Qt 中,事件是代表应用程序内部或由于外部活动而发生的事情的对象。它们是 Qt 应用程序中实现交互性和响应性的核心机制。
-
基本概念:
- 事件对象: 当应用程序中发生某些事情时,Qt 会创建一个
QEvent
或其子类的实例来表示该事件。鼠标点击、键盘输入等; - 事件类型: 每个事件都有一个与之关联的类型,由
QEvent::Type
枚举定义。进行事件区分处理; - 事件目标: 事件会被传递给特定的
QObject
及其子类(通常是窗口部件QWidget
)。这个目标对象负责处理该事件并做出相应的响应。
- 事件对象: 当应用程序中发生某些事情时,Qt 会创建一个
-
主要作用:
- 通知对象: 事件机制用于通知对象应用程序中发生的各种情况。
- 处理用户交互: 大部分用户与 GUI 应用程序的交互(如鼠标点击、键盘输入)都会生成相应的事件,这些事件被传递给相关的窗口部件进行处理。
- 系统和应用程序内部通信: 事件不仅用于处理用户交互,还可以用于应用程序内部的组件之间的通信,以及操作系统发出的通知(例如,窗口关闭请求)。
-
事件处理流程:
- 事件生成: 当一个事件发生时(例如,用户按下键盘上的一个键),Qt 会创建一个相应的事件对象。
- 事件传递: Qt 的事件系统会将这个事件对象传递给目标对象。事件传递的机制可以通过以下方式进行:
- 直接传递 (Directly): 大多数事件通过调用目标对象的
event()
函数来直接传递。 - 发布 (Posting): 可以使用
QCoreApplication::postEvent()
将事件发布到事件队列中,稍后由主事件循环处理。 - 发送 (Sending): 可以使用
QCoreApplication::sendEvent()
同步地将事件发送给目标对象,该函数会立即处理事件。
- 直接传递 (Directly): 大多数事件通过调用目标对象的
- 事件处理: 目标对象(通常是一个
QWidget
)会接收到事件,并通常在其event()
函数中进行处理。例如:mousePressEvent()
处理鼠标按下事件 (QMouseEvent
).keyPressEvent()
处理键盘按下事件 (QKeyEvent
).paintEvent()
处理绘制事件 (QPaintEvent
).resizeEvent()
处理窗口大小调整事件 (QResizeEvent
).closeEvent()
处理窗口关闭事件 (QCloseEvent
). 子类通常会重写这些特定的事件处理函数来响应相应的事件。
- 事件过滤: Qt 允许使用事件过滤器来拦截和处理发送给其他对象的事件
QObject::installEventFilter()
安装事件过滤器。
-
常见事件
类名 描述 QMouseEvent
鼠标事件(按下、释放、移动、双击等) QKeyEvent
键盘事件(按下、释放) QPaintEvent
窗口部件需要被绘制或重绘的事件 QResizeEvent
窗口部件大小改变的事件 QMoveEvent
窗口部件位置移动的事件 QCloseEvent
窗口部件关闭的事件 QTimerEvent
由 QTimer
发出的定时器事件QFocusEvent
窗口部件获得或失去键盘焦点的事件 QActionEvent
与 QAction
相关的事件(例如,触发)QContextMenuEvent
上下文菜单事件 QDragEnterEvent
拖放操作相关的事件 QDragMoveEvent
拖放操作相关的事件 QDropEvent
拖放操作相关的事件 QShowEvent
窗口部件显示的事件 QHideEvent
窗口部件隐藏的事件 QWindowStateChangeEvent
窗口部件窗口状态改变的事件(例如,最大化、最小化) QCustomEvent
用户自定义的事件类型
-
QEvent::Type
常量常量名 描述 QEvent::None
空事件。 QEvent::MouseButtonPress
鼠标按键按下。 QEvent::MouseButtonRelease
鼠标按键释放。 QEvent::MouseMove
鼠标移动。 QEvent::KeyPress
键盘按键按下。 QEvent::KeyRelease
键盘按键释放。 QEvent::Timer
定时器到期。 QEvent::Paint
需要重绘部件。 QEvent::Close
窗口关闭请求。 QEvent::Resize
部件大小改变。 QEvent::Move
部件位置移动。 QEvent::Show
部件显示。 QEvent::Hide
部件隐藏。 QEvent::FocusIn
部件获得焦点。 QEvent::FocusOut
部件失去焦点。 QEvent::InputMethod
输入法相关事件。 QEvent::SockAct
套接字活动。 QEvent::FileOpen
文件打开请求。 QEvent::Quit
应用程序退出。 QEvent::User
用户自定义事件类型的起始值。开发者可以定义大于或等于 QEvent::User
的值作为自定义事件类型。
测试代码-QEvent
#include <QCoreApplication>
#include <QEvent>
#include <QTimer>
#include <QDebug>
// ======================
// 1. 定义自定义事件类型
// ======================
const QEvent::Type CustomEventType = static_cast<QEvent::Type>(QEvent::User + 1);
// ======================
// 2. 定义自定义事件类
// ======================
class MyCustomEvent : public QEvent {
public:
MyCustomEvent() : QEvent(CustomEventType) {}
QString message = "Hello from Custom Event!";
};
// ======================
// 3. 自定义对象,用于演示事件处理
// ======================
class EventHandlingObject : public QObject {
Q_OBJECT
public:
explicit EventHandlingObject(QObject *parent = nullptr) : QObject(parent) {}
protected:
// 重写 event 方法,处理自定义事件
bool event(QEvent *event) override {
if (event->type() == CustomEventType) {
MyCustomEvent *customEvent = static_cast<MyCustomEvent*>(event);
qDebug() << "EventHandlingObject: Received Custom Event - " << customEvent->message;
return true;
}
return QObject::event(event);
}
// 重写 timerEvent 方法,处理定时器事件
void timerEvent(QTimerEvent *event) override {
qDebug() << "EventHandlingObject: Timer event with ID:" << event->timerId();
QObject::timerEvent(event);
}
};
// ======================
// 4. 应用程序级别事件过滤器
// ======================
class ApplicationEventFilter : public QObject {
protected:
// 重写 eventFilter 方法,过滤自定义事件和定时器事件
bool eventFilter(QObject *watched, QEvent *event) override {
if (event->type() == CustomEventType) {
qDebug() << "ApplicationEventFilter: Received Custom Event on" << watched;
} else if (event->type() == QEvent::Timer) {
QTimerEvent *timerEvent = static_cast<QTimerEvent*>(event);
qDebug() << "ApplicationEventFilter: Timer event on" << watched << " with ID:" << timerEvent->timerId();
}
return QObject::eventFilter(watched, event);
}
};
// ======================
// 5. 对象级别事件过滤器
// ======================
class ObjectEventFilter : public QObject {
Q_OBJECT
public:
explicit ObjectEventFilter(EventHandlingObject *target, QObject *parent = nullptr)
: QObject(parent), targetObj(target) {}
protected:
// 重写 eventFilter 方法,过滤自定义事件
bool eventFilter(QObject *watched, QEvent *event) override {
if (watched == targetObj && event->type() == CustomEventType) {
qDebug() << "ObjectEventFilter (Handler): Received Custom Event";
}
return QObject::eventFilter(watched, event);
}
private:
EventHandlingObject *targetObj; // 目标对象
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
// 创建事件处理对象
EventHandlingObject handler;
// ======================
// 安装应用程序级别事件过滤器
// ======================
ApplicationEventFilter appFilter;
a.installEventFilter(&appFilter);
// ======================
// 安装对象级别事件过滤器
// ======================
ObjectEventFilter *objFilter = new ObjectEventFilter(&handler);
handler.installEventFilter(objFilter);
// ======================
// 创建并启动定时器,定期发送自定义事件
// ======================
QTimer customEventTimer;
QObject::connect(&customEventTimer, &QTimer::timeout, [&handler]() {
MyCustomEvent *customEvent = new MyCustomEvent();
QCoreApplication::postEvent(&handler, customEvent);
});
customEventTimer.start(2000);
// ======================
// 启动另一个定时器,发送标准的 QTimerEvent
// ======================
QTimer standardTimer;
QObject::connect(&standardTimer, &QTimer::timeout, [&]() {
qDebug() << "main: Timer2 timeout - standard QTimerEvent will be generated.";
});
standardTimer.start(3000);
// ======================
// 启动应用程序事件循环
// ======================
qDebug() << "Starting application event loop...";
return a.exec();
}
#include "main.moc" // 包含 moc 生成的文件
// ======================
// console
// ======================
//Starting application event loop...
//ApplicationEventFilter: Timer event on QTimer(0x7fffffffe0e0) with ID: 1
//ApplicationEventFilter: Received Custom Event on EventHandlingObject(0x7fffffffe0c0)
//ObjectEventFilter (Handler): Received Custom Event
//EventHandlingObject: Received Custom Event - "Hello from Custom Event!"
//ApplicationEventFilter: Timer event on QTimer(0x7fffffffe100) with ID: 2
//main: Timer2 timeout - standard QTimerEvent will be generated.
//ApplicationEventFilter: Timer event on QTimer(0x7fffffffe0e0) with ID: 1
//ApplicationEventFilter: Received Custom Event on EventHandlingObject(0x7fffffffe0c0)
//ObjectEventFilter (Handler): Received Custom Event
//EventHandlingObject: Received Custom Event - "Hello from Custom Event!"
//ApplicationEventFilter: Timer event on QTimer(0x7fffffffe0e0) with ID: 1
//ApplicationEventFilter: Timer event on QTimer(0x7fffffffe100) with ID: 2
//main: Timer2 timeout - standard QTimerEvent will be generated.
QTimer
QTimer
类是 Qt 框架中用于实现定时功能的类。它允许您在指定的时间间隔后发出一个信号 (timeout()
),从而触发相应的操作。
-
核心功能与工作原理
- 定时触发:
QTimer
的主要功能是在设定的时间间隔结束后,自动发出timeout()
信号。 - 基于事件循环:
QTimer
的工作完全依赖于 Qt 的事件循环。 - 非阻塞: 使用
QTimer
不会阻塞您的应用程序。
- 定时触发:
-
主要方法
timeout()
信号: 这是QTimer
最重要的信号。start(int interval)
: 启动定时器,并设置时间间隔interval
,单位是毫秒。stop()
: 停止定时器。setInterval(int interval)
: 设置定时器的时间间隔,单位是毫秒。interval() const
: 返回当前定时器的时间间隔(毫秒)。isSingleShot() const
: 返回一个布尔值,指示定时器是否为单次触发模式。setSingleShot(bool singleShot)
: 设置定时器是否为单次触发模式。- 如果
singleShot
为true
,定时器在第一次timeout()
信号发出后会自动停止。 - 如果
singleShot
为false,
调用stop()停止
。
- 如果
isActive() const
: 返回一个布尔值,指示定时器是否正在运行。- 静态方法
singleShot(int msec, const QObject *receiver, const char *member)
: 这是一个方便的静态函数,用于创建并启动一个单次触发的定时器。
-
定时器精度:
QTimer
的精度取决于操作系统和系统的负载。 -
与
QThread::sleep()
的区别QTimer
(非阻塞): 使用QTimer
时,您的应用程序仍然可以响应其他事件(例如用户输入),因为定时器的工作依赖于事件循环。QThread::sleep()
(阻塞): 调用QThread::sleep()
会使当前线程暂停执行指定的时间,期间应用程序无法响应任何事件,导致界面冻结。
-
使用场景
用途 描述 周期性更新 UI 例如,每隔一段时间更新显示的时间、动画的帧等。 延迟执行操作 例如,在用户完成某个操作后延迟一段时间再执行下一步。 实现超时机制 例如,在等待网络响应时设置一个超时时间。 轮询数据 例如,定期检查服务器是否有新的数据。 动画效果 通过定时器不断触发重绘事件来创建动画效果。
测试代码-QTimer
#include <QCoreApplication>
#include <QTimer>
#include <QDebug>
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
// ======================
// 单次触发定时器示例
// ======================
qDebug() << "--- 单次触发定时器示例 ---";
QTimer::singleShot(3000, []() {
qDebug() << "3 秒后执行一次!";
});
qDebug() << "单次触发定时器已启动,将在 3 秒后输出消息。";
// ======================
// 重复触发定时器示例
// ======================
qDebug() << "\n--- 重复触发定时器示例 ---";
int counter = 0;
QTimer *repeatingTimer = new QTimer(); // 使用 new 创建,避免局部变量在 lambda 中失效
QObject::connect(repeatingTimer, &QTimer::timeout, [&]() {
qDebug() << "每隔 1 秒触发一次,当前计数:" << ++counter;
if (counter >= 3) {
repeatingTimer->stop();
qDebug() << "重复触发定时器已停止。";
QTimer::singleShot(100, [&]() { // 稍作延迟后退出,确保输出完整
QCoreApplication::quit();
});
}
});
repeatingTimer->start(1000); // 设置时间间隔为 1000 毫秒 (1 秒)
qDebug() << "重复触发定时器已启动,每隔 1 秒输出消息,计数到 3 次后停止。";
// ======================
// 启动应用程序事件循环
// ======================
return app.exec();
}
// ======================
// console
// ======================
//--- 单次触发定时器示例 ---
//单次触发定时器已启动,将在 3 秒后输出消息。
//--- 重复触发定时器示例 ---
//重复触发定时器已启动,每隔 1 秒输出消息,计数到 3 次后停止。
//每隔 1 秒触发一次,当前计数: 1
//每隔 1 秒触发一次,当前计数: 2
//3 秒后执行一次!
//每隔 1 秒触发一次,当前计数: 3
//重复触发定时器已停止。
QMetaObject:
QMetaObject
类是 Qt 框架中元对象系统的核心组成部分。它提供了对 Qt 类在运行时进行自省的能力,使得 Qt 能够实现诸如信号与槽、属性系统、动态属性、以及对象序列化等高级特性。
-
运行时类型信息 (RTTI)
-
类名 (Class Name)
QMetaObject
存储了类的完整名称。- 通过
QMetaObject::className()
方法可以获取到这个名称。
-
父类 (Parent Class)
QMetaObject
包含一个指向其直接父类的QMetaObject
对象的指针。- 这形成了类的继承层次结构,允许 Qt 在运行时遍历类的继承链。
- 通过
QMetaObject::superClass()
方法可以获取到父类的QMetaObject
。
-
3. 信号 (Signals)
QMetaObject
存储了以下信息:- 信号名称: 不包括参数列表。
- 参数类型: 每个参数的类型信息。
- 参数数量: 信号的参数个数。
- 信号索引: 信号在其信号表中的唯一索引。
- 信号签名: 包含信号名称和参数类型的完整签名(通常用于连接信号和槽)。
- 通过下面方法访问:
QMetaObject::signalCount()
,QMetaObject::signal(int index)
,QMetaObject::indexOfSignal()
-
4. 槽 (Slots)
QMetaObject
存储了以下信息:- 槽函数名称: 不包括参数列表。
- 参数类型: 每个参数的类型信息。
- 参数数量: 槽函数的参数个数。
- 槽函数索引: 槽函数在其槽表中的唯一索引。
- 槽函数签名: 包含槽函数名称和参数类型的完整签名。
- 访问修饰符: 指示槽函数是
public
、protected
还是private
。
- 通过下面方法访问:
QMetaObject::slotCount()
,QMetaObject::slot(int index)
,QMetaObject::indexOfSlot()
-
属性 (Properties)
-
Q_PROPERTY
宏声,QMetaObject
存储了以下信息:- 属性名称: 属性的名称。
- 数据类型: 属性的数据类型。
- 可读性: 指示属性是否可读(通常通过
READ
方法指定)。 - 可写性: 指示属性是否可写(通常通过
WRITE
方法指定)。 - Notifier 信号: 如果属性有
NOTIFY
信号,则存储该信号的元方法索引。 - Designable/Scriptable 标志: 是否可见或是否可以被脚本语言访问。
- User 属性标志: 指示属性是否为用户可见的属性。
- 调用方法:
QMetaObject::propertyCount()
QMetaObject::property(int index)
QMetaObject::indexOfProperty()
-
-
方法 (Methods)
-
Q_INVOKABLE
宏QMetaObject
存储了以下信息:- 方法名称: 不包括参数列表。
- 参数类型: 每个参数的类型信息。
- 返回值类型: 方法的返回值类型。
- 方法索引: 方法在其方法表中的唯一索引。
- 方法签名: 包含方法名称和参数类型的完整签名。
- 方法类型: 指示方法是信号、槽还是普通的
Q_INVOKABLE
方法。
- 调用方法
-
QMetaObject::methodCount()
-
QMetaObject::method(int index)
-
QMetaObject::indexOfMethod()
-
-
-
枚举 (Enums)
- 使用
enum
或enum class
,QMetaObject
存储了以下信息:- 枚举名称: 枚举类型的名称。
- 作用域: 指示枚举是类作用域还是全局作用域。
- 枚举值: 枚举中每个枚举常量的名称及其对应的整数值。
- 是否为 Flag 类型: 使用了
Q_FLAG
宏标记,一个可以进行位运算的标志类型。
- 获取方法
-
QMetaObject::enumeratorCount()
, QMetaObject::enumerator(int index)
,QMetaObject::indexOfEnumerator()
-
- 使用
-
测试代码-运行时类型信息
#include <QCoreApplication>
#include <QObject>
#include <QDebug>
#include <QMetaMethod>
#include <QMetaProperty>
#include <QMetaObject>
#include <QMetaEnum>
#include <QTimer>
// ======================
// MyClass 类定义
// ======================
class MyClass : public QObject {
Q_OBJECT
public:
enum MyEnum {
Value1 = 1,
Value2 = 2,
Value3 = 4
};
Q_ENUM(MyEnum)
Q_PROPERTY(int myProperty READ getMyProperty WRITE setMyProperty NOTIFY myPropertyChanged)
explicit MyClass(QObject *parent = nullptr) : QObject(parent), m_myProperty(0) {}
int getMyProperty() const { return m_myProperty; }
void setMyProperty(int value) {
if (m_myProperty != value) {
m_myProperty = value;
emit myPropertyChanged(m_myProperty);
}
}
public slots:
void mySlot(int value) {
qDebug() << "MySlot received:" << value;
}
void anotherSlot(const QString& text) {
qDebug() << "AnotherSlot received:" << text;
}
void overloadedSlot(int value) {
qDebug() << "OverloadedSlot (int) received:" << value;
}
void overloadedSlot(const QString& text) {
qDebug() << "OverloadedSlot (QString) received:" << text;
}
Q_INVOKABLE void myMethod(bool flag) {
qDebug() << "MyMethod called with flag:" << flag;
}
signals:
void mySignal(QString message);
void anotherSignal(int code, bool status);
void myPropertyChanged(int newValue);
private:
int m_myProperty; // 属性值
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
// 实例化 MyClass
MyClass obj;
const QMetaObject *metaObject = obj.metaObject(); // 使用实例的 metaObject
// ======================
// 1. 类名
// ======================
qDebug() << "--- 类名 ---";
qDebug() << "Class Name:" << metaObject->className();
// ======================
// 2. 父类
// ======================
qDebug() << "\n--- 父类 ---";
if (metaObject->superClass()) {
qDebug() << "Super Class Name:" << metaObject->superClass()->className();
} else {
qDebug() << "No super class.";
}
// ======================
// 3. 信号列表 Qt 4
// ======================
qDebug() << "\n--- 信号列表 ---";
int signalCount = 0;
for (int i = 0; i < metaObject->methodCount(); ++i) {
if (metaObject->method(i).methodType() == QMetaMethod::Signal) {
signalCount++;
}
}
qDebug() << "Number of Signals:" << signalCount;
for (int i = 0; i < metaObject->methodCount(); ++i) {
QMetaMethod method = metaObject->method(i);
if (method.methodType() == QMetaMethod::Signal) {
qDebug() << " Index:" << i << ", Name:" << method.name() << ", Signature:" << method.methodSignature();
}
}
// ======================
// 4. 槽列表
// ======================
int slotCount = 0;
for (int i = 0; i < metaObject->methodCount(); ++i) {
if (metaObject->method(i).methodType() == QMetaMethod::Slot) {
slotCount++;
}
}
qDebug() << "Number of Slots:" << slotCount;
for (int i = 0; i < metaObject->methodCount(); ++i) {
QMetaMethod method = metaObject->method(i);
if (method.methodType() == QMetaMethod::Slot) {
qDebug() << " Index:" << i << ", Name:" << method.name() << ", Signature:" << method.methodSignature();
}
}
// ======================
// 5. 属性列表
// ======================
qDebug() << "\n--- 属性列表 ---";
int propertyCount = metaObject->propertyCount();
qDebug() << "Number of Properties:" << propertyCount;
for (int i = 0; i < propertyCount; ++i) {
QMetaProperty property = metaObject->property(i);
qDebug() << " Index:" << i << ", Name:" << property.name() << ", Type:" << property.typeName()
<< ", Readable:" << property.isReadable() << ", Writable:" << property.isWritable();
}
// ======================
// 6. 方法列表
// ======================
qDebug() << "\n--- 方法列表 ---";
int methodCount = metaObject->methodCount();
qDebug() << "Number of Methods:" << methodCount;
for (int i = 0; i < methodCount; ++i) {
QMetaMethod method = metaObject->method(i);
QString type;
// if (method.methodType() == QMetaMethod::Method)
{
qDebug() << " Index:" << i << ", Type:" << method.methodType() << ", Name:" << method.name() << ", Signature:" << method.methodSignature();
}
}
// ======================
// 7. 枚举列表
// ======================
qDebug() << "\n--- 枚举列表 ---";
int enumCount = metaObject->enumeratorCount();
qDebug() << "Number of Enums:" << enumCount;
for (int i = 0; i < enumCount; ++i) {
QMetaEnum metaEnum = metaObject->enumerator(i);
qDebug() << " Name:" << metaEnum.name() << ", Scope:" << metaEnum.scope();
qDebug() << " Keys and Values:";
for (int j = 0; j < metaEnum.keyCount(); ++j) {
qDebug() << " " << metaEnum.key(j) << "=" << metaEnum.value(j);
}
}
// ======================
// 8. 测试功能
// ======================
qDebug() << "\n--- 测试功能 ---";
obj.setMyProperty(42); // 测试属性
QObject::connect(&obj, &MyClass::mySignal, &obj, &MyClass::anotherSlot);
emit obj.mySignal("Test Signal"); // 测试信号和槽
QTimer::singleShot(1000, [&obj]() {
obj.myMethod(true); // 测试 Q_INVOKABLE 方法
QCoreApplication::quit(); // 1 秒后退出
});
// ======================
// 启动应用程序事件循环
// ======================
return a.exec();
}
#include "main.moc" // 包含 moc 生成的文件
// ======================
// console
// ======================
//--- 类名 ---
//Class Name: MyClass
//--- 父类 ---
//Super Class Name: QObject
//--- 信号列表 ---
//Number of Signals: 6
// Index: 0 , Name: "destroyed" , Signature: "destroyed(QObject*)"
// Index: 1 , Name: "destroyed" , Signature: "destroyed()"
// Index: 2 , Name: "objectNameChanged" , Signature: "objectNameChanged(QString)"
// Index: 5 , Name: "mySignal" , Signature: "mySignal(QString)"
// Index: 6 , Name: "anotherSignal" , Signature: "anotherSignal(int,bool)"
// Index: 7 , Name: "myPropertyChanged" , Signature: "myPropertyChanged(int)"
//Number of Slots: 7
// Index: 3 , Name: "deleteLater" , Signature: "deleteLater()"
// Index: 4 , Name: "_q_reregisterTimers" , Signature: "_q_reregisterTimers(void*)"
// Index: 8 , Name: "mySlot" , Signature: "mySlot(int)"
// Index: 9 , Name: "anotherSlot" , Signature: "anotherSlot(QString)"
// Index: 10 , Name: "overloadedSlot" , Signature: "overloadedSlot(int)"
// Index: 11 , Name: "overloadedSlot" , Signature: "overloadedSlot(QString)"
// Index: 12 , Name: "myMethod" , Signature: "myMethod(bool)"
//--- 属性列表 ---
//Number of Properties: 2
// Index: 0 , Name: objectName , Type: QString , Readable: true , Writable: true
// Index: 1 , Name: myProperty , Type: int , Readable: true , Writable: true
//--- 方法列表 ---
//Number of Methods: 13
// Index: 0 , Type: 1 , Name: "destroyed" , Signature: "destroyed(QObject*)"
// Index: 1 , Type: 1 , Name: "destroyed" , Signature: "destroyed()"
// Index: 2 , Type: 1 , Name: "objectNameChanged" , Signature: "objectNameChanged(QString)"
// Index: 3 , Type: 2 , Name: "deleteLater" , Signature: "deleteLater()"
// Index: 4 , Type: 2 , Name: "_q_reregisterTimers" , Signature: "_q_reregisterTimers(void*)"
// Index: 5 , Type: 1 , Name: "mySignal" , Signature: "mySignal(QString)"
// Index: 6 , Type: 1 , Name: "anotherSignal" , Signature: "anotherSignal(int,bool)"
// Index: 7 , Type: 1 , Name: "myPropertyChanged" , Signature: "myPropertyChanged(int)"
// Index: 8 , Type: 2 , Name: "mySlot" , Signature: "mySlot(int)"
// Index: 9 , Type: 2 , Name: "anotherSlot" , Signature: "anotherSlot(QString)"
// Index: 10 , Type: 2 , Name: "overloadedSlot" , Signature: "overloadedSlot(int)"
// Index: 11 , Type: 2 , Name: "overloadedSlot" , Signature: "overloadedSlot(QString)"
// Index: 12 , Type: 2 , Name: "myMethod" , Signature: "myMethod(bool)"
//--- 枚举列表 ---
//Number of Enums: 1
// Name: MyEnum , Scope: MyClass
// Keys and Values:
// Value1 = 1
// Value2 = 2
// Value3 = 4
//--- 测试功能 ---
//AnotherSlot received: "Test Signal"
//MyMethod called with flag: true
-
信号与槽连接
-
运行时通过名称连接信号和槽
- 建立连接记录: 如果信号和槽被成功找到且类型匹配,Qt 会在内部维护一个连接记录,将发送者对象的特定信号与接收者对象的特定槽函数关联起来。
- 类型匹配:
QMetaObject
包含了信号和槽的参数类型信息。connect()
会在运行时检查信号的参数类型是否与槽的参数类型兼容。 - 查找信号和槽:
connect()
函数会获取发送者和接收者的QMetaObject
对象。
- 支持跨线程的队列连接
- 线程亲缘性 (Thread Affinity): 每个
QObject
都有一个线程亲缘性,它属于创建它的线程,或者通过moveToThread()
移动到的线程。 - 信号发射: 当一个信号被发射时,Qt 会检查信号的发送者和接收者是否位于同一个线程。
- 队列连接的触发: 如果发送者和接收者位于不同的线程,并且连接类型是
Qt::QueuedConnection
,Qt 就不会直接调用槽函数。 - 事件的创建: 相反,Qt 会使用
QMetaObject
中存储的关于信号的信息(包括信号的名称和参数类型),将信号的参数值封装成一个事件 (event)。 - 事件的投递: 这个事件会被投递 (posted) 到接收者对象所关联的线程的事件队列 (event queue) 中。通常通过
QCoreApplication::postEvent()
完成。 - 事件循环的检索: 接收者对象所在的线程必须运行着一个事件循环 (
QEventLoop
)。 - 事件的分发和处理: 当接收者线程的事件循环检索到之前投递的
QMetaCallEvent
时,它会使用接收者对象的QMetaObject
中的信息,找到对应的槽函数,并在接收者线程的上下文中执行 (invoke) 该槽函数,并将事件中携带的参数传递给槽函数。
- 线程亲缘性 (Thread Affinity): 每个
-
-
属性系统
Q_PROPERTY
宏声明属性DataType
: 属性的数据类型。Name
: 属性的名称,在运行时通过这个名称访问属性。READ getterMethod
: 指定用于获取属性值的成员函数(通常是const
的)。WRITE setterMethod
(可选): 指定用于设置属性值的成员函数。NOTIFY signal
(可选): 指定当属性值改变时会发出的信号。- 其他关键字 (可选): 这些关键字提供了关于属性的额外元信息。
- 运行时通过名称访问和修改属性
-
QObject::property(const char *name) const
:- 接收一个属性名称的字符串作为参数。
QMetaObject
会查找具有该名称的属性的元数据。- 调用该属性声明中指定的
READ
方法(getter)来获取属性的当前值。 - 返回一个
QVariant
对象,该对象封装了属性的值。
-
QObject::setProperty(const char *name, const QVariant &value)
:- 接收一个属性名称的字符串和一个
QVariant
对象作为参数。 QMetaObject
会查找具有该名称的属性的元数据。- 如果找到,它会检查
value
的类型是否与属性的类型兼容。 - 调用该属性声明中指定的
WRITE
方法(setter)来设置属性的新值。 - 如果属性声明了
NOTIFY
信号,setProperty
方法通常会在成功设置属性值后发出该信号,通知其他对象属性已发生更改。 - 返回一个布尔值,指示属性是否成功设置。
- 接收一个属性名称的字符串和一个
-
- 属性元数据的访问
QObject::metaObject()
获取对象的QMetaObject
。QMetaObject::propertyCount()
获取属性的总数。QMetaObject::property(int index)
获取指定索引的QMetaProperty
对象,该对象包含了属性的名称、类型、是否可读写等信息。QMetaObject::indexOfProperty(const char *name)
获取指定名称属性的索引- QMetaProperty 提供方法:
- name(): 属性名。
- typeName(): 类型名。
- isReadable(): 是否可读。
- isWritable(): 是否可写。
- read(QObject*): 读取属性值。
- write(QObject*, QVariant): 设置属性值。
- notifySignal(): 获取通知信号。
测试代码-属性系统
#include <QCoreApplication>
#include <QObject>
#include <QMetaObject>
#include <QMetaProperty>
#include <QDebug>
#include <QTimer>
// ======================
// MyClass 类定义
// ======================
class MyClass : public QObject {
Q_OBJECT
// 定义属性
Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged)
Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
Q_PROPERTY(double score READ getScore WRITE setScore) // 无通知信号
public:
explicit MyClass(QObject *parent = nullptr) : QObject(parent), m_age(0), m_score(0.0) {}
// age 属性
int getAge() const { return m_age; }
void setAge(int value) {
if (m_age != value) {
m_age = value;
emit ageChanged(m_age);
}
}
// name 属性
QString getName() const { return m_name; }
void setName(const QString &value) {
if (m_name != value) {
m_name = value;
emit nameChanged(m_name);
}
}
// score 属性
double getScore() const { return m_score; }
void setScore(double value) { m_score = value; }
signals:
void ageChanged(int newAge);
void nameChanged(const QString &newName);
private:
int m_age; // 年龄属性
QString m_name; // 姓名属性
double m_score; // 分数属性
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
// 创建对象
MyClass obj;
// 获取元对象
const QMetaObject *metaObject = obj.metaObject();
// ======================
// 1. 打印属性信息
// ======================
qDebug() << "--- 属性列表 ---";
int propertyCount = metaObject->propertyCount();
qDebug() << "Number of Properties:" << propertyCount;
for (int i = 0; i < propertyCount; ++i) {
QMetaProperty property = metaObject->property(i);
qDebug() << " Index:" << i
<< ", Name:" << property.name()
<< ", Type:" << property.typeName()
<< ", Readable:" << property.isReadable()
<< ", Writable:" << property.isWritable()
<< ", Has Notify:" << property.hasNotifySignal();
if (property.hasNotifySignal()) {
qDebug() << " Notify Signal:" << property.notifySignal().methodSignature();
}
}
// ======================
// 2. 测试属性读写
// ======================
qDebug() << "\n--- 测试属性读写 ---";
// 设置初始值
obj.setAge(25);
obj.setName("Alice");
obj.setScore(95.5);
// 通过 QMetaProperty 读取属性
QMetaProperty ageProp = metaObject->property(metaObject->indexOfProperty("age"));
QMetaProperty nameProp = metaObject->property(metaObject->indexOfProperty("name"));
QMetaProperty scoreProp = metaObject->property(metaObject->indexOfProperty("score"));
qDebug() << "Initial Values:";
qDebug() << " Age:" << ageProp.read(&obj).toInt();
qDebug() << " Name:" << nameProp.read(&obj).toString();
qDebug() << " Score:" << scoreProp.read(&obj).toDouble();
// 通过 QMetaProperty 修改属性
ageProp.write(&obj, 30);
nameProp.write(&obj, "Bob");
scoreProp.write(&obj, 88.0);
qDebug() << "After Modification:";
qDebug() << " Age:" << ageProp.read(&obj).toInt();
qDebug() << " Name:" << nameProp.read(&obj).toString();
qDebug() << " Score:" << scoreProp.read(&obj).toDouble();
// ======================
// 3. 测试通知信号
// ======================
qDebug() << "\n--- 测试通知信号 ---";
QObject::connect(&obj, &MyClass::ageChanged, [](int newAge) {
qDebug() << "Signal: ageChanged emitted with value:" << newAge;
});
QObject::connect(&obj, &MyClass::nameChanged, [](const QString &newName) {
qDebug() << "Signal: nameChanged emitted with value:" << newName;
});
obj.setAge(35); // 触发 ageChanged 信号
obj.setName("Charlie"); // 触发 nameChanged 信号
obj.setScore(99.0); // 无信号
// ======================
// 退出程序
// ======================
QTimer::singleShot(1000, &app, &QCoreApplication::quit);
return app.exec();
}
#include "main.moc" // 包含 moc 生成的文件
// ======================
// console
// ======================
//--- 属性列表 ---
//Number of Properties: 4
// Index: 0 , Name: objectName , Type: QString , Readable: true , Writable: true , Has Notify: true
// Notify Signal: "objectNameChanged(QString)"
// Index: 1 , Name: age , Type: int , Readable: true , Writable: true , Has Notify: true
// Notify Signal: "ageChanged(int)"
// Index: 2 , Name: name , Type: QString , Readable: true , Writable: true , Has Notify: true
// Notify Signal: "nameChanged(QString)"
// Index: 3 , Name: score , Type: double , Readable: true , Writable: true , Has Notify: false
//--- 测试属性读写 ---
//Initial Values:
// Age: 25
// Name: "Alice"
// Score: 95.5
//After Modification:
// Age: 30
// Name: "Bob"
// Score: 88
//--- 测试通知信号 ---
//Signal: ageChanged emitted with value: 35
//Signal: nameChanged emitted with value: "Charlie"
-
动态方法调用
QMetaObject::invokeMethod()
函数object
(QObject):* 指向要调用方法的对象。method
(const char):* 要调用的方法的名称。type
(Qt::ConnectionType): 指定调用类型:Qt::AutoConnection:自动选择
Qt::DirectConnection
: 立即在调用者的线程中执行。Qt::QueuedConnection
: 跨线程队列。Qt::BlockingQueuedConnection
: 阻塞调用Qt::UniqueConnection
: 唯一的连接。
ret
(QGenericReturnArgument): 用于接收方法返回值的参数。returnVal
(QVariant): 接受QVariant
类型的返回值。val0
到val9
(QGenericArgument 或 QVariant): 最多可以传递 10 个参数。-
函数返回
true
如果方法被成功调用,否则返回false
。
-
QMetaObject::invokeMethod()
工作流程- 获取
QMetaObject
对象。 - 查找方法:。
- 类型转换和参数准备。
- 方法调用。
- 返回值处理。
- 获取
-
类型转换
- 方法 static T QMetaObject::cast(QObject *obj)
T
: 目标指针类型,您希望将obj
转换成的类型。obj
: 要进行类型转换的QObject
指针。- 返回指向该对象的
T
类型指针或返回nullptr
。
QMetaObject::cast()
的工作原理- 获取目标类型
T
的QMetaObject
对象(通常通过T::staticMetaObject
)。 - 从要转换的
QObject
指针obj
获取其QMetaObject
对象 (obj->metaObject()
). - 遍历
obj
的QMetaObject
继承链(通过superClass()
方法),检查在链中是否存在与目标类型T
的QMetaObject
相同的对象。 - 如果找到匹配的
QMetaObject
,则说明obj
指向的对象是类型T
或其派生类的一个实例,可以安全地转换为T*
。 - 如果没有找到匹配的
QMetaObject
,则转换是不安全的,cast()
返回nullptr
。
- 获取目标类型
-
上转型 (Upcasting) 和下转型 (Downcasting)
- 上转型 (Derived to Base): 将派生类指针转换为基类指针是安全的。
- 下转型 (Base to Derived): 将基类指针转换为派生类指针是不安全的。
- 方法 static T QMetaObject::cast(QObject *obj)
测试代码-类型转换
#include <QCoreApplication>
#include <QObject>
#include <QDebug>
// ======================
// Base 类定义
// ======================
class Base : public QObject {
Q_OBJECT
public:
explicit Base(QObject *parent = nullptr) : QObject(parent) {}
virtual void baseFunction() {
qDebug() << "Base: baseFunction called";
}
};
// ======================
// Derived 类定义
// ======================
class Derived : public Base {
Q_OBJECT
public:
explicit Derived(QObject *parent = nullptr) : Base(parent) {}
void derivedFunction() {
qDebug() << "Derived: derivedFunction called";
}
void baseFunction() override {
qDebug() << "Derived: baseFunction (overridden) called";
}
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
// 创建基类和派生类对象
Base baseObj;
Derived derivedObj;
// ======================
// 1. 上转型 (Derived* -> Base*) - 总是安全的
// ======================
Derived *derivedPtr = &derivedObj;
Base *upcastedPtr = qobject_cast<Base*>(derivedPtr);
qDebug() << "--- Upcasting ---";
qDebug() << "Derived pointer:" << derivedPtr;
if (upcastedPtr) {
qDebug() << "Upcasting successful. Base pointer:" << upcastedPtr;
upcastedPtr->baseFunction(); // 调用重写后的方法(多态性)
} else {
qDebug() << "Upcasting failed (should not happen).";
}
// ======================
// 2. 下转型 (Base* -> Derived*) - 成功的情况
// ======================
Base *basePtr1 = &derivedObj; // 基类指针指向派生类对象
Derived *downcastedPtr1 = qobject_cast<Derived*>(basePtr1);
qDebug() << "\n--- Downcasting (Successful) ---";
qDebug() << "Base pointer:" << basePtr1;
if (downcastedPtr1) {
qDebug() << "Downcasting successful. Derived pointer:" << downcastedPtr1;
downcastedPtr1->derivedFunction(); // 调用派生类特有方法
downcastedPtr1->baseFunction(); // 调用重写后的方法
} else {
qDebug() << "Downcasting failed.";
}
// ======================
// 3. 下转型 (Base* -> Derived*) - 失败的情况
// ======================
Base *basePtr2 = &baseObj; // 基类指针指向基类对象
Derived *downcastedPtr2 = qobject_cast<Derived*>(basePtr2);
qDebug() << "\n--- Downcasting (Failed) ---";
qDebug() << "Base pointer:" << basePtr2;
if (downcastedPtr2) {
qDebug() << "Downcasting successful (should not happen). Derived pointer:" << downcastedPtr2;
downcastedPtr2->derivedFunction();
} else {
qDebug() << "Downcasting failed (as expected).";
}
return 0; // 无需事件循环,直接退出
}
#include "main.moc" // 包含 moc 生成的文件
// ======================
// console
// ======================
//--- Upcasting ---
//Derived pointer: Derived(0x7fffffffe130)
//Upcasting successful. Base pointer: Derived(0x7fffffffe130)
//Derived: baseFunction (overridden) called
//--- Downcasting (Successful) ---
//Base pointer: Derived(0x7fffffffe130)
//Downcasting successful. Derived pointer: Derived(0x7fffffffe130)
//Derived: derivedFunction called
//Derived: baseFunction (overridden) called
//--- Downcasting (Failed) ---
//Base pointer: Base(0x7fffffffe120)
//Downcasting failed (as expected).
-
自我检查
- 获取
QMetaObject
对象- 通过对象实例: 可以使用
obj->metaObject()
方法来获取指向该对象所属类的QMetaObject
的指针。 - 通过类名: 可以使用静态成员
ClassName::staticMetaObject
来获取该类的QMetaObject
对象。
- 通过对象实例: 可以使用
-
获取类名:使用
className()
方法获取类的名称(返回const char*
): -
获取信号列表(Qt 4)
- 获取信号数量: 使用
signalCount()
方法获取类中定义的信号总数。 - 遍历信号: 使用
signal(int index)
方法获取指定索引处的信号的元数据,返回一个QMetaMethod
对象。 - 获取信号名称和签名: 可以使用
name()
方法获取信号的名称,methodSignature()
方法获取包含参数类型的完整签名。 - 查找特定信号的索引: 使用
indexOfSignal(const QByteArray &signalSignature)
方法可以通过信号签名查找其索引。
- 获取信号数量: 使用
-
获取槽列表(Qt 4)
- 获取槽数量: 使用
slotCount()
方法获取类中定义的槽函数总数。 - 遍历槽: 使用
slot(int index)
方法获取指定索引处的槽函数的元数据,返回一个QMetaMethod
对象。 - 获取槽名称和签名: 可以使用
name()
方法获取槽函数的名称,methodSignature()
方法获取包含参数类型的完整签名。 - 查找特定槽的索引: 使用
indexOfSlot(const QByteArray &slotSignature)
方法可以通过槽函数签名查找其索引。
- 获取槽数量: 使用
-
获取方法列表 (包括信号和槽)
- 获取方法总数: 使用
methodCount()
方法获取类中定义的所有方法(包括信号、槽和普通的Q_INVOKABLE
方法)的总数。 - 遍历方法: 使用
method(int index)
方法获取指定索引处的方法的元数据,返回一个QMetaMethod
对象。 - 获取方法名称和类型: 可以使用
name()
方法获取方法的名称,使用methodType()
方法获取方法的类型(QMetaMethod::Signal
,QMetaMethod::Slot
,QMetaMethod::Method
)。
- 获取方法总数: 使用
- 获取
注:部分接口可能有更新!!!
四. 多线程和并发 (Multithreading and Concurrency)
-
QThread
QThread
提供了线程管理的功能,允许开发者创建和管理线程。 -
同步工具QMutex、QReadWriteLock、QSemaphore、QWaitCondition
QMutex:互斥锁,确保线程安全。
QReadWriteLock:读写锁,适用于多读少写场景。
QSemaphore:信号量,控制资源访问。
QWaitCondition:线程等待条件。
-
QThreadPool 和 QRunnable
线程池管理。
-
原子操作
QAtomicInt 和 QAtomicPointer 提供线程安全的原子操作。
-
QtConcurrent
QtConcurrent
提供了对并行计算的高层次抽象。它允许开发者简洁地进行多线程任务处理,如并行遍历、过滤等操作。
五. 国际化和本地化 (Internationalization and Localization)
-
QLocale
QLocale
类用于管理语言、区域设置等本地化信息,能够根据不同的区域设置显示日期、时间、数字等格式。 -
QTranslator
QTranslator
用于加载翻译文件,以便将应用程序界面翻译成不同的语言。
六. 进程和进程间通信 (Process and Inter-Process Communication - IPC)
-
QProcess
QProcess
类用于启动外部进程并与其进行交互,支持进程输入输出流的管理。 -
QLocalSocket, QLocalServer
-
QLocalSocket
: 本地套接字,用于与本地进程进行通信。 -
QLocalServer
: 本地服务器,用于监听来自其他进程的连接请求。
-
-
QSharedMemory
QSharedMemory
提供了进程间共享内存的访问,允许多个进程共享同一块内存区域。
七. 插件支持 (Plugin Support)
-
QPluginLoader
QPluginLoader
用于加载动态插件,使得应用程序能够在运行时加载和卸载插件。 -
QFactoryInterface
QFactoryInterface
是插件接口,插件可以实现特定功能,并通过该接口供应用程序使用。
八. 文本编解码 (Text Codecs)
-
QTextCodec
QTextCodec
用于文本编码和解码,支持多种字符集,如 UTF-8、GBK、ISO-8859-1 等。
九. 动态库加载 (Dynamic Library Loading)
-
QLibrary
QLibrary
用于加载动态链接库,并提供访问其导出函数的能力,支持跨平台的动态库管理。
十. 全局函数
-
qAbs
计算绝对值。
-
qDebug、qWarning、qCritical
调试和日志输出。
-
qInstallMsgHandler
安装消息处理程序。
-
qVersion
返回 Qt 版本。
-
qRegisterResourceData 和 qUnregisterResourceData:
资源数据管理。
十一. 其他实用工具
-
QUrl
URL 解析和生成。
-
QRandomGenerator
线程安全的随机数生成。
-
QCryptographicHas
加密哈希类(如 MD5、SHA-256)。
-
QJsonDocument、QJsonArray、QJsonObject、QJsonValue
JSON 处理:
-
QRegularExpression
Perl 兼容的正则表达式。
-
QSettings
跨平台应用设置存储。
-
QStandardPaths
返回标准目录路径。
-
QDebug
调试输出工具。
-
QCalendar
支持多种日历系统。
-
QDeadlineTimer
截止时间管理。