1.项目结构

2.核心代码
counterApp.pro (Qt项目文件)
bash
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
counter.cpp \
main.cpp \
mainwindow.cpp
HEADERS += \
counter.h \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
counter.h(定义类和信号槽)
cpp
#ifndef COUNTER_H
#define COUNTER_H
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QMessageBox>
class Counter : public QWidget
{
Q_OBJECT // 必须的宏,启用Qt元对象系统(信号槽所需)
public:
explicit Counter(QWidget *parent = nullptr);
signals: // 信号声明区域
// 信号只有声明,没有实现,由Qt的moc自动生成
void counterChanged(int newValue); // 自定义信号:计数器值变化时发射
void counterReachedTen(); // 自定义信号:计数器达到10时发射
public slots: // 槽函数声明区域
void increaseCounter(); // 槽函数:增加计数器
void decreaseCounter(); // 槽函数:减少计数器
void resetCounter(); // 槽函数:重置计数器
void onCounterChanged(int value); // 槽函数:响应counterChanged信号
void onCounterReachedTen(); // 槽函数:响应counterReachedTen信号
private:
int count = 0;
QLabel *countLabel;
QPushButton *incButton;
QPushButton *decButton;
QPushButton *resetButton;
QLabel *statusLabel;
};
#endif // COUNTER_H
counter.cpp(实现类)
cpp
#include "counter.h"
Counter::Counter(QWidget *parent) : QWidget(parent)
{
// 初始化UI组件
countLabel = new QLabel("当前计数: 0", this);
countLabel->setAlignment(Qt::AlignCenter);
statusLabel = new QLabel("状态: 正常", this);
statusLabel->setAlignment(Qt::AlignCenter);
incButton = new QPushButton("增加 (+1)", this);
decButton = new QPushButton("减少 (-1)", this);
resetButton = new QPushButton("重置 (0)", this);
// 布局
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(countLabel);
layout->addWidget(statusLabel);
layout->addWidget(incButton);
layout->addWidget(decButton);
layout->addWidget(resetButton);
setLayout(layout);
setWindowTitle("Qt信号槽示例 - 计数器");
// ================== 信号槽连接示例 ==================
// 示例1: 按钮点击信号 -> 槽函数
// clicked()是QPushButton的内置信号
connect(incButton, &QPushButton::clicked, this, &Counter::increaseCounter);
connect(decButton, &QPushButton::clicked, this, &Counter::decreaseCounter);
connect(resetButton, &QPushButton::clicked, this, &Counter::resetCounter);
// 示例2: 自定义信号 -> 槽函数
// 当计数器变化时,发射counterChanged信号,触发onCounterChanged槽
connect(this, &Counter::counterChanged, this, &Counter::onCounterChanged);
// 示例3: 自定义信号 -> 槽函数(带条件触发)
connect(this, &Counter::counterReachedTen, this, &Counter::onCounterReachedTen);
// 示例4: 一个信号连接多个槽
// 当计数器变化时,同时更新两个标签
connect(this, &Counter::counterChanged, this, [this](int value) {
countLabel->setText(QString("当前计数: %1").arg(value));
});
// 示例5: 按钮点击直接触发匿名函数(lambda表达式作为槽)
connect(incButton, &QPushButton::clicked, this, [this]() {
statusLabel->setText("状态: 上次操作是增加");
});
// 示例6: 断开连接示例(当计数为5时断开减少按钮)
connect(this, &Counter::counterChanged, this, [this](int value) {
if (value == 5) {
disconnect(decButton, &QPushButton::clicked, this, &Counter::decreaseCounter);
decButton->setText("减少 (-1) [已禁用]");
decButton->setEnabled(false);
}
});
}
// 槽函数实现:增加计数器
void Counter::increaseCounter()
{
count++;
qDebug() << "计数器增加,当前值:" << count;
// 发射自定义信号
emit counterChanged(count);
// 条件发射信号:当计数达到10时
if (count == 10) {
emit counterReachedTen();
}
}
// 槽函数实现:减少计数器
void Counter::decreaseCounter()
{
count--;
qDebug() << "计数器减少,当前值:" << count;
emit counterChanged(count);
}
// 槽函数实现:重置计数器
void Counter::resetCounter()
{
count = 0;
qDebug() << "计数器重置为0";
emit counterChanged(count);
}
// 槽函数实现:响应counterChanged信号
void Counter::onCounterChanged(int value)
{
qDebug() << "收到counterChanged信号,新值:" << value;
// 根据计数值改变颜色
if (value > 0) {
countLabel->setStyleSheet("color: green; font-size: 16px;");
} else if (value < 0) {
countLabel->setStyleSheet("color: red; font-size: 16px;");
} else {
countLabel->setStyleSheet("color: black; font-size: 16px;");
}
}
// 槽函数实现:响应counterReachedTen信号
void Counter::onCounterReachedTen()
{
QMessageBox::information(this, "恭喜", "计数器达到10啦!🎉");
statusLabel->setText("状态: 已到达10");
}
main.cpp(程序入口)
cpp
// #include "mainwindow.h"
#include "counter.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// MainWindow w;
// w.show();
Counter counter;
counter.resize(300, 200);
counter.show();
return a.exec();
}
3.运行效果




4.核心概念解释
信号 (Signal)
-
信号是事件发生时发射的通知
-
声明在类的
signals:区域 -
只有声明,没有实现
-
使用
emit signalName(parameters);发射信号 -
特点:
-
不关心谁接收
-
可以带参数传递数据
-
由moc自动生成实现代码
cpp
// 声明
signals:
void valueChanged(int newValue);
// 发射
emit valueChanged(5);
槽 (Slot)
-
槽是响应信号的函数
-
声明在
public slots:或private slots:区域 -
特点:
-
需要完整实现
-
可以是普通成员函数、lambda表达式
-
参数必须与连接的信号匹配
cpp
public slots:
void onValueChanged(int value); // 槽声明
连接 (Connect)
-
作用:建立信号和槽的映射关系
-
时机:在程序初始化时建立(通常是构造函数)
-
语法 :
connect(发送者, 信号, 接收者, 槽) -
必须性 :必须先connect,信号发射才有效
cpp
connect(sender, &SenderClass::signalName,
receiver, &ReceiverClass::slotName);
5.混淆点
信号发射后才建立连接。X
正确的理解是:
-
连接(connect)是注册/绑定操作,在程序初始化时(通常是构造函数中)完成
-
信号发射(emit)是触发操作,在用户交互或程序逻辑中发生
-
必须先有连接,然后发射信号才有效
-
如果没有连接,发射信号相当于空操作
简单说:
-
connect = 插上电源线
-
emit = 按下开关
-
槽函数执行 = 灯泡亮起
必须先插上电源线,按下开关灯泡才会亮!
6.核心工作流程
html
程序启动
↓
构造函数执行
↓
创建UI组件
↓
建立所有connect连接 ← 关键!连接在这里建立!
↓
等待用户操作
↓
用户触发事件 → 发射信号 → 触发已连接的槽函数
↓
槽函数执行处理逻辑
7.关键理解点
1. 连接是注册,不是触发
-
connect()只是告诉Qt:"当A发射信号X时,请调用B的槽Y" -
连接操作是立即执行的注册操作
-
连接建立后,映射关系一直存在直到断开
2. 信号发射是广播
-
emit signal()相当于广播:"事件发生了!" -
如果有连接,Qt会自动调用所有连接的槽函数
-
如果没有连接,什么都不会发生
3. 槽是被动调用
-
槽函数不会主动执行
-
只有对应的信号发射时,Qt才会调用它
-
槽函数是回调函数
8. moc(元对象编译器)的作用
-
扩展C++:让C++支持信号槽、反射等特性
-
生成代码:为信号生成实现代码
-
元对象系统:创建类的元信息(类名、方法、属性等)
-
运行时支持:支持动态类型检查、属性系统
html
源代码(.h) → moc → 预处理代码(.cpp) → 编译器
↓
添加"魔法代码"(信号实现、元对象等)
9.总结
当使用信号槽时,请确保:
-
✅ 类必须直接或间接继承自QObject
html// Qt 官方继承链 QObject ↳ QWidget ↳ QPushButton, QLabel, QLineEdit... ↳ Counter类 -
✅ 类声明中有
Q_OBJECT宏 -
✅ 信号声明在
signals:区域 -
✅ 槽声明在
slots:区域 -
✅ 在构造函数中先 调用
connect()建立连接 -
✅ 在事件处理中后 使用
emit发射信号 -
✅ 信号和槽的参数类型匹配
-
✅ 使用新语法:
connect(发送者, &类名::信号, 接收者, &类名::槽)