Qt 之 connect 信号和槽函数的连接问题、总结记录、 二

Qt 的 connect 函数有三种常见的用法:1.使用 SIGNALSLOT 宏、2.使用 &类名::函数名以及3.使用 Lambda 表达式。每种方法各有优缺点和注意事项。

1. 使用 SIGNALSLOT

connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(onPushButtonClicked()));

参数解析

  • sender:信号的发送者对象的指针,这里是 ui->pushButton
  • SIGNAL(clicked()):信号的成员函数的字符串形式,其中 clicked 是信号函数的名称。
  • receiver:信号的接收者对象的指针,这里是 this(当前对象)。
  • SLOT(onPushButtonClicked()):槽函数的字符串形式,其中 onPushButtonClicked 是槽函数的名称。

优点

  • 适用于 Qt 4 及更早版本。
  • 直观,容易理解和使用。

缺点

  • 依赖字符串匹配,容易出错,编译时不会检查函数签名,只有在运行时出错。 其中 SIGNAL() 和 SLOT() 是宏,使用宏将信号和槽函数转换为字符串,编译器无法对字符串进行类型检查,因此在编译时无法发现连接错误。如果信号或槽的名称拼写错误或者类型不匹配,只有在运行时才会报错,不利于代码调试和维护,因此不推荐使用。
  • 难以进行代码重构(如重命名信号或槽时需要手动更新字符串)。
  • 缺乏类型安全性。

注意事项

  • 确保信号和槽的字符串匹配正确。
  • 使用前需要在头文件中声明槽函数。

2. 使用 &类名::函数名

connect(ui->pushButton_2, &QPushButton::clicked, this, &Widget::onSetBlockedSignalStatus);

参数解析

  • sender:信号的发送者对象的指针,这里是 ui->pushButton_2
  • &QPushButton::clicked:信号的成员函数指针,其中 QPushButton 是发送者对象的类名,clicked 是信号函数的名称。
  • receiver:信号的接收者对象的指针,这里是 this(当前对象)。
  • &Widget::onSetBlockedSignalStatus:槽函数的成员函数指针,其中 Widget 是接收者对象的类名,onSetBlockedSignalStatus 是槽函数的名称。

优点

  • 适用于 Qt 5 及更高版本。
  • 类型安全,编译时会检查信号和槽的匹配性。编译器可以在编译时检查到连接错误。如果信号或槽函数的名称拼写错误或者类型不匹配,编译器会报错,而不是等到运行时才发现错误。
  • 支持重载函数:支持连接重载函数的信号与槽,编译器会根据函数参数的类型自动匹配合适的重载函数。
  • 支持 Lambda 表达式:你也可以在 connect() 中使用 Lambda 表达式作为槽函数,使得连接更加灵活。
  • 更易于代码重构和维护。

缺点

  • 需要更详细的语法,可能对初学者不太友好。

注意事项

  • 确保信号和槽函数的签名匹配。
  • 槽函数必须在头文件中声明。

3. 使用 Lambda 表达式

connect(ui->pushButton, &QPushButton::clicked, [=]() {
    ui->label->setText("Hello Qt");
});

参数解析

  • sender:信号的发送者对象的指针,这里是 ui->pushButton
  • &QPushButton::clicked:信号的成员函数指针,其中 QPushButton 是发送者对象的类名,clicked 是信号函数的名称。
  • receiver:由于使用 Lambda 表达式,这里没有明确的接收者对象指针。
  • Lambda 表达式:信号触发时要执行的代码块,直接在 connect 语句中定义。[=] 是捕获列表,用于捕获外部变量。

优点

  • 适用于 Qt 5 及更高版本。
  • 灵活,不需要在类中声明槽函数。
  • 可以直接在 connect 语句中定义要执行的代码,简化代码逻辑。

缺点

  • Lambda 表达式中的代码不易复用。
  • 过多使用可能导致代码难以阅读和维护。

注意事项

  • Lambda 表达式中的捕获列表需要正确捕获外部变量(如 [=][&] 或具体变量名)。
  • Lambda 表达式中的代码应保持简洁。
  • Lambda表达式是C++ 11 的内容,所以,需要再Pro项目文件中加入 CONFIG += C++ 11

4.ui界面--转到槽--功能

补充:在 Qt Designer 中也可以通过图形界面将信号与槽连接起来。也就是我们在ui界面常用的“”转到槽“功能。具体实现原理笔者也不太清楚,之前看到过一篇文章说也是实现类似信号与槽连接的,这里想到了就提一下。

5.例子,展示了三种 connect 用法:

#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>

class Widget : public QWidget {
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr) : QWidget(parent) {
        QVBoxLayout *layout = new QVBoxLayout(this);
        
        QPushButton *button1 = new QPushButton("Button 1", this);
        QPushButton *button2 = new QPushButton("Button 2", this);
        QPushButton *button3 = new QPushButton("Button 3", this);
        QLabel *label = new QLabel("Label", this);

        layout->addWidget(button1);
        layout->addWidget(button2);
        layout->addWidget(button3);
        layout->addWidget(label);

        // 使用 SIGNAL 和 SLOT 宏
        connect(button1, SIGNAL(clicked()), this, SLOT(onButton1Clicked()));

        // 使用 &类名::函数名
        connect(button2, &QPushButton::clicked, this, &Widget::onButton2Clicked);

        // 使用 Lambda 表达式
        connect(button3, &QPushButton::clicked, [=]() {
            label->setText("Hello Qt");
        });
    }

public slots:
    void onButton1Clicked() {
        qDebug() << "Button 1 clicked!";
    }

    void onButton2Clicked() {
        qDebug() << "Button 2 clicked!";
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    Widget w;
    w.show();

    return app.exec();
}

#include "main.moc"

5.总结

  1. 使用 SIGNALSLOT:适用于旧版本的 Qt,容易理解但缺乏类型安全性,容易出错。
  2. 使用 &类名::函数名:类型安全,适用于现代 Qt 版本,适合复杂项目和长期维护。
  3. 使用 Lambda 表达式:灵活简洁,适合简短的处理逻辑,不需要在类中声明槽函数,但不易复用。

选择哪种方法取决于具体的需求和项目的复杂性。对于简单的操作,Lambda 表达式非常方便;而对于大型项目,推荐使用类型安全的 &类名::函数名 方式

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用Qt信号记录按钮点击次数的示例代码: ``` #include <QtWidgets> class Counter : public QWidget { Q_OBJECT public: Counter(QWidget *parent = nullptr); private: QLabel *valueLabel; QPushButton *countButton; int count; private slots: void incrementCount(); }; Counter::Counter(QWidget *parent) : QWidget(parent), count(0) { valueLabel = new QLabel("0", this); valueLabel->setAlignment(Qt::AlignCenter); valueLabel->setFixedSize(100, 50); countButton = new QPushButton("Count", this); countButton->setFixedSize(100, 50); connect(countButton, SIGNAL(clicked()), this, SLOT(incrementCount())); QHBoxLayout *layout = new QHBoxLayout(); layout->addWidget(valueLabel); layout->addWidget(countButton); setLayout(layout); } void Counter::incrementCount() { count++; valueLabel->setText(QString::number(count)); } int main(int argc, char *argv[]) { QApplication app(argc, argv); Counter counter; counter.show(); return app.exec(); } ``` 在这个示例中,Counter类继承自QWidget,它有一个值标签和一个计数按钮。在构造函数中,我们创建了这些小部件并将它们添加到布局中。我们还将计数按钮的clicked信号连接到incrementCount。 incrementCount只是简单地增加计数并将其显示在值标签上。 当我们运行这个应用程序时,它会显示一个窗口,其中包含一个值标签和一个计数按钮。每次单击计数按钮时,计数将增加,并且值标签将更新以反映新计数值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值