赞
踩
Qt信号与槽机制是一种用于处理对象间通信的强大机制,它是Qt框架的核心特性之一。信号与槽机制使得Qt对象可以在不了解彼此的情况下进行通信,这种松耦合的设计思想极大地提高了代码的可重用性和灵活性。
在Qt中,一个对象可以发出一个信号,而另一个对象的槽函数可以接收这个信号并作出响应。信号是一个类的成员函数,当某个特定的事件发生时,它会被自动调用。槽函数也是类的成员函数,它可以被信号连接,当信号发出时,槽函数会被自动执行。下面是与信号槽机制相关的基本概念,如下:
1)信号
在Qt中,信号是一个特定的成员函数,用于在某个特定事件发生时被发射
(emit)
。信号本身并不实现任何功能,它只是表明某个事件已经发生。信号可以被定义为一个类的成员函数,但其实现是由Qt的元对象系统自动完成的。信号的声明通常在类的头文件中进行,使用signals
关键字来标记。
2)槽
槽是普通的成员函数,可以被信号触发。当信号被发射时,与之连接的槽函数将被调用。槽函数可以实现具体的功能,比如更新界面、处理数据等。槽函数的声明和普通成员函数一样,但在Qt的元对象系统中需要进行一些特殊的标记,以便与信号进行连接。
信号与槽的连接是通过
QObject::connect()
函数实现的,需要注意的是,为了使类支持信号与槽机制,需要在类的声明中包含Q_OBJECT
宏。下面介绍几种不同的连接方式,如下:
在Qt4及之前的版本基于connect+宏实现信号与槽绑定,其中发送信号和槽函数需要用
SIGNAL()
和SLOT()
来进行声明,connect
函数声明如下:
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
下面是一段示例代码,如下:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); // 登录按键绑定槽函数 connect(this->ui->btn_ok, SIGNAL(clicked(bool)), this, SLOT(login())); } void MainWindow::login() { QString username = ui->lineEdit_username->text(); QString password = ui->lineEdit_password->text(); if (username == "jack" && password == "12345") { qDebug() << "login success"; } else { qDebug() << "login fail"; } }
基于connect+宏实现信号与槽连接,需要注意下面这些问题
private slots
或public slots
关键字Qt5推出了新的
connect
函数,不需要使用SIGNAL()
和SLOT()
宏,可以在编译时做类型检查,connect
函数声明如下:
QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal,
const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
使用这种方法槽函数的声明不需要放到
slots
中,只要像普通的函数一样声明就可以了,类型需要与信号保持一致,下面给登录按键绑定槽函数,如下:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// 声明槽函数,与普通的成员函数一样
void login();
private:
Ui::MainWindow *ui;
};
绑定槽函数
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 登录按键绑定槽函数
connect(ui->btn_ok, &QPushButton::clicked, this, &MainWindow::login);
}
void MainWindow::login()
{
qDebug() << "login";
}
在Qt 5版本的
connect
函数里,信号与槽函数的参数其实都是函数指针,当信号或槽函数有重载时,使用函数指针可以明确告诉编译器使用哪一个重载函数避免歧义
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// 声明两个同名的login函数
void login();
void login(int state);
通过函数指针绑定槽函数
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); // 定义函数指针(注意:声明指向成员函数的指针时,要增加类作用域) void (MainWindow::*pfnLoginSlot)() = &MainWindow::login; // 登录按键绑定无参槽函数 connect(ui->btn_ok, &QPushButton::clicked, this, pfnLoginSlot); } void MainWindow::login() { qDebug() << "login"; } void MainWindow::login(int state) { qDebug() << "login state"; }
在connect函数中,槽函数参数可以改用Lambda表达式的方式来进行传参。使用 Lambda表达式的好处是代码的书写更加方便快捷,同时不需要在类中对槽函数做任何的声明了
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 使用Lamdbda表达式作为槽函数
connect(ui->btn_ok, &QPushButton::clicked, this, [=](){
qDebug() << "login";
});
}
通过Qt Creator 界面来完成发送信号和槽函数的连接,比如右键点击一个按钮,然后选择“转到槽”:
Qt Creator会自动生成如下代码,首先是槽函数的声明:
// 槽函数声明
private slots:
void on_btn_cancel_clicked(bool checked);
槽函数实现,如下:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_btn_cancel_clicked(bool checked) { }
使用这种方法不需要使用connect函数将信号与槽函数做连接。 这里槽函数的命名有一定的规则,一般是 on_objectname_signal 这样来命名的。这种方法优点是减少了手动敲代码的工作量,缺点是究竟有哪些信号与槽函数做了连接不易被发现,没有connect 函数看起来直观。
在Qt中,断开(disconnect)信号与槽函数之间的连接是一个重要的操作,特别是在对象被销毁、连接不再需要或者需要改变信号与槽的关联关系时。Qt提供了
QObject::disconnect
函数来断开信号与槽之间的连接。以下是几种常见的断开信号与槽的方法:
如果你想要断开一个对象上所有信号与某个特定槽的连接,可以使用以下方式:
// Qt4
QObject::disconnect(sender, nullptr, receiver, SLOT(specificSlot()));
// Qt5
QObject::disconnect(sender, nullptr, receiver, &ReceiverClass::specificSlot);
如果你想要断开某个特定信号与所有槽的连接,可以这样做:
// Qt4
QObject::disconnect(sender, SIGNAL(specificSignal()), nullptr, nullptr);
// Qt5
QObject::disconnect(sender, &SenderClass::specificSignal, nullptr, nullptr);
如果你想要断开某个特定信号与某个特定槽的连接,可以这样做:
// Qt4
QObject::disconnect(sender, SIGNAL(specificSignal()), receiver, SLOT(specificSlot()));
// Qt5
QObject::disconnect(sender, &SenderClass::specificSignal, receiver, &ReceiverClass::specificSlot);
注意:当QObject被销毁时,Qt会自动断开所有与之相关的信号和槽的连接。但是,如果你想要更早地断开连接,或者想要确保某些连接在对象销毁之前被断开,那么你需要显式调用QObject::disconnect。
信号与槽函数参数类型要匹配,例如:信号函数带的参数是QString,那么绑定的槽函数要么不带参数(表示不接收数据),要么是跟信号函数一样的参数,下面的代码编译时会报错,因为信号与槽函数参数类型不匹配,如下:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); // 登录按键绑定槽函数 connect(ui->btn_ok, &QPushButton::clicked, this, &MainWindow::login); m_pStudent = new Student(this); m_pTeacher = new Teacher(this); // 信号函数不带参数,槽函数带参数(QString),信号与槽函数参数类型不匹配编译时报错 void (Teacher::*pfnTeacherSignal)() = &Teacher::hungry; void (Student::*pfnStudentSlot)(QString) = &Student::treat; QMetaObject::Connection conn = connect(m_pTeacher, pfnTeacherSignal, m_pStudent, pfnStudentSlot); }
Qt的信号与槽机制提供了一种强大且灵活的方式来处理对象间的通信,它是Qt框架中不可或缺的一部分。通过信号和槽,开发者可以轻松地构建出响应式、事件驱动的用户界面和应用程序逻辑。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。