一、信号和槽核心概念(大白话)
信号和槽是Qt特有的通信机制 ,用来实现对象之间的交互,简单理解就是:一个对象发出信号,另一个对象的槽函数接收并执行动作,全程不用手动调用函数,触发信号就自动运行槽函数,非常适合界面交互、事件响应这类场景。
比如你之前写的按钮点击弹窗,就是按钮发出"被点击"的信号 ,主窗口的槽函数接收到信号后弹出对话框,完全靠信号槽绑定实现自动触发。
核心前提 :使用信号槽的类,必须继承QObject,并且类内第一行加 Q_OBJECT 宏,否则信号槽无法生效,Qt Widgets项目里的主窗口、QDialog、QPushButton都满足这个条件。
二、实用举例:按钮点击弹出对话框(Qt6 完整可运行)
这个例子完全贴合你之前的代码场景,适配当前主流的 Qt6 版本,同时保留你用过的 Qt4 旧版语法(Qt6 依然兼容),重点推荐 Qt5/Qt6 通用的新版函数指针语法,代码可直接复制运行,适配 Qt Widgets 常规项目。
这个例子完全贴合你之前的代码场景,用按钮点击触发槽函数,分别演示Qt4旧版语法(你之前用的)和Qt5+新版语法(更推荐),代码适配Qt Widgets常规项目,直接复制就能用。
场景说明
主窗口放一个按钮,按钮文字设为"弹出子对话框",点击按钮后,触发槽函数,弹出一个简单的模态对话框,全程用信号槽实现交互。
1. 头文件(mainwindow.h)
头文件主要完成类定义、信号槽声明,必须加Q_OBJECT宏,槽函数用private slots声明(旧版要求严格,新版可放宽)。
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPushButton>
#include <QDialog>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
// 核心:必须加这个宏,信号槽才能生效
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// 槽函数声明区,旧版语法必须写在slots关键字下
private slots:
// 自定义槽函数:点击按钮后执行,弹出对话框
void showChildDialog();
private:
Ui::MainWindow *ui;
// 定义按钮对象(也可以用UI设计师拖拽生成,对应你之前的ui->showChildButton)
QPushButton *showBtn;
};
#endif // MAINWINDOW_H
2. 源文件(mainwindow.cpp)
源文件完成按钮创建、信号槽绑定、槽函数实现,Qt6 中旧版宏语法可正常运行 ,但强烈推荐新版函数指针写法,编译期检查错误、无运行时失效风险,和你之前的代码完美衔接。
cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QVBoxLayout>
#include <QLabel>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 设置窗口标题和大小,Qt6 语法无变化
this->setWindowTitle("Qt6 信号槽示例");
this->resize(400, 300);
// 创建按钮,设置文字
showBtn = new QPushButton("弹出子对话框", this);
// 把按钮放到窗口中央
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(showBtn);
QWidget *centerWidget = new QWidget(this);
centerWidget->setLayout(layout);
this->setCentralWidget(centerWidget);
// ===================== Qt6 两种信号槽绑定写法(均兼容) =====================
// 写法1:你之前用的 Qt4 旧版宏语法(Qt6 兼容,不推荐新项目使用)
// 缺点:运行时才检查错误,拼写错误不报错,Qt6 官方已不建议新项目使用
// connect(showBtn, SIGNAL(clicked(bool)), this, SLOT(showChildDialog()));
// 写法2:Qt5/Qt6 通用新版语法(函数指针,Qt6 首选,编译期强校验)
// 格式:connect(发送者, &发送者类::信号名, 接收者, &接收者类::槽函数名);
connect(showBtn, &QPushButton::clicked, this, &MainWindow::showChildDialog);
}
MainWindow::~MainWindow()
{
delete ui;
}
// 槽函数具体实现:点击按钮后执行的逻辑
void MainWindow::showChildDialog()
{
// 弹出模态对话框,Qt6 用法不变
QDialog childDlg(this);
childDlg.setWindowTitle("子对话框");
childDlg.resize(300, 200);
// 对话框加提示文字
QLabel *tip = new QLabel("这是 Qt6 信号槽触发的弹窗", &childDlg);
QVBoxLayout *dlgLay = new QVBoxLayout(&childDlg);
dlgLay->addWidget(tip);
// 阻塞显示对话框,关闭前无法操作主窗口
childDlg.exec();
}
3. 主函数(main.cpp)
标准Qt主函数,无需修改,直接运行即可。
cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
三、代码逐部分拆解(对应你的旧版connect)
你之前的代码: connect(ui->showChildButton, SIGNAL(clicked(bool)), this, SLOT(showChildDialog()));
-
connect:Qt信号槽绑定函数,负责把信号和槽关联起来
-
ui->showChildButton :信号发送者,就是UI里的按钮,负责发出点击信号
-
SIGNAL(clicked(bool)) :信号,按钮自带的clicked点击信号,bool参数表示按钮是否按下,SIGNAL是Qt4宏,用来包裹信号
-
this :信号接收者,当前主窗口,槽函数属于主窗口
-
SLOT(showChildDialog()) :槽函数,自己定义的函数,信号触发后自动执行这个函数,SLOT是Qt4宏,用来包裹槽函数
四、Qt6 关键注意事项(核心必看)
-
参数匹配规则:信号的参数可以比槽函数多,反之不行,比如 clicked 带 bool 参数,槽函数无参数,Qt6 下依然合法;如果槽函数有参数,信号必须有对应类型参数。
-
Qt6 语法选择(重点) :旧版 SIGNAL/SLOT 宏语法 Qt6 只是兼容,不推荐新项目使用;新版函数指针写法是 Qt6 官方首选,编译期检查错误,彻底避免运行时连接失效,新手优先学这个。
-
自定义信号槽:Qt6 中自定义信号槽规则不变,依然需要继承 QObject + Q_OBJECT 宏,信号只需声明无需实现,槽函数正常编写即可。
-
Qt6 其他小变化:部分旧版弃用接口已移除,但信号槽核心机制、常用控件信号(如 QPushButton::clicked)完全没变,之前学的逻辑直接通用。
-
适用场景:界面交互、线程通信、组件间传值等场景,Qt6 依旧靠信号槽实现,是 Qt 核心交互机制,和旧版本用法一致。
-
参数匹配规则:信号的参数可以比槽函数多,反之不行,比如clicked带bool参数,槽函数无参数,是合法的;如果槽函数有参数,信号必须有对应参数
-
旧版vs新版:旧版语法运行时才会检查错误,拼写错了不报错;新版编译期就检查,更不容易出错,优先用新版
-
自定义信号槽:除了Qt自带的信号(按钮点击、窗口关闭),也可以自己定义信号和槽,实现自定义对象通信
-
适用场景:界面交互、线程通信、组件间传值等场景都能用,是Qt最核心的交互机制