Qt 基础语法 #
信号与槽机制 #
概念介绍 #
信号与槽(Signals and Slots)是 Qt 的核心通信机制,用于对象间的松耦合通信。
text
┌─────────────────────────────────────────────────────────────┐
│ 信号与槽机制 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统回调函数: │
│ │
│ ┌─────────┐ 直接调用 ┌─────────┐ │
│ │ 对象A │ ───────────────> │ 对象B │ │
│ └─────────┘ └─────────┘ │
│ │
│ 问题:紧耦合,A 必须知道 B 的存在 │
│ │
│ Qt 信号与槽: │
│ │
│ ┌─────────┐ 发射信号 ┌─────────┐ │
│ │ 对象A │ ───────────────> │ 对象B │ │
│ │ (信号) │ │ (槽) │ │
│ └─────────┘ └─────────┘ │
│ │
│ 优势: │
│ ✅ 松耦合:发送者不需要知道接收者 │
│ ✅ 类型安全:编译时检查参数类型 │
│ ✅ 灵活:一个信号可连接多个槽 │
│ ✅ 多对多:多个信号可连接同一个槽 │
│ │
└─────────────────────────────────────────────────────────────┘
基本语法 #
定义信号和槽 #
cpp
// MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = nullptr);
signals:
void valueChanged(int newValue);
void messageSent(const QString &msg);
public slots:
void setValue(int value);
void onMessageReceived(const QString &msg);
private:
int m_value;
};
#endif // MYOBJECT_H
cpp
// MyObject.cpp
#include "MyObject.h"
MyObject::MyObject(QObject *parent)
: QObject(parent), m_value(0)
{
}
void MyObject::setValue(int value)
{
if (m_value != value) {
m_value = value;
emit valueChanged(value);
}
}
void MyObject::onMessageReceived(const QString &msg)
{
qDebug() << "Received message:" << msg;
}
连接方式 #
Qt 5+ 新式语法(推荐) #
cpp
// 函数指针方式(编译时类型检查)
connect(sender, &Sender::valueChanged,
receiver, &Receiver::onValueChanged);
// Lambda 表达式
connect(button, &QPushButton::clicked, [=]() {
qDebug() << "Button clicked";
});
// 带参数的 Lambda
connect(slider, &QSlider::valueChanged, [=](int value) {
label->setText(QString::number(value));
});
// 连接到普通函数
connect(timer, &QTimer::timeout, &someFunction);
Qt 旧式语法(不推荐) #
cpp
// 字符串方式(运行时检查)
connect(sender, SIGNAL(valueChanged(int)),
receiver, SLOT(onValueChanged(int)));
// 问题:
// ❌ 编译时不检查参数类型
// ❌ 参数不匹配时运行时才报错
// ❌ 重构时容易出错
信号与槽的连接模式 #
cpp
// 自动连接(默认)
// 当信号发射时,槽函数被调用
connect(sender, &Sender::signal, receiver, &Receiver::slot);
// 队列连接(跨线程)
// 槽函数在接收者所在线程的事件循环中执行
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::QueuedConnection);
// 直接连接
// 槽函数在信号发射时立即执行
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::DirectConnection);
// 唯一连接
// 确保连接只建立一次
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::UniqueConnection);
// 阻塞队列连接(跨线程)
// 发送者等待槽函数执行完成
connect(sender, &Sender::signal, receiver, &Receiver::slot,
Qt::BlockingQueuedConnection);
断开连接 #
cpp
// 断开特定连接
disconnect(sender, &Sender::signal, receiver, &Receiver::slot);
// 断开发送者的所有连接
disconnect(sender, nullptr, nullptr, nullptr);
// 断开接收者的所有连接
disconnect(nullptr, nullptr, receiver, nullptr);
// 使用 QMetaObject::Connection 断开
QMetaObject::Connection conn = connect(...);
disconnect(conn);
一对多和多对一 #
cpp
// 一个信号连接多个槽
connect(button, &QPushButton::clicked, []() {
qDebug() << "Slot 1";
});
connect(button, &QPushButton::clicked, []() {
qDebug() << "Slot 2";
});
connect(button, &QPushButton::clicked, []() {
qDebug() << "Slot 3";
});
// 点击按钮时,三个槽函数按顺序执行
// 多个信号连接同一个槽
connect(button1, &QPushButton::clicked, this, &MyClass::onButtonClicked);
connect(button2, &QPushButton::clicked, this, &MyClass::onButtonClicked);
connect(button3, &QPushButton::clicked, this, &MyClass::onButtonClicked);
元对象系统 (Meta-Object System) #
概念介绍 #
Qt 的元对象系统提供了运行时类型信息、动态属性系统和信号槽机制的基础。
text
┌─────────────────────────────────────────────────────────────┐
│ 元对象系统组成 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. QObject 基类 │
│ - 提供对象树结构 │
│ - 支持信号与槽 │
│ - 提供对象名称和属性 │
│ │
│ 2. Q_OBJECT 宏 │
│ - 声明元对象特性 │
│ - 启用信号槽、属性、国际化 │
│ │
│ 3. MOC (Meta-Object Compiler) │
│ - 元对象编译器 │
│ - 处理 Q_OBJECT 宏 │
│ -生成 moc_*.cpp 文件 │
│ │
└─────────────────────────────────────────────────────────────┘
Q_OBJECT 宏 #
cpp
class MyClass : public QObject
{
Q_OBJECT // 必须放在类声明的最前面
public:
explicit MyClass(QObject *parent = nullptr);
virtual ~MyClass();
signals:
void mySignal();
public slots:
void mySlot();
private:
Q_DISABLE_COPY(MyClass) // 禁用拷贝
};
MOC 工作流程 #
text
┌─────────────────────────────────────────────────────────────┐
│ MOC 编译流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ myclass.h │
│ (包含 Q_OBJECT 宏) │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ MOC │ 读取头文件,识别 Q_OBJECT 宏 │
│ └────┬────┘ │
│ │ │
│ ▼ │
│ moc_myclass.cpp │
│ (生成的元对象代码) │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ 编译器 │ 编译 moc 文件 │
│ └────┬────┘ │
│ │ │
│ ▼ │
│ myclass.o + moc_myclass.o │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ 链接器 │ 链接生成可执行文件 │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
运行时类型信息 #
cpp
// 获取类名
QObject *obj = new MyClass;
qDebug() << obj->metaObject()->className(); // "MyClass"
// 继承关系检查
if (obj->inherits("QObject")) {
qDebug() << "obj is a QObject";
}
// 动态类型转换
MyClass *myObj = qobject_cast<MyClass*>(obj);
if (myObj) {
// 转换成功
}
// 获取父类信息
const QMetaObject *super = obj->metaObject()->superClass();
qDebug() << super->className();
反射机制 #
cpp
// 遍历方法
const QMetaObject *metaObj = obj->metaObject();
for (int i = 0; i < metaObj->methodCount(); ++i) {
QMetaMethod method = metaObj->method(i);
qDebug() << method.methodSignature();
}
// 遍历属性
for (int i = 0; i < metaObj->propertyCount(); ++i) {
QMetaProperty property = metaObj->property(i);
qDebug() << property.name() << property.read(obj);
}
// 遍历信号和槽
for (int i = metaObj->methodOffset(); i < metaObj->methodCount(); ++i) {
QMetaMethod method = metaObj->method(i);
if (method.methodType() == QMetaMethod::Signal) {
qDebug() << "Signal:" << method.methodSignature();
} else if (method.methodType() == QMetaMethod::Slot) {
qDebug() << "Slot:" << method.methodSignature();
}
}
// 动态调用方法
QMetaObject::invokeMethod(obj, "mySlot", Qt::DirectConnection);
属性系统 #
定义属性 #
cpp
class MyObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
public:
QString name() const { return m_name; }
void setName(const QString &name) {
if (m_name != name) {
m_name = name;
emit nameChanged(name);
}
}
int age() const { return m_age; }
void setAge(int age) {
if (m_age != age) {
m_age = age;
emit ageChanged(age);
}
}
bool isEnabled() const { return m_enabled; }
void setEnabled(bool enabled) {
m_enabled = enabled;
}
signals:
void nameChanged(const QString &name);
void ageChanged(int age);
private:
QString m_name;
int m_age = 0;
bool m_enabled = true;
};
使用属性 #
cpp
MyObject obj;
// 使用 setter/getter
obj.setName("Qt");
qDebug() << obj.name();
// 使用属性系统
obj.setProperty("name", "Qt6");
qDebug() << obj.property("name");
// 动态属性
obj.setProperty("customProperty", 42);
qDebug() << obj.property("customProperty");
// 检查属性是否存在
if (obj.metaObject()->indexOfProperty("name") >= 0) {
qDebug() << "Property 'name' exists";
}
对象树与所有权 #
对象树结构 #
text
┌─────────────────────────────────────────────────────────────┐
│ Qt 对象树 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ │
│ │ Parent │ │
│ │ QObject │ │
│ └────┬────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ │ │ │ │
│ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │
│ │ Child 1 │ │ Child 2 │ │ Child 3 │ │
│ │ QObject │ │ QObject │ │ QObject │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 特点: │
│ - 父对象销毁时,自动销毁所有子对象 │
│ - 子对象自动添加到父对象的子列表 │
│ - 简化内存管理 │
│ │
└─────────────────────────────────────────────────────────────┘
使用对象树 #
cpp
// 创建父对象
QObject *parent = new QObject;
// 创建子对象,指定父对象
QObject *child1 = new QObject(parent);
QObject *child2 = new QObject(parent);
// 设置父对象
child1->setParent(parent);
// 获取父对象
QObject *p = child1->parent();
// 获取所有子对象
QList<QObject*> children = parent->children();
// 查找子对象
QObject *found = parent->findChild<QObject*>("childName");
// 删除父对象时,所有子对象自动删除
delete parent; // child1, child2 也被删除
控件的对象树 #
cpp
// QWidget 继承自 QObject,同样支持对象树
QWidget *window = new QWidget;
// 创建子控件,指定父控件
QPushButton *button1 = new QPushButton("Button 1", window);
QPushButton *button2 = new QPushButton("Button 2", window);
QLabel *label = new QLabel("Label", window);
// 布局自动设置父子关系
QVBoxLayout *layout = new QVBoxLayout(window);
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(label);
// 删除窗口时,所有子控件自动删除
delete window;
常用 Qt 类 #
QString #
cpp
// 创建字符串
QString str1 = "Hello";
QString str2 = QString("World");
QString str3 = QString("%1 %2").arg("Hello").arg("Qt");
// 字符串操作
str1.append(" World"); // 追加
str1.prepend("Say: "); // 前置
str1.replace("World", "Qt"); // 替换
str1.remove(0, 5); // 删除
str1.insert(5, " "); // 插入
// 字符串查询
bool contains = str1.contains("Qt");
int index = str1.indexOf("Qt");
bool starts = str1.startsWith("Hello");
bool ends = str1.endsWith("Qt");
// 字符串分割
QStringList parts = str1.split(" ");
// 数字转换
int num = str1.toInt();
QString numStr = QString::number(42);
QString floatStr = QString::number(3.14, 'f', 2);
QList / QVector #
cpp
// 创建列表
QList<int> list;
list << 1 << 2 << 3 << 4 << 5;
// 访问元素
int first = list.first();
int last = list.last();
int value = list[2];
int value2 = list.at(2);
// 添加元素
list.append(6);
list.prepend(0);
list.insert(3, 10);
// 删除元素
list.removeFirst();
list.removeLast();
list.removeAt(2);
list.removeOne(3);
list.removeAll(4);
// 查询
bool has = list.contains(5);
int count = list.count(5);
int index = list.indexOf(5);
// 遍历
for (int i : list) {
qDebug() << i;
}
// STL 风格遍历
for (auto it = list.begin(); it != list.end(); ++it) {
qDebug() << *it;
}
QMap / QHash #
cpp
// 创建映射
QMap<QString, int> map;
map["one"] = 1;
map["two"] = 2;
map.insert("three", 3);
// 访问元素
int value = map["one"];
int value2 = map.value("two");
int defaultValue = map.value("four", 0);
// 检查键是否存在
bool has = map.contains("one");
// 删除元素
map.remove("one");
// 遍历
QMapIterator<QString, int> it(map);
while (it.hasNext()) {
it.next();
qDebug() << it.key() << ":" << it.value();
}
// Java 风格遍历
for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
qDebug() << it.key() << ":" << it.value();
}
下一步 #
现在你已经掌握了 Qt 的基础语法,接下来学习 窗口与对话框,开始构建完整的用户界面!
最后更新:2026-03-29