一 Qt初识
暂时不写了
我的理解是类似于c#,是一个组件库,不局限是一个组件框架。
二 Qt Core
Qt Core 是 Qt 框架的基础模块,提供非 GUI 的核心功能:
核心类:
QObject
(信号槽机制)、QEvent
(事件处理)、QThread
(线程)数据序列化:
QDataStream
、QSettings
文件处理:
QFile
、QDir
、QFileInfo
时间日期:
QDateTime
、QTimer
容器类:
QList
、QVector
、QMap
等字符串处理:
QString
(Unicode)、QByteArray
(二进制数据)资源管理:
QResource
(嵌入资源文件)插件系统:
QPluginLoader
1. QObject
所有 Qt 对象的基类
支持信号槽、事件处理、对象树管理
所有的对象都继承于QObject(与java挺类似)
class MyObject : public QObject {
Q_OBJECT // 启用元对象系统
signals:
void mySignal();
};
2. QString
属于core,创建对象可以使用构造函数。或者直接Qstring str= “”;
特点:
存储 UTF-16 编码的 Unicode 字符串
隐式共享(Copy-on-Write)优化内存
自动处理内存分配
2.1 常用操作
QString str = "Hello, 世界!"; // 自动处理多语言
// 拼接
str.append(" 😊");
str = QString("%1 %2").arg("Qt", "6.5");
// 分割
QStringList list = str.split(",");
// 转换
std::string s = str.toStdString();
const char* cstr = str.toUtf8().constData();
// 查找/替换
if (str.contains("世界"))
str.replace("世界", "World");
2.2 常用函数
toInt(), toDouble():类型转换
trimmed():移除空白
toUpper()/toLower():大小写转换
sprintf():C 风格格式化(已弃用,推荐 arg())
append:字符串拼接
preappend:在前面拼接
arg: 参数 Qstring("xxx%1yyy%2").arg(1).arg(2);
contains:包含
isNull: 判Null
isEmpty:判Empty
startWith:以什么开始
endWith:以什么结束
indexOf:在那个位置
replace:替换
split:拆分
3. QByteArray
用途:
存储原始字节(
char*
)处理二进制数据(如图片、网络数据)
8-bit 文本(ASCII/Latin-1)
和Qstring的相互转换
new Qstirng(QByteArray)
Qstirng.toUtf8();
QByteArray data("\x48\x65\x6C\x6C\x6F", 5); // "Hello"
// 编码转换
QByteArray base64 = data.toBase64(); // "SGVsbG8="
QByteArray hex = data.toHex(); // "48656c6c6f"
// 与 QString 互转
QString str = QString::fromUtf8(data);
QByteArray utf8 = str.toUtf8();
// 直接访问内存
char* rawData = data.data();
4. 集合
Qt 提供类似 STL 但更易用的容器,支持 隐式共享。
(1) 顺序容器
类型 特点 示例 QList 动态数组(最优通用容器) QList<int> list = {1,2}
QVector 连续内存(Qt6 中同 QList) QVector<QString> vec;
QStack LIFO(继承自 QVector) stack.push(10);
QQueue FIFO(继承自 QList) queue.enqueue("A");
QList<QString> fruits = {"Apple", "Banana"};
fruits.prepend("Mango"); // 头部添加
fruits.removeLast(); // 删除尾部
(2) 关联容器
类型 特点 示例 QMap 有序键值对(红黑树) QMap<QString, int> map;
QHash 哈希表(更快查找) QHash<int, QString> hash;
QSet 无序唯一值集合 QSet<QString> set;
QMap<QString, int> ages;
ages["Alice"] = 30;
ages.insert("Bob", 25);
if (ages.contains("Alice"))
qDebug() << ages.value("Alice"); // 30
(3) 迭代器
// Java 风格迭代器
QListIterator<QString> it(list);
while (it.hasNext())
qDebug() << it.next();
// STL 风格迭代器(更快)
QList<QString>::const_iterator stlIt;
for (stlIt = list.begin(); stlIt != list.end(); ++stlIt)
qDebug() << *stlIt;
5. 其他核心类
QVariant:万能数据类型容器
QVariant v1 = 42; // int
QVariant v2 = "Qt"; // QString
int i = v1.toInt(); // 42
QObject:所有 Qt 对象的基类
支持信号槽、事件处理、对象树管理
class MyObject : public QObject {
Q_OBJECT // 启用元对象系统
signals:
void mySignal();
};
QFile:文件 I/O
QFile file("data.txt");
if (file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readAll();
}
5. 日期,定时器,线程等
5.1 日期
类名 描述 示例 QDate 处理日期(年月日) QDate(2023, 12, 31)
QTime 处理时间(时分秒毫秒) QTime(14, 30, 45, 500)
QDateTime 组合日期和时间(含时区支持) QDateTime::currentDateTime()
(2) 关键操作
// 获取当前时间
QDateTime now = QDateTime::currentDateTime();
// 日期运算
QDateTime tomorrow = now.addDays(1);
QDateTime nextHour = now.addSecs(3600);
// 格式化输出
qDebug() << "ISO格式: " << now.toString(Qt::ISODate); // 2023-12-31T14:30:45
qDebug() << "自定义格式: " << now.toString("yyyy-MM-dd hh:mm:ss.zzz");
// 时间差计算
QDateTime deadline(QDate(2024, 1, 1), QTime(0, 0));
qint64 secsRemaining = QDateTime::currentSecsTo(deadline);
// 时区转换
QDateTime utcTime = now.toUTC();
QDateTime localTime = utcTime.toLocalTime();
(3) 实用技巧
// 计算程序耗时
QElapsedTimer timer;
timer.start();
// ... 执行操作 ...
qDebug() << "耗时:" << timer.elapsed() << "毫秒";
// 周/月计算
QDate today = QDate::currentDate();
qDebug() << "本周第一天:" << today.addDays(1 - today.dayOfWeek());
qDebug() << "本月天数:" << today.daysInMonth();
5.2 定时器
(1) 定时器类型
类型 描述 触发方式 单次定时器 触发一次后自动停止 setSingleShot(true)
间隔定时器 按固定间隔重复触发 start(interval)
精确定时器 高精度定时(Qt.PreciseTimer) setTimerType()
(2) 基本用法
// 创建定时器
QTimer *timer = new QTimer(this);
// 连接信号槽
connect(timer, &QTimer::timeout, []() {
qDebug() << "定时触发!";
});
// 启动定时器(1秒间隔)
timer->start(1000);
// 停止定时器
timer->stop();
(3) 高级用法
// 单次延时执行
QTimer::singleShot(2000, []() {
qDebug() << "2秒后执行";
});
// 多定时器管理
QTimer *timer1 = new QTimer(this);
QTimer *timer2 = new QTimer(this);
// 使用不同精度
timer1->setTimerType(Qt::PreciseTimer); // 毫秒级精度
timer2->setTimerType(Qt::CoarseTimer); // 5%误差容忍
// 定时器ID管理
int timerId = startTimer(500); // 需重写timerEvent()
5.3 线程
(1) 线程创建方式
方式 适用场景 特点 继承QThread 简单任务 重写run()方法 moveToThread 复杂对象(推荐) 利用事件循环 QtConcurrent 并行计算 无需显式管理线程
(2) 继承QThread示例
class WorkerThread : public QThread {
Q_OBJECT
protected:
void run() override {
// 线程执行体
for(int i=0; i<10; i++) {
qDebug() << "Working..." << i;
sleep(1);
emit progress(i*10);
}
}
signals:
void progress(int percent);
};
// 使用线程
WorkerThread *thread = new WorkerThread;
connect(thread, &WorkerThread::progress, this, &MainWindow::updateProgress);
connect(thread, &WorkerThread::finished, thread, &QObject::deleteLater);
thread->start();
(3) moveToThread 示例(推荐)
class Worker : public QObject {
Q_OBJECT
public slots:
void doWork() {
// 耗时任务
emit resultReady(42);
}
signals:
void resultReady(int result);
};
// 在主线程创建
Worker *worker = new Worker;
QThread *thread = new QThread;
// 移动对象到新线程
worker->moveToThread(thread);
// 连接信号
connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &MainWindow::handleResult);
connect(worker, &Worker::resultReady, thread, &QThread::quit);
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();
(4) 线程同步机制
类名 用途 示例 QMutex 互斥锁 QMutexLocker locker(&mutex)
QReadWriteLock 读写锁 locker.lockForRead()
QSemaphore 信号量 semaphore.acquire(3)
QWaitCondition 条件变量 condition.wait(&mutex)
// 互斥锁示例
QMutex mutex;
void ThreadSafeFunction() {
QMutexLocker locker(&mutex); // 自动加锁/解锁
// 临界区操作
}
// 条件变量示例
QWaitCondition condition;
QMutex mutex;
// 等待线程
mutex.lock();
condition.wait(&mutex); // 释放mutex并等待
mutex.unlock();
// 唤醒线程
condition.wakeOne(); // 唤醒一个线程
condition.wakeAll(); // 唤醒所有线程
(5) 线程间通信
// 跨线程信号槽(自动排队)
connect(worker, &Worker::dataReady,
guiObject, &GUI::updateDisplay,
Qt::QueuedConnection); // 显式指定排队连接
// 使用事件传递复杂数据
class CustomEvent : public QEvent {
public:
CustomEvent(const Data &d) : QEvent(User), data(d) {}
Data data;
};
// 发送事件
QCoreApplication::postEvent(receiver, new CustomEvent(data));
5.4 并发框架(QtConcurrent)
(1) 并行处理模式
(2) 线程池管理
三 槽(Slots)& 信号(Signals)& 连接(connect)
即为,连接(信号发送方,发送的信号,信号的接收者,信号的处理)
连接:即为链接这些个操作的函数
信号发送方:即,要执行操作的初始方(button)
发送的信号:即,要执行的初始操作(click,release)
信号的接收者:即接收信号并处理信号的函数(click后执行的,release和后的操作)
信号的处理(处理的槽函数):系统预置
// 关闭窗口
链接 发送者按钮 要执行的事件 接受 处理方式(槽的处理)
connect(btn, &QPushButton::clicked, this, &MainWindow::close);
1. 槽的本质与特性
本质:普通 C++ 成员函数,但需在类声明中使用
slots
关键字标识核心特性:
可被信号触发执行
可具有返回值(但通常被忽略)
支持任意参数类型(需与连接信号匹配)
可声明为虚函数
支持访问控制(public/protected/private)
class MyObject : public QObject {
Q_OBJECT
public:
explicit MyObject(QObject *parent = nullptr);
public slots: // 公共槽(可被外部连接)
void processData(const QByteArray &data);
private slots: // 私有槽(仅限类内部连接)
void internalCleanup();
protected slots: // 受保护槽(子类可访问)
virtual void updateState();
};
2. 槽的声明与定义
(1) 声明位置
class MyClass : public QObject {
Q_OBJECT // 必须包含此宏
public slots:
void slot1(); // 无参数槽
QString slot2(int param); // 带参数槽
signals:
void dataProcessed(); // 信号声明
};
(2) 定义实现
void MyClass::slot1() {
qDebug() << "Slot1 called";
}
QString MyClass::slot2(int param) {
return QString("Received: %1").arg(param);
}
3. 信号与槽的连接
(1) 基本连接方式
// 语法:connect(发送者, 信号, 接收者, 槽)
connect(button, &QPushButton::clicked,
processor, &DataProcessor::startProcessing);
(2) 连接类型
连接类型 | 行为描述 | 适用场景 |
---|---|---|
Qt::AutoConnection | 自动选择(同线程=Direct,跨线程=Queued) | 默认选项(推荐) |
Qt::DirectConnection | 立即在发送者线程调用槽 | 单线程应用 |
Qt::QueuedConnection | 将调用加入接收者线程事件队列 | 跨线程通信(最安全) |
Qt::BlockingQueuedConnection | 阻塞发送者线程直到槽执行完毕 | 线程同步(需谨慎) |
Qt::UniqueConnection | 确保相同对象间不重复连接 | 防止重复连接 |
// 显式指定连接类型
connect(sender, &Sender::signal,
receiver, &Receiver::slot,
Qt::QueuedConnection);
(3) Lambda 表达式作为槽
connect(button, &QPushButton::clicked, [=]() {
qDebug() << "Button clicked at" << QTime::currentTime();
// 可捕获局部变量 [=]或[&]
});
4. 槽的高级用法
(1) 重载槽的处理
// 使用静态转型解决重载歧义
connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MyClass::onIndexChanged);
// C++14 风格
connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged),
this, &MyClass::onIndexChanged);
(2) 槽的参数处理
// 槽可接收比信号更少的参数(多余参数被忽略)
connect(networkManager, &NetworkManager::dataReceived, // 信号:dataReceived(QByteArray, QDateTime)
logger, &Logger::logData); // 槽:logData(QByteArray)
// 使用 QSignalMapper(旧版)或 Lambda 处理参数转换
connect(buttonGroup, QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked),
[=](QAbstractButton *btn) {
handleButtonClick(btn->text());
});
(3) 槽的自动连接(UI 文件)
在 Qt Designer 中命名槽为
on_对象名_信号名
可实现自动连接:
// 自动连接条件:
// 槽名:on_buttonSubmit_clicked()
// 对象名:buttonSubmit
// 信号:clicked()
5. 槽的生命周期管理
(1) 连接断开
// 手动断开特定连接
QMetaObject::Connection conn = connect(...);
disconnect(conn);
// 断开对象所有连接
disconnect(sender, nullptr, receiver, nullptr);
disconnect(receiver); // 断开接收者所有槽
(2) 自动清理
// 接收者销毁时自动断开连接
connect(sender, &Sender::signal,
receiver, &Receiver::slot);
// 当receiver被delete时连接自动断开
// 使用上下文对象管理Lambda生命周期
connect(sender, &Sender::signal,
contextObject, [=](){ /* ... */ });
6. 槽与线程安全
(1) 跨线程通信模式
(2) 最佳实践
// Worker对象在工作线程
Worker *worker = new Worker;
worker->moveToThread(workerThread);
// 主线程到工作线程的连接
connect(this, &Controller::startWork, // 主线程信号
worker, &Worker::doWork, // 工作线程槽
Qt::QueuedConnection); // 必须指定排队连接
// 工作线程到主线程的连接
connect(worker, &Worker::resultReady, // 工作线程信号
this, &Controller::handleResult, // 主线程槽
Qt::QueuedConnection); // 自动排队
7. 槽的性能优化
减少连接数量:
使用单个槽处理多个信号
使用
QSignalMapper
(Qt5)或 Lambda(推荐)
避免阻塞槽:
void MainWindow::onDataReceived() {
// 错误:阻塞事件循环
processHugeData(); // 耗时操作
// 正确:移入工作线程
QtConcurrent::run(this, &MainWindow::processHugeData);
}
使用直接连接(同线程):
connect(model, &DataModel::dataChanged,
view, &DataView::refresh,
Qt::DirectConnection); // 减少事件队列开销
8. 常见问题与解决方案
问题1:槽未触发
检查点:
1. 类声明是否包含 Q_OBJECT 宏?
2. 是否调用了 QCoreApplication::exec()?
3. 连接是否成功建立?(connect 返回 QMetaObject::Connection)
4. 发送者/接收者是否已被销毁?
问题2:跨线程崩溃
// 错误:跨线程访问GUI
void WorkerThread::run() {
label->setText("Done"); // 崩溃!
}
// 正确:通过信号更新
emit updateGUI("Done"); // 连接至主线程槽
问题3:内存泄漏
// Lambda捕获this导致泄漏
connect(button, &QPushButton::clicked, [this]() {
this->process(); // 若this先于button销毁...
});
// 解决方案:使用弱引用
connect(button, &QPushButton::clicked, [weakThis = QPointer(this)]() {
if (weakThis) weakThis->process();
});
9. 槽的最佳实践总结
命名规范:
使用
on_ObjectName_SignalName()
实现自动连接常规槽使用动词描述行为(如
processData()
)
访问控制:
公有槽:供外部对象连接
私有槽:内部实现细节
线程规则:
GUI操作只在主线程槽中执行
耗时操作在工作线程槽中执行
资源管理:
使用
QPointer
或上下文对象管理接收者生命周期及时断开不再需要的连接
性能关键路径:
避免在频繁触发的槽中执行复杂操作
使用
QElapsedTimer
监控槽执行时间
总结:槽的核心价值
松耦合通信:对象间无需相互引用
类型安全:编译时检查参数匹配
线程安全:跨线程通信内置支持
灵活组合:一个信号可连接多个槽,一个槽可响应多个信号
元对象集成:支持动态连接/断开
6. 官方文档
四 事件(Events)
1. 事件系统核心概念
事件(Event)是Qt框架中处理用户输入、系统通知和应用程序内部通信的基础机制。与信号槽机制不同,事件系统提供了更底层的控制能力。
事件系统关键特性:
事件对象:所有事件继承自
QEvent
,包含事件类型和相关信息事件传递:通过事件队列(event loop)分发
事件处理:对象通过重写事件处理器(event handler)响应事件
事件过滤:支持在事件到达目标对象前进行拦截
五 核心组件关系图
┌────────────┐ ┌────────────┐ ┌──────────────┐ │ QTimer │───> │ QThread │<───>│ QtConcurrent │ └────────────┘ └────────────┘ └──────────────┘ │ │ │ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ QDateTime ├────>│ 信号槽系统 │<────│ 线程池管理 │ └────────────┘ └────────────┘ └──────────────┘ │ ▼ ┌──────────────┐ │ 同步原语 │ │ (QMutex等) │ └──────────────┘