【Qt基础知识】信号与槽

一、信号与槽的概念

Qt的信号与槽(Signals and Slots)是一种用于对象间通信和事件处理的机制。它是Qt框架的核心特性之一,提供了一种灵活而强大的方式来连接对象之间的交互和响应。

在Qt中,一个信号(Signal)是一个特殊的成员函数,用于表示某个事件的发生。一个槽(Slot)是另一个成员函数,用于处理信号所代表的事件。信号可以被发射(emitted),而槽函数可以被连接(connected)到一个或多个信号上。

使用信号和槽的步骤如下:

  1. 定义信号:在类中声明一个信号,使用signals关键字,可以带有参数。
  2. 定义槽函数:在类中声明一个槽函数,使用slots关键字,与信号的参数列表一致
  3. 连接信号和槽:使用connect函数将信号与槽函数连接起来。连接可以在运行时进行,也可以在代码中静态地指定。
  4. 发射信号:在适当的时机,使用emit关键字发射信号。一旦信号被发射,所有连接的槽函数将会被调用。

二、槽函数

本小节主要介绍Qt5以后的两种槽函数的写法,=普通槽函数和自动槽函数的写法。

在Qt中,槽函数的写法要求如下:

  1. 槽函数必须位于类的声明中,并且需要在类的声明中使用slots关键字进行声明。
  2. 槽函数可以具有参数,可以根据需要在槽函数的声明和定义中定义参数类型和数量。
  3. 槽函数的返回类型必须为void,因为槽函数不允许有返回值。

1. 普通槽函数

  1. 使用slots宏:在类的声明部分,使用slots宏标记槽函数。

    cpp 复制代码
    class MyObject : public QObject
    {
        Q_OBJECT
    
    public slots:
        void mySlot(int value, QString text);
    };
  2. 槽函数的实现:槽函数的实现与以前的版本相同。在类的实现部分,按照声明中的函数签名来实现槽函数。

    cpp 复制代码
    void MyObject::mySlot(int value, QString text)
    {
        // 在槽函数中执行操作
        qDebug() << "Received value:" << value;
        qDebug() << "Received text:" << text;
    }

2. 自动槽函数

在Qt5中,有一种自动匹配槽函数的命名格式,称为自动槽(Auto Slots)。自动槽使用一定的命名规则,使得当信号触发时,会自动调用对应的槽函数,无需手动连接信号和槽。

自动槽的命名规则如下:

  1. 槽函数的名称以on开头,后面跟着发射信号的对象名称和信号名,使用下划线连接。
  2. 槽函数的参数列表与信号的参数列表一致。

假设有一个名为btn的按钮对象,根据命名规则,这个函数会被视为一个槽函数,并且会在名为btn的对象发出clicked()信号时自动调用,而不需要通过connect函数连接。

cpp 复制代码
class MyWidget : public QWidget
{
    Q_OBJECT

public:
    MyWidget(QWidget *parent = nullptr);

private slots:
    void on_btn_clicked();
};

三、信号

在Qt中,自定义信号的格式要求如下:

  1. 自定义信号必须在类的声明中使用signals关键字进行声明。
  2. 自定义信号可以具有参数,您可以根据需要在信号声明中定义参数类型和数量。
  3. 自定义信号的返回类型必须为void,因为信号只是一种通知机制,不返回任何值。

以下是一个示例,展示了一个带有参数的自定义信号的声明:

cpp 复制代码
class MyObject : public QObject
{
    Q_OBJECT

signals:
    void myCustomSignal(int value, QString text);
};

当想要发出自定义信号时,可以使用emit关键字,如下所示:

cpp 复制代码
void MyObject::someFunction()
{
    int value = 42;
    QString text = "Hello";
    emit myCustomSignal(value, text);
}

在上述示例中,someFunction函数发出了myCustomSignal信号,并传递了一个int类型的值和一个QString类型的文本。其他对象可以通过连接自定义信号与槽函数来响应自定义信号的发出,并在信号触发时执行相应的操作。

需要注意的是,为了使用自定义信号,类必须继承自QObject,并且在类的声明中包含Q_OBJECT宏以启用元对象系统的功能。

1. 跨UI发送信号

假设现在有一个Widget,点击按钮打开一个Dialog,通过点击Dialog中的按钮向Widget中发送信息并展示。

我们的设计逻辑应该是:

  1. Dialog::on_btn_clicked():这是一个槽函数,负责处理按钮的点击事件。当按钮被点击时,它将发送信号。

    cpp 复制代码
    void Dialog::on_btn_clicked()
    {
        Data data{"Tom", 333, 18, true};
        emit(sig_btn_click(data));
    }
  2. Dialog::signal_btn_clicked(Data data):这是一个自定义信号,它将与按钮的点击事件相关联。信号可以带有参数,这里的参数是一个名为data的自定义数据对象。

    cpp 复制代码
     void sig_btn_click(Data data);
  3. Widget::slot_dialog_btn_clicked(Data data):这是一个槽函数,用于接收来自Dialog的信号并处理传递的数据。这个槽函数在QWidget中定义,当signal_btn_clicked信号被触发时,它将被调用。(这里借助lambda函数实现)

  4. Widget::on_btn_dialog_clicked():这是一个槽函数,用于接受QWidget的按钮信号。生成Dialog窗口,并通过connect函数连接跨UI的信号与槽。

    cpp 复制代码
    void Widget::on_btn_dialog_clicked()
    {
        Dialog dialog;
        // slot_dialog_btn_clicked(Data data)
        connect(&dialog, &Dialog::sig_btn_click, this, [=](Data data){
            QString message = "name: " + QString::fromStdString(data.name) + \
                              " id: " + QString::number(data.id) + \
                              " age: " + QString::number(data.age) + \
                              " gender: " + QString{data.gender ? "male" : "female"};
            ui->lineEdit_cout->setText(message);
        });
    
        dialog.exec();
    }

2. 跨线程发送信号

(Todo:待完善)

子线程无法修改UI,必须通过发送信号的方式进行修改。但是Lambda可以直接修改。

四、connect 函数

connect方法是Qt框架中用于建立信号与槽之间连接的函数。它允许您将一个对象的信号与另一个对象的槽函数关联起来,以便在信号触发时调用槽函数。

connect方法的语法如下:

cpp 复制代码
bool QObject::connect(const QObject *sender, const char *signal,
                      const QObject *receiver, const char *slot,
                      Qt::ConnectionType type = Qt::AutoConnection);
  • sender:发送信号的对象。
  • signal:信号的名称,以字符串形式表示。信号是在发送对象中声明的。
  • receiver:接收信号的对象。
  • slot:槽函数的名称,以字符串形式表示。槽函数是在接收对象中声明的。
  • type:连接类型,用于指定连接的行为。它可以是以下值之一:
    • Qt::AutoConnection:根据上下文自动选择连接类型。
      • 如果信号发送者和接收者位于同一线程中,则使用DirectConnection。这意味着信号触发时,槽函数会立即在同一线程中执行。
      • 如果信号发送者和接收者位于不同线程中,则使用QueuedConnection。这意味着信号会被放入接收者对象所在线程的事件队列中,在事件循环处理时调用槽函数。(需要确保槽在接受方的线程中执行。
      • 需要注意的是,当使用AutoConnection时,如果信号发送者和接收者位于不同线程,并且没有开启事件循环,那么连接将会失败。因此,在多线程的情况下,确保在接收者线程中有事件循环是非常重要的。
    • Qt::DirectConnection:直接连接,即在信号发出时立即调用槽函数。
    • Qt::QueuedConnection:队列连接,即将信号放入接收对象的事件队列中,在事件循环处理时调用槽函数。
      • 当控制权回到接受者所依附线程的事件循环时,槽函数被调用。
      • 槽函数在接收者所依附线程执行。
    • Qt::BlockingQueuedConnection:阻塞队列连接,与Qt::QueuedConnection类似,但在槽函数返回之前阻塞发送对象的执行。
    • Qt::UniqueConnection:唯一连接,确保重复连接不会创建。
  • connect方法的返回值是一个布尔值,表示连接是否成功建立。

五、重载信号、槽的 connect 方式

使用QOverload,它的作用是返回重载函数的指针,模板参数为函数类型列表。

cpp 复制代码
// 基于方法地址匹配 connect, 使用 QOverload 实现
connect(comboBox, QOverload<const QString&>::of(&QComboBox::currentIndexChanged),
    label, &QLabel::setText);

connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
    label, &QLabel::setText);
相关推荐
一律清风10 小时前
QT-文件创建时间修改器
c++·qt
不知所云,10 小时前
qt cmake自定义资源目录,手动加载资源(图片, qss文件)
开发语言·qt
Death20011 小时前
Qt 6 相比 Qt 5 的主要提升与更新
开发语言·c++·qt·交互·数据可视化
机器视觉知识推荐、就业指导11 小时前
使用Qt实现实时数据动态绘制的折线图示例
开发语言·qt
Geek之路15 小时前
QT系统学习篇(1)
开发语言·qt·学习
Geek之路1 天前
Qt系统学习篇(6)-QMainWindow
数据库·qt·学习
初阳7851 天前
【Qt】控件概述(2)—— 按钮类控件
开发语言·qt
初阳7851 天前
【Qt】控件概述(3)—— 显示类控件
开发语言·qt
efls1111 天前
Qt_绘图
开发语言·c++·qt
TravisBytes1 天前
在 Qt 项目中使用 spdlog 的全攻略
开发语言·c++·qt