Qt 核心概念
Qt 框架有一些独特的设计理念和核心机制,理解这些概念是掌握 Qt 开发的关键。
元对象系统(Meta-Object System)
Qt 的元对象系统是其最核心的特性之一,它为 C++ 添加了反射、信号槽、属性系统等动态特性。
元对象编译器(moc)
Qt 使用 moc(Meta-Object Compiler)在编译时处理特殊的 C++ 代码:
class MyClass : public QObject
{
Q_OBJECT // 这个宏会触发 moc 处理
public:
MyClass(QObject *parent = nullptr);
signals:
void mySignal(); // 信号声明
public slots:
void mySlot(); // 槽声明
};
编译流程:
头文件 (.h)
│
▼
moc 编译器 ──► moc_xxx.cpp(生成的元对象代码)
│
▼
C++ 编译器 ──► 目标文件
│
▼
链接器 ──► 可执行文件
Q_OBJECT 宏的作用
Q_OBJECT 宏展开后包含:
static const QMetaObject staticMetaObject- 类的元对象virtual const QMetaObject *metaObject() const- 获取元对象virtual void *qt_metacast(const char *)- 动态类型转换virtual int qt_metacall(QMetaObject::Call, int, void **)- 调用信号槽
这些功能使得 Qt 能够在运行时获取类信息、调用方法、实现信号槽。
信号与槽(Signals & Slots)
信号槽是 Qt 对象间通信的机制,替代了传统的回调函数。
基本概念
- 信号(Signal):当某个事件发生时发出的通知
- 槽(Slot):接收信号并执行相应操作的函数
- 连接(Connect):将信号和槽关联起来
基本用法
#include <QObject>
#include <QDebug>
class Sender : public QObject
{
Q_OBJECT
signals:
// 信号声明:只需声明,无需实现
void valueChanged(int newValue);
void stringSignal(const QString &text);
};
class Receiver : public QObject
{
Q_OBJECT
public slots:
// 槽函数:普通的成员函数
void onValueChanged(int value)
{
qDebug() << "收到数值:" << value;
}
void onStringReceived(const QString &text)
{
qDebug() << "收到字符串:" << text;
}
};
// 使用
Sender sender;
Receiver receiver;
// 连接信号和槽
QObject::connect(&sender, &Sender::valueChanged,
&receiver, &Receiver::onValueChanged);
// 发射信号
emit sender.valueChanged(42); // 接收者会收到通知
连接类型
Qt 提供多种连接方式,控制槽函数的执行时机:
| 连接类型 | 说明 | 使用场景 |
|---|---|---|
Qt::AutoConnection | 自动选择(默认) | 大多数情况 |
Qt::DirectConnection | 立即直接调用 | 同线程,需要立即执行 |
Qt::QueuedConnection | 放入事件队列异步执行 | 跨线程通信 |
Qt::BlockingQueuedConnection | 阻塞等待槽执行完成 | 跨线程,需要结果 |
Qt::UniqueConnection | 确保连接唯一 | 防止重复连接 |
// 指定连接类型
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::QueuedConnection);
Lambda 表达式作为槽
Qt 5 起支持使用 Lambda 表达式,使代码更简洁:
QPushButton *button = new QPushButton("Click");
QLabel *label = new QLabel("Count: 0");
int count = 0;
// Lambda 作为槽
connect(button, &QPushButton::clicked, [=, &count]() mutable {
count++;
label->setText(QString("Count: %1").arg(count));
});
信号连接信号
信号可以连接另一个信号,实现信号转发:
// 窗口内的按钮点击转发为窗口的提交信号
connect(ui->submitButton, &QPushButton::clicked,
this, &MyDialog::submitted);
断开连接
// 断开特定连接
disconnect(sender, &Sender::signal, receiver, &Receiver::slot);
// 断开接收者的所有槽
disconnect(sender, nullptr, receiver, nullptr);
// 断开发送者的所有信号
disconnect(sender, nullptr, nullptr, nullptr);
对象树与内存管理
Qt 使用对象树(Object Tree)实现自动内存管理。
父子对象关系
// 创建父对象
QWidget *window = new QWidget;
// 创建子对象,指定父对象
QPushButton *button1 = new QPushButton("Button 1", window);
QPushButton *button2 = new QPushButton("Button 2", window);
QLabel *label = new QLabel("Label", window);
// 对象树结构:
// window
// ├── button1
// ├── button2
// └── label
// 删除父对象会自动删除所有子对象
delete window; // button1, button2, label 也会被删除
内存管理规则
- 有父对象时:父对象销毁时自动销毁子对象,不要手动 delete
- 无父对象时:需要手动管理内存,或使用智能指针
- QObject 的子类:优先使用对象树管理
- 非 QObject 对象:使用标准 C++ 内存管理
// 好的实践:使用对象树
void setupUI(QWidget *parent)
{
QVBoxLayout *layout = new QVBoxLayout(parent);
QPushButton *btn1 = new QPushButton("OK", parent);
QPushButton *btn2 = new QPushButton("Cancel", parent);
layout->addWidget(btn1);
layout->addWidget(btn2);
// 无需手动 delete,parent 销毁时会自动清理
}
// 特殊情况:需要提前释放
void temporaryObject()
{
QDialog *dialog = new QDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose); // 关闭时自动删除
dialog->show();
}
事件系统
Qt 使用事件驱动编程模型,所有用户交互都通过事件处理。
事件循环
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow window;
window.show();
// 启动事件循环
// 不断从系统获取事件并分发处理
return app.exec();
}
常见事件类型
| 事件类 | 触发时机 | 处理方法 |
|---|---|---|
QMouseEvent | 鼠标操作 | mousePressEvent, mouseMoveEvent |
QKeyEvent | 键盘操作 | keyPressEvent, keyReleaseEvent |
QPaintEvent | 需要重绘 | paintEvent |
QResizeEvent | 窗口大小改变 | resizeEvent |
QCloseEvent | 窗口关闭 | closeEvent |
QTimerEvent | 定时器触发 | timerEvent |
事件处理示例
class MyWidget : public QWidget
{
public:
MyWidget(QWidget *parent = nullptr) : QWidget(parent) {}
protected:
// 重写鼠标按下事件
void mousePressEvent(QMouseEvent *event) override
{
if (event->button() == Qt::LeftButton) {
qDebug() << "左键点击:" << event->pos();
}
// 调用基类实现(保持默认行为)
QWidget::mousePressEvent(event);
}
// 重写键盘事件
void keyPressEvent(QQKeyEvent *event) override
{
switch (event->key()) {
case Qt::Key_Escape:
close();
break;
case Qt::Key_F1:
showHelp();
break;
default:
QWidget::keyPressEvent(event);
}
}
// 重绘事件
void paintEvent(QPaintEvent *event) override
{
QPainter painter(this);
painter.setBrush(Qt::blue);
painter.drawRect(10, 10, 100, 80);
}
};
事件过滤器
事件过滤器允许一个对象监视另一个对象的事件:
class EventFilter : public QObject
{
protected:
bool eventFilter(QObject *watched, QEvent *event) override
{
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << "拦截到鼠标事件:" << mouseEvent->pos();
return true; // 消费事件,不再传递
}
return QObject::eventFilter(watched, event); // 继续传递
}
};
// 使用
EventFilter *filter = new EventFilter(this);
ui->button->installEventFilter(filter); // 为按钮安装过滤器
发送自定义事件
// 定义自定义事件类型
const QEvent::Type MyCustomEventType =
static_cast<QEvent::Type>(QEvent::User + 1);
class MyCustomEvent : public QEvent
{
public:
MyCustomEvent(const QString &data)
: QEvent(MyCustomEventType), m_data(data) {}
QString data() const { return m_data; }
private:
QString m_data;
};
// 发送事件
QCoreApplication::postEvent(receiver, new MyCustomEvent("Hello"));
// 处理自定义事件
void MyObject::customEvent(QEvent *event) override
{
if (event->type() == MyCustomEventType) {
MyCustomEvent *myEvent = static_cast<MyCustomEvent*>(event);
qDebug() << "收到自定义事件:" << myEvent->data();
}
}
属性系统
Qt 的属性系统提供了动态访问对象属性的能力。
定义属性
class Person : public QObject
{
Q_OBJECT
// 声明属性
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)
public:
explicit Person(QObject *parent = nullptr) : QObject(parent) {}
// Getter
QString name() const { return m_name; }
int age() const { return m_age; }
// Setter
void setName(const QString &name) {
if (m_name != name) {
m_name = name;
emit nameChanged(name);
}
}
void setAge(int age) {
if (m_age != age) {
m_age = age;
emit ageChanged(age);
}
}
signals:
void nameChanged(const QString &name);
void ageChanged(int age);
private:
QString m_name;
int m_age = 0;
};
使用属性
Person person;
// 通过 setter/getter
person.setName("张三");
person.setAge(25);
qDebug() << person.name(); // "张三"
// 通过元对象系统动态访问
person.setProperty("name", "李四");
QVariant name = person.property("name");
qDebug() << name.toString(); // "李四"
// 获取所有属性
const QMetaObject *meta = person.metaObject();
for (int i = 0; i < meta->propertyCount(); i++) {
QMetaProperty prop = meta->property(i);
qDebug() << "属性:" << prop.name();
}
字符串与容器
Qt 提供了自己的字符串和容器类,与 C++ STL 类似但更适合 Qt 开发。
QString
Qt 的字符串类,支持 Unicode:
// 创建字符串
QString str1 = "Hello";
QString str2 = QStringLiteral("World"); // 编译期优化
// 字符串操作
QString result = str1 + " " + str2; // "Hello World"
result.append("!");
result.prepend("Say: ");
// 格式化
QString formatted = QString("Name: %1, Age: %2").arg("张三").arg(25);
// 与数字转换
int num = QString("42").toInt();
QString str = QString::number(3.14159, 'f', 2); // "3.14"
// 中文字符串
QString chinese = QStringLiteral("你好,世界");
int length = chinese.length(); // 6(字符数,不是字节数)
Qt 容器类
| Qt 容器 | STL 等价 | 特点 |
|---|---|---|
QList<T> | std::vector<T> | 通用列表,推荐首选 |
QVector<T> | std::vector<T> | 连续内存存储 |
QLinkedList<T> | std::list<T> | 双向链表 |
QMap<Key, T> | std::map<Key, T> | 有序字典 |
QHash<Key, T> | std::unordered_map<Key, T> | 哈希表,查找更快 |
QSet<T> | std::set<T> | 集合 |
QStringList | - | QString 的列表 |
// QList 示例
QList<int> numbers;
numbers << 1 << 2 << 3; // 流式插入
numbers.append(4);
numbers.prepend(0);
for (int num : numbers) {
qDebug() << num;
}
// 使用 Java 风格迭代器
QListIterator<int> iter(numbers);
while (iter.hasNext()) {
qDebug() << iter.next();
}
// QMap 示例
QMap<QString, int> scores;
scores["张三"] = 90;
scores["李四"] = 85;
scores.insert("王五", 95);
// 遍历
for (auto it = scores.begin(); it != scores.end(); ++it) {
qDebug() << it.key() << ":" << it.value();
}
// C++11 范围 for
for (const auto &[name, score] : scores.toStdMap()) {
qDebug() << name << score;
}
总结
Qt 的核心概念相互关联,形成完整的开发框架:
- 元对象系统 - 提供反射、信号槽、属性等动态特性
- 信号槽 - 对象间松耦合的通信机制
- 对象树 - 自动内存管理
- 事件系统 - 响应用户交互和系统事件
- 属性系统 - 动态访问对象状态
理解这些概念后,你就能更好地利用 Qt 开发高效、稳定的应用程序。