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