【QT笔记】信号和槽
3.信号和槽删除线格式
文章目录
- 3.信号和槽~~删除线格式~~
- 概念的引出
- 概念
- 信号的本质
- 槽的本质
- 说明
- 信号和槽的使用
- 连接信号和槽
- 代码编写信号和槽
- UI生成信号和槽
- 自定义信号和槽
- 自定义槽函数的实现方法
- 自定义信号
- 信号和槽断开连接
- lambda表达式的使用
- 信号和槽机制与其他GUI开发框架的对比
- 总结
概念的引出
QT中的信号和槽是一种用于对象间通信的机制,主要用于替代传统的回调函数。当某个特定事件发生时(例如用户点击按钮),对象会发射一个信号,而与该信号关联的槽函数(即接收方的方法)会自动被调用。
QT中信号和槽与Linux中的信号有相似之处。
- 在Linux中,信号是系统内部的通知机制和进程间通信方式,例如当程序出现内存访问越界时会收到SIGSEGV信号,管道对端关闭时会触发SIGPIPE信号。Linux信号包含三个关键要素:信号源(发送者)、信号类型(不同情况触发不同信号)和信号处理方式(通过注册回调函数)。
- 虽然Qt中的信号与Linux信号不是同一概念,但存在相似之处。Qt信号同样包含三个要素:信号源(由哪个控件发出,如按钮或输入框)、信号类型(区分用户不同操作,如点击按钮或移动光标)和信号处理方式(通过槽函数实现)。在Qt中可以使用connect函数将信号与槽关联起来,当信号触发时会自动执行对应的槽函数。槽函数本质上是一种回调函数,这与编程中常见的回调函数概念一致。
概念
信号的本质
信号是由于⽤⼾对窗⼝或控件进⾏了某些操作,导致窗⼝或控件产⽣了某个特定事件,这时 Qt 对应的窗⼝类会发出某个信号,以此对⽤⼾的操作做出反应。因此,信号的本质就是事件
槽的本质
槽(Slot)就是对信号响应的函数。槽就是⼀个函数,与⼀般的 C++ 函数是⼀样的,可以定义在类的任何位置( public、protected 或 private ),可以具有任何参数,可以被重载,也可以被直接调⽤(但是不能有默认参数)。槽函数与⼀般的函数不同的是:槽函数可以与⼀个信号关联,当信号被发射时,关联的槽函数被⾃动执⾏
说明
- 信号和槽机制底层是通过函数间的相互调⽤实现的。每个信号都可以⽤函数来表⽰,称为信号函数;每个槽也可以⽤函数表⽰,称为槽函数。例如: “按钮被按下” 这个信号可以⽤
clicked()函数表⽰,“窗⼝关闭” 这个槽可以⽤close()函数表⽰,假如使⽤信号和槽机制实现:“点击按钮会关闭窗⼝” 的功能,其实就是clicked()函数调⽤close()函数的效果- 信号函数和槽函数通常位于某个类中,和普通的成员函数相⽐,它们的特别之处在于:
- 信号函数⽤ signals 关键字修饰,槽函数⽤ public slots、protected slots 或者 private slots 修饰。
signals和slots是 Qt 在 C++ 的基础上扩展的关键字,专⻔⽤来指明信号函数和槽函数;- 信号函数只需要声明,不需要定义(实现),⽽槽函数需要定义(实现)
信号和槽的使用
连接信号和槽
在Qt中可以使用connect函数将信号与槽关联起来,当信号触发时会自动执行对应的槽函数。槽函数本质上是一种回调函数,这与编程中常见的回调函数概念一致。
注意:
在Qt编程中,信号和槽的关联顺序至关重要。必须先通过
connect函数将信号与槽关联好,然后再触发信号。如果顺序颠倒,信号将无法被正确处理,导致程序出错。
connect (const QObject *sender,const char * signal ,const QObject * receiver ,const char * method ,Qt::ConnectionType type = Qt::AutoConnection )
简单说明一下:
-
sender:信号的发送者;
-
signal:发送的信号(信号函数);
-
receiver:信号的接收者;
-
method:接收信号的槽函数;
-
type: ⽤于指定关联⽅式,默认的关联⽅式为 Qt::AutoConnection,通常不需要⼿动设定。
我们注意到
connect()函数的第二和第四个参数在官方文档中的类型是char*,但是在实际使用的时候我们向这两个参数传递的是函数指针。
char*和函数指针是不同的类型,char*是一个指向字符的指针,而函数指针是指向函数的指针。不同类型的指针在C++中不能直接相互赋值。- 旧版本的Qt中,
connect函数确实使用char*参数,并通过SIGNAL和SLOT宏将函数指针转换为char*。但从Qt5开始,connect函数被重载,使用泛型参数代替char*,允许直接传入函数指针。新的connect函数还引入了编译时的类型检查,确保传入的函数指针确实是相应对象的成员函数。- Qt官方文档可能没有及时更新,因此直接查看Qt源码能更准确地理解connect函数的实现细节。
代码编写信号和槽
我们直接使用代码,写一个关闭窗口的按钮:按照之前的步骤创建项目之后首先创建QPushButton对象,设置其父对象为当前窗口(挂载到对象树),并指定按钮文本和位置。随后使用connect函数关联按钮的clicked信号与窗口的close槽函数。clicked信号表示按钮被点击后的触发事件,而close函数用于关闭窗口。
在widget.cpp中添加代码:
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton *button = new QPushButton(this);button->setText("按钮");//按钮的名称button->move(200, 200);//按钮的位置connect(button, &QPushButton::clicked, this, &Widget::close);//连接信号和槽
}
注意:
在编写信号槽代码时,需注意信号和槽的语法形式。信号通常以过去分词命名(如clicked),表示动作完成后的触发;槽函数则以动词命名(如close),表示具体操作。
开发工具(如Qt Creator)支持通过Ctrl+鼠标左键跳转到函数或类定义,Alt+左方向键返回,便于代码导航。
信号本质上是对象的成员函数,因此在connect中需通过函数指针指定信号和槽。示例中,QPushButton::clicked是信号,而QWidget::close是槽函数,两者通过connect关联后实现功能逻辑。
如何知道Qt提供了哪些内置信号和槽,以及如何查阅文档获取这些信息。建议通过查阅Qt官方文档来了解各个类提供的信号和槽,特别提醒如果在当前类中找不到所需信息,应该沿着继承层次向上查找父类文档。
- 比如通过查阅文档可知:Qt中类的继承体系设计理念,特别是QAbstractButton作为抽象基类的作用。QAbstractButton抽象了各种按钮的共性功能,QPushButton等具体按钮类继承并扩展这些功能。这种设计体现了面向对象编程的继承特性,将共性功能放在基类中,派生类只需实现特定功能。
UI生成信号和槽

UI自动生成信号和槽的时候,在 “widget.h” 头⽂件中⾃动添加槽函数的声明,在widget.cpp中会添加一个对应槽函数的空函数,这个自动生成的槽函数有独特的命名规则:
- 以 " on " 开头,中间使⽤下划线连接起来
- " XXX " 表⽰的是对象名(控件的 objectName 属性)。
- " SSS " 表⽰的是对应的信号
我们需要做的就是在自动生成的槽函数框架中添加需要执行的代码
void Widget::on_bp_clicked()
{this->close();
}
- 按照这种命名⻛格定义的槽函数, 就会被 Qt ⾃动的和对应的信号进⾏连接。这种机制依赖于控件的objectName属性,该属性在UI设计器中设置。自动连接是通过Qt的元对象系统实现的,在程序启动时会扫描所有符合命名规则的槽函数并自动建立连接。可以通过修改函数名来验证这个机制:当函数名不符合规则时,连接就会失效。
- Qt Creator生成的UI类代码中会调用
connectSlotsByName函数来完成这种自动连接。这种机制简化了信号槽的连接过程,特别是对于UI设计器创建的控件。自动连接不仅适用于按钮点击事件,也可以用于其他控件和信号。这种方式的限制是必须严格遵循命名规则,且只能连接UI设计器中创建的控件。对于动态创建的控件或需要更复杂连接逻辑的情况,仍然需要使用显式的connect函数。- Qt的信号槽机制经历了从Qt4到Qt5的重要演变。在Qt4中,槽函数必须声明在特定的slots区域内,使用
public slots、private slots或protected slots关键字。slots是Qt通过元编程技术扩展的关键字,不是标准C++语法。Qt使用qmake工具和moc(元对象编译器)预处理代码,扫描特殊关键字并生成额外的C++代码来实现信号槽机制。在Qt5中,槽函数可以像普通成员函数一样定义,不再需要特殊的slots声明。这种变化简化了代码编写,但底层实现仍然依赖于元编程技术。信号槽机制的核心是Qt的元对象系统,它提供了运行时类型信息和动态方法调用的能力。- 但是咱们⽇常写代码的时候, 除⾮是 IDE ⾃动⽣成, 否则最好还是不要依赖命名规则, ⽽是显式使⽤ connect 更好.
自定义信号和槽
自定义槽函数的实现方法
(1)早期的 Qt 版本要求槽函数必须写到 “public slots” 下,但是现在⾼级版本的 Qt 允许写到类的 “public” 作⽤域中或者全局下
(2)返回值为 void,需要声明,也需要实现;
(3)可以有参数,可以发⽣重载;
- 简单来说,自定义槽函数的过程与定义普通成员函数完全相同,没有特殊要求。

- 自定义槽函数支持带参,支持函数重载

自定义槽函数带参时需要注意:槽函数与信号函数的参数是要匹配的
自定义信号
1)⾃定义信号函数必须写到 “signals” 下;
2)返回值为 void,只需要声明,不需要实现
3)可以有参数,也可以发⽣重载;
4)发送信号:使用
emit+信号函数直接发送信号,信号触发之后会自动调用对应的槽函数,前提是这个信号已经与对应的槽函数connect

信号和槽断开连接
使用信号和槽的时候,我们不仅可以使用connect建立信号和槽的连接,也可以使用disconnect断开连接。其使用方式与connect类似但功能相反;disconnect在实际开发中使用较少,通常连接后不需要主动断开。只有在需要将信号重新绑定到另一个槽函数时才需要先断开原有连接,以避免形成一对多关系。
lambda表达式的使用
在定义槽函数时可以使用Lambda表达式,这是一种在C++课程中介绍过的语法,被许多编程语言支持。Lambda表达式本质上是一个匿名函数,主要应用在回调函数场景中。回调函数的特点是往往一次性使用,Lambda正好适合这种场景,可以方便地现场定义回调函数而无需单独声明和定义函数。具体的使用方法参考C++部分内容,这里举一个简单的例子:
我们创建一个按钮,点击之后将按钮的位置移动到窗口的其他位置,并在调试中断输出字符串
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮");button->move(200,200);connect(button, &QPushButton::clicked, this, [button](){qDebug() << "lambda";button->move(100,100);});
}

需要注意的是
Lambda表达式语法是C++11引入的,Qt5及更高版本默认支持C++11,但使用Qt4或更老版本时需要手动在.pro文件中添加C++11编译选项。Qt5和Qt6中已经自带这个选项,但在Qt4中需要手动添加才能使用Lambda表达式。
信号和槽机制与其他GUI开发框架的对比
-
信号和槽机制说明:
信号槽机制是Qt中用于响应用户操作的一种独特技术。它通过将用户的操作(信号)与对应的处理逻辑(槽)连接起来,实现图形化界面开发的基本功能。信号槽机制的核心目标是解耦合,使得发送信号的控件和处理逻辑可以独立存在。此外,信号槽机制还支持多对多的连接方式,即一个信号可以连接到多个槽函数,一个槽函数也可以被多个信号连接。
-
其他GUI对比说明:
相比之下,其他图形化开发框架(如网页开发中的回调函数机制)通常采用一对一的连接方式,即一个事件只能对应一个处理函数,一个处理函数也只能对应一个事件。
-
类比数据库说明:
Qt的信号槽机制中的多对多连接方式与数据库中的多对多关系非常相似。在数据库中,多对多关系需要通过关联表来实现。例如,学生和课程之间的关系是多对多的,因为一个学生可以选择多门课程,一门课程也可以被多个学生选择。为了表示这种关系,需要引入第三张表(关联表)来记录学生和课程的对应关系。同样,Qt的信号槽机制中,一个信号可以连接到多个槽函数,一个槽函数也可以被多个信号连接,这种多对多的关系通过
connect方法实现。 -
使用说明:
虽然Qt的信号槽机制支持多对多关联,但在实际开发中很少需要真正使用这种功能,大部分情况下只需要一对一关联。
总结
- Qt中的信号槽机制涉及三个核心要素:信号源(谁发的信号)、信号类型(什么样的信号)和信号处理方式(指定的回调函数)。
- 信号槽的使用关键在于connect函数,使用connect函数时,最初连接的是Qt内置的信号和槽,需要查阅文档了解控件内置的信号和槽,包括信号何时触发以及槽的作用。查找信号和槽时需要注意,可能需要到父类甚至更上层的类中查询。
- 自定义槽函数本质上是自定义一个普通的成员函数,也可以通过Qt Creator自动生成,在界面编辑器中右键控件选择"转到槽"可以自动生成函数声明和定义,这种方式虽然没有显式的connect,但通过函数名的特定规则可以自动完成连接。使用自动生成的方式比手动编写代码更方便快捷。
- 自定义信号本质上是一个成员函数,但与普通成员函数不同,函数的定义由Qt自动生成,只需要写函数声明并将其放在signals关键字中。Qt调用qmake编译代码时会根据signals关键字识别信号并自动生成函数定义。
- 自定义信号通过emit发射,emit可以省略。信号和槽可以带有参数,参数传递要求信号函数的参数类型和数量与槽函数一致,信号的参数可以多于槽的参数。信号槽机制的意义在于解耦合和实现多对多的效果。

