1. 自定义信号与槽(基本语法)

在 Qt 中,不仅可以使用控件自带的信号,还可以为自己设计的类添加自定义信号和槽,用于对象间的通信。例如:老师类发出"上课了"的信号,学生类执行"回到座位"的槽。
1.1 自定义信号(书写规范)
-
位置 :必须写在类的
signals:区域下。 -
返回值 :必须是
void。 -
实现 :只需声明,不需要定义 (由
moc自动生成)。 -
参数:可以有参数,也可以重载。
示例(在 teacher.h 中):
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
void classBegin(); // 无参信号
void classBegin(QString msg); // 带参信号(重载)
};
1.2 自定义槽(书写规范)
-
位置 :早期版本要求写在
public slots:、protected slots:或private slots:下,高版本 Qt 也允许直接放在public:或全局。 -
返回值 :
void。 -
实现 :需要声明,也需要定义(和普通函数一样)。
-
参数:可以有参数,也可以重载。
示例(在 student.h 中):
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
public slots: // 或直接 public:
void goToClass(); // 无参槽
void goToClass(QString lesson); // 带参槽
};
在 student.cpp 中实现槽:
void Student::goToClass()
{
qDebug() << "回到座位,开始学习!";
}
void Student::goToClass(QString lesson)
{
qDebug() << "开始上" << lesson << "课!";
}
1.3 发射信号:emit 关键字
信号需要被"发射"才能触发槽。使用 emit 关键字(实际上是一个空宏,仅用于提示)。
emit teacher->classBegin(); // 发射无参信号
emit teacher->classBegin("数学"); // 发射带参信号
注意:先
connect再emit,否则信号无人接收。


2. 完整示例:老师上课,学生响应
我们通过一个经典案例来串联上述知识:
场景:老师对象发出"上课了"的信号,学生对象执行"回到座位,开始学习"的槽。
2.1 新建老师类(Teacher)
-
继承自
QObject(便于使用信号槽和对象树)。 -
在
signals:中声明classBegin()。




2.2 新建学生类(Student)
-
继承自
QObject。 -
在
public slots:中声明goToClass(),并在.cpp中实现。



2.3 在 Widget 中关联
widget.h 中声明成员变量:




3. 带参数的信号与槽(重载与匹配)
3.1 为什么要带参数?
信号可以携带数据,比如传递按钮状态、输入框内容、当前进度等。槽函数接收这些参数并处理。
核心规则 :信号函数的参数列表必须和对应连接的槽函数参数列表一致(或信号参数多于槽,但一般不建议)。

3.2 重载信号槽
信号和槽都可以重载(多个同名但参数不同的版本)。关联重载函数时,需要用函数指针明确指定版本,否则编译器无法确定。
示例:在 Widget 中声明重载信号和槽:
signals:
void MySignal(); // 无参
void MySignal(int value); // 带 int
public slots:
void MySlot();
void MySlot(int value);
在 .cpp 中连接:
// 使用函数指针明确选择无参版本
void (Widget::*sigVoid)() = &Widget::MySignal;
void (Widget::*slotVoid)() = &Widget::MySlot;
connect(this, sigVoid, this, slotVoid);
// 选择带参版本
void (Widget::*sigInt)(int) = &Widget::MySignal;
void (Widget::*slotInt)(int) = &Widget::MySlot;
connect(this, sigInt, this, slotInt);
注意 :函数指针要指明作用域(Widget::*),否则编译报错。
3.3 参数个数匹配规则
-
信号参数个数可以多于槽参数个数(多的参数会被忽略)。
-
槽参数个数不能多于信号参数个数(否则编译失败)。
-
类型必须兼容(例如
QString不能传给int)。
但为了清晰和安全,强烈建议保持参数个数和类型完全一致。
4. 信号与槽的连接关系(多对多)
4.1 一对一(最常用)
一个信号对应一个槽(之前所有示例都是)。
4.2 一对多(一个信号连接多个槽)
同一个信号可以连接多个不同的槽,信号发射后,所有关联的槽会按连接顺序依次执行。
示例:
connect(this, &Widget::MySignal, this, &Widget::slot1);
connect(this, &Widget::MySignal, this, &Widget::slot2);
connect(this, &Widget::MySignal, this, &Widget::slot3);
// 发射信号后,三个槽依次执行
emit MySignal();
4.3 多对一(多个信号连接同一个槽)
多个不同的信号可以连接到同一个槽函数,槽函数会被多次调用。
示例:
connect(btn1, &QPushButton::clicked, this, &Widget::commonSlot);
connect(btn2, &QPushButton::clicked, this, &Widget::commonSlot);
connect(this, &Widget::signalA, this, &Widget::commonSlot);


4.4 信号连接信号
一个信号可以连接到另一个信号,形成信号链。当第一个信号发射时,会触发第二个信号,进而触发其关联的槽。
用途:在某些场景下,你想在中间做一些额外处理,或者只是转发信号。
示例:
connect(btn, &QPushButton::clicked, this, &Widget::MySignal); // 按钮点击 → 发射 MySignal
connect(this, &Widget::MySignal, this, &Widget::MySlot); // MySignal → 执行槽
此时点击按钮,MySignal 被发射,随后 MySlot 执行。
5. 断开连接:disconnect
如果不再需要某个连接,可以使用 QObject::disconnect() 断开。
disconnect(btn, &QPushButton::clicked, this, &Widget::close);
通常不主动断开,因为对象销毁时会自动断开所有连接,但某些特殊场景(如临时禁用响应)会用到。