(三)QT——信号与槽机制——计数器程序

目录

前言

信号(Signal)与槽(Slot)的定义

一、系统自带的信号和槽

二、自定义信号和槽

三、信号和槽的扩展

四、Lambda 表达式

总结


前言

信号与槽机制是 Qt 中的一种重要的通信机制,用于不同对象之间的事件响应和交互。简单来说,信号(signal)和槽(slot)是 Qt 中对象之间沟通的桥梁。

信号与槽的特性

  1. 解耦:发出信号的对象和接收信号的对象之间没有直接依赖,提供了松耦合的设计。
  2. 类型安全:信号和槽通过参数类型匹配来确保类型安全。
  3. 支持多个槽:一个信号可以连接多个槽,多个信号也可以连接同一个槽。

连接方式

connect 方法支持多种连接方式,最常见的三种是:

  • 默认连接:使用 Qt 的自动机制,通常是直接调用槽函数。
  • 直接连接:信号发出时,槽函数会立即执行,通常适用于 GUI 线程。
  • 队列连接:信号发出时,槽函数会在事件队列中排队执行,适用于跨线程通信。

跨线程信号与槽

当信号与槽位于不同线程时,Qt 会自动处理信号的传递和槽函数的调用,以保证线程安全。

这种机制是 Qt 中实现事件驱动和响应式编程的关键,可以用来处理用户交互、网络事件、定时器事件等。


信号(Signal)与槽(Slot)的定义

  • 信号(Signal):表示某个事件的发生。例如,按钮被点击时会发出一个信号 clicked()
  • 槽(Slot):响应信号的动作。每个槽是一个普通的成员函数,用于处理信号发出的事件。

连接信号与槽

为了让一个对象收到另一个对象发出的信号,必须通过 connect 函数来建立连接。通常的连接方式如下:

connect(sender, &Sender::signalName, receiver, &Receiver::slotName);
  • sender:发出信号的对象。
  • signalName:信号的名字。
  • receiver:接收信号的对象。
  • slotName:接收信号后调用的槽函数。

示例

假设有一个按钮和一个标签,点击按钮时,标签的文本会改变。

// 假设有一个QPushButton和QLabel
QPushButton *button = new QPushButton("Click me", this);
QLabel *label = new QLabel("Hello, Qt!", this);// 连接信号与槽
connect(button, &QPushButton::clicked, this, [=]() {label->setText("Button clicked!");
});

在这个例子中,clicked 信号和修改标签文本的槽通过 connect 被连接起来。当按钮被点击时,标签的文本会被更新。

重要概念

  1. 信号:表示一个事件或状态的变化(例如按钮点击)。
  2. :是对信号的响应,通常是一个成员函数,可以执行与信号相关的动作。
  3. 连接:通过 connect 函数来将信号和槽关联起来,使得当信号发生时,槽能自动执行。

这种机制极大地方便了对象间的通信,也使得 Qt 中的事件驱动编程更加简洁和高效。


一、系统自带的信号和槽

在 Qt 中,系统自带了许多信号和槽,这些信号和槽提供了对常见事件的处理,比如用户输入、窗口状态变化等。以下是一些常见的系统自带的信号和槽:

常见的系统信号

  1. QPushButton

    • clicked():当按钮被点击时发出的信号。
    • pressed():当按钮被按下时发出的信号。
    • released():当按钮被释放时发出的信号。
  2. QLineEdit

    • textChanged(const QString &text):当文本改变时发出的信号。
    • editingFinished():当用户完成编辑时发出的信号(例如按下 Enter 键)。
    • returnPressed():当用户按下 Enter 键时发出的信号。
  3. QComboBox

    • currentIndexChanged(int index):当选中的项改变时发出的信号。
    • activated(int index):当某个选项被激活时发出的信号。
  4. QCheckBox

    • toggled(bool checked):当复选框的状态改变时发出的信号(勾选或取消勾选)。
  5. QSlider

    • valueChanged(int value):当滑动条的值发生变化时发出的信号。
    • sliderPressed():当滑动条被按下时发出的信号。
    • sliderReleased():当滑动条被释放时发出的信号。
  6. QMainWindow

    • closeEvent(QCloseEvent *event):当窗口关闭时发出的信号。
    • resizeEvent(QResizeEvent *event):当窗口被调整大小时发出的信号。
    • moveEvent(QMoveEvent *event):当窗口位置发生改变时发出的信号。
  7. QTimer

    • timeout():当定时器超时时发出的信号。
  8. QFileDialog

    • fileSelected(const QString &file):当用户选择了文件时发出的信号。
    • directoryEntered(const QString &dir):当用户进入一个目录时发出的信号。
  9. QApplication

    • aboutToQuit():当应用程序即将退出时发出的信号。

常见的系统槽

  1. QWidget

    • setText(const QString &text):设置部件的文本(通常用于 QLabelQLineEdit 等)。
    • setChecked(bool checked):设置复选框的状态(用于 QCheckBox)。
    • resize(int width, int height):调整部件的大小。
    • setVisible(bool visible):设置部件是否可见。
  2. QPushButton

    • setEnabled(bool enabled):设置按钮是否启用。
    • setText(const QString &text):设置按钮的文本。
  3. QLineEdit

    • clear():清除输入框中的内容。
    • setText(const QString &text):设置输入框的文本。
  4. QSlider

    • setValue(int value):设置滑动条的值。
    • setOrientation(Qt::Orientation orientation):设置滑动条的方向(水平或垂直)。
  5. QComboBox

    • setCurrentIndex(int index):设置当前选中的项。
  6. QTimer

    • start(int msec):启动定时器。
    • stop():停止定时器。

示例:使用系统信号与槽

假设我们有一个 QPushButtonQLabel,点击按钮后更改标签的文本。这个操作可以通过系统自带的信号与槽机制实现。

QPushButton *button = new QPushButton("Click me", this);
QLabel *label = new QLabel("Hello", this);// 连接按钮的 clicked() 信号到标签的 setText() 槽
connect(button, &QPushButton::clicked, label, &QLabel::setText);

在这个例子中:

  • QPushButtonclicked() 信号会在按钮被点击时触发。
  • QLabelsetText() 槽会被调用,将标签的文本更改为按钮的文本。

Qt 提供了丰富的系统信号和槽,开发者可以直接使用这些现成的信号和槽来处理常见的交互和事件,而无需自己实现基础的事件响应逻辑。


二、自定义信号和槽

在 Qt 中,除了使用系统自带的信号和槽外,你也可以自定义信号和槽,以便实现更灵活的事件处理和对象间的通信。

自定义信号和槽的步骤

  1. 定义信号

    • 信号通常在类的 public 部分定义,并使用 signals 关键字声明。
    • 信号可以有参数,类型和个数可以根据需要定义。
  2. 定义槽

    • 槽通常在类的 publicprotectedprivate 部分定义,并使用 slots 关键字声明。
    • 槽函数是普通的成员函数,可以接收信号传递的参数。
  3. 连接信号与槽

    • 使用 connect() 函数将信号与槽连接起来,这样当信号被发射时,槽就会自动被调用。

示例:自定义信号和槽

假设我们有一个 Counter 类,它包含一个自定义信号 countChanged(int count) 和一个槽 updateLabel(int count),当计数值变化时,发出信号,并通过槽更新标签的文本。

步骤 1:定义自定义信号和槽
#include <QWidget>
#include <QPushButton>
#include <QLabel>class Counter : public QWidget
{Q_OBJECT  // 这是必需的宏,用于启用信号与槽机制public:explicit Counter(QWidget *parent = nullptr);signals:void countChanged(int count);  // 自定义信号public slots:void updateLabel(int count);   // 自定义槽private:int counter;QLabel *label;QPushButton *button;
};Counter::Counter(QWidget *parent) : QWidget(parent), counter(0)
{label = new QLabel("Count: 0", this);button = new QPushButton("Increase", this);// 布局设置QVBoxLayout *layout = new QVBoxLayout(this);layout->addWidget(label);layout->addWidget(button);// 连接信号与槽connect(button, &QPushButton::clicked, this, &Counter::onButtonClicked);connect(this, &Counter::countChanged, this, &Counter::updateLabel);
}void Counter::onButtonClicked()
{counter++;  // 增加计数器的值emit countChanged(counter);  // 发射 countChanged 信号
}void Counter::updateLabel(int count)
{label->setText("Count: " + QString::number(count));  // 更新标签文本
}
代码解释
  • Q_OBJECT 宏是定义自定义信号和槽时必须要加的宏,它启用 Qt 的信号和槽机制。
  • countChanged(int count) 是自定义的信号,表示计数器的值变化。
  • updateLabel(int count) 是自定义的槽,用于更新标签上的文本。
  • 当按钮被点击时,onButtonClicked() 槽函数会增加计数器的值并发射 countChanged() 信号。
  • 通过 connect() 将按钮的 clicked() 信号连接到 onButtonClicked() 槽,将 countChanged() 信号连接到 updateLabel() 槽。

信号与槽的连接

在 Qt 中,信号和槽可以是同一对象中的方法,也可以是不同对象之间的通信。信号与槽的连接方式有两种:

  1. 传统连接方式(基于指针和成员函数的连接)

    connect(sender, SIGNAL(signalName()), receiver, SLOT(slotName()));
    
  2. 新的连接方式(使用函数指针和类型安全的连接)

    connect(sender, &Sender::signalName, receiver, &Receiver::slotName);
    

推荐使用新的连接方式,它提供类型检查,避免了传统连接方式可能带来的错误。

信号和槽的类型

  1. 无参数的信号和槽:信号和槽不接受任何参数,直接触发某些操作。

    signals:void someSignal();public slots:void someSlot();
    
  2. 带参数的信号和槽:信号传递数据,槽接受这些数据进行处理。

    signals:void countChanged(int count);public slots:void updateLabel(int count);
  3. 返回值:Qt 的信号与槽机制不允许槽函数有返回值,因为信号发射后并不会等待槽返回。槽函数只能执行动作。

  4. 多个信号连接到一个槽:一个槽可以响应多个信号。

    connect(button, &QPushButton::clicked, this, &Counter::onButtonClicked);
    connect(timer, &QTimer::timeout, this, &Counter::onButtonClicked);
    

小结

  • 自定义信号和槽提供了更灵活的事件处理机制,可以使得不同对象之间能够通过信号传递信息。
  • 使用 Q_OBJECT 宏来启用信号和槽机制。
  • 通过 connect() 将信号和槽连接起来,形成信号与槽的连接。
  • 可以自定义信号和槽的参数类型,支持复杂的事件传递。

这种机制使得 Qt 中的对象可以高度解耦,一个对象的事件发生并不直接影响另一个对象的行为,而是通过信号与槽来实现响应。

下面是一个完整的 Qt 程序示例,展示如何定义自定义信号和槽、连接信号与槽,并通过按钮点击事件更新标签文本。这个例子使用 QPushButton 来增加一个计数器,点击按钮时会触发自定义信号 countChanged(int),并通过自定义槽 updateLabel(int) 更新显示的文本。

完整代码:

1. 在 Qt Creator 中创建项目

  1. 启动 Qt Creator,选择 File -> New File or Project
  2. 选择 Application -> Qt Widgets Application,然后点击 Choose
  3. 输入项目名称(例如 CounterApp),选择一个存储路径,然后点击 Next
  4. Qt Kit Selection 中,选择适合的 Qt Kit(通常是默认的)。然后点击 Next
  5. 选择生成的文件名、类名(例如 MainWindow),然后点击 Finish 完成项目创建。

2. 创建 counter.hcounter.cpp 文件

接下来,在 Qt Creator 中创建一个新的文件来包含 CounterWidget 类。

2.1 创建 counter.h 文件
  1. 右键点击项目目录中的 Header Files 文件夹,选择 New -> C++ Header File
  2. 输入文件名为 counter.h,然后点击 NextFinish

counter.h 文件中,添加以下代码:

#ifndef COUNTER_H
#define COUNTER_H#include <QWidget>
#include <QPushButton>
#include <QLabel>class CounterWidget : public QWidget
{Q_OBJECTpublic:CounterWidget(QWidget *parent = nullptr);signals:void countChanged(int newCount);public slots:void increment();void updateLabel(int newCount);private:int count;QLabel *label;
};#endif // COUNTER_H
2.2 创建 counter.cpp 文件
  1. 右键点击项目目录中的 Source Files 文件夹,选择 New -> C++ Source File
  2. 输入文件名为 counter.cpp,然后点击 NextFinish

counter.cpp 文件中,添加以下代码:

#include "counter.h"
#include <QVBoxLayout>
#include <QString>CounterWidget::CounterWidget(QWidget *parent): QWidget(parent), count(0)
{QPushButton *button = new QPushButton("Increment", this);label = new QLabel("Count: 0", this);QVBoxLayout *layout = new QVBoxLayout(this);layout->addWidget(button);layout->addWidget(label);connect(button, &QPushButton::clicked, this, &CounterWidget::increment);connect(this, &CounterWidget::countChanged, this, &CounterWidget::updateLabel);
}void CounterWidget::increment()
{count++;emit countChanged(count);
}void CounterWidget::updateLabel(int newCount)
{label->setText("Count: " + QString::number(newCount));
}

3. 修改 mainwindow.ui(可选)

如果你希望使用 Qt Designer 来编辑界面,可以在 mainwindow.ui 中添加一个 QWidget 来容纳 CounterWidget。不过,如果你想完全通过代码实现界面,也可以跳过这个步骤。

4. 修改 mainwindow.cpp 文件

打开 mainwindow.cpp 文件,修改代码以包含 counter.h 和使用 CounterWidget 类。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "counter.h"  // 引入 counter.h 文件MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 创建 CounterWidget 对象并设置为主窗口的中央小部件CounterWidget *counterWidget = new CounterWidget();setCentralWidget(counterWidget);
}MainWindow::~MainWindow()
{delete ui;
}

    5. 修改 .pro 文件

    确保 .pro 文件中包含了 counter.hcounter.cpp,以便编译器能够找到它们。

    .pro 文件中,添加以下内容:

    HEADERS += counter.h
    SOURCES += counter.cpp
    

    6. 构建和运行

    1. 构建项目:点击 Qt Creator 上方的绿色播放按钮,或者选择 Build -> Build Project 来编译项目。
    2. 运行项目:构建完成后,点击绿色播放按钮来运行程序。

    你将看到一个窗口,其中有一个按钮,点击按钮会增加计数器值,并通过 QLabel 更新显示当前值。

    小结

    • 在 Qt Creator 中创建一个 Qt Widgets 应用程序项目。
    • 创建 counter.hcounter.cpp 文件,定义和实现 CounterWidget 类。
    • mainwindow.cpp 中引用 CounterWidget,并将其设置为主窗口的中心小部件。
    • 确保 .pro 文件包含新的头文件和源文件。
    • 构建并运行程序,查看结果。

    通过这些步骤,你应该能够正确地在 Qt 中实现并运行你所描述的计数器程序。


    三、信号和槽的扩展

    在 Qt 中,信号和槽机制是非常灵活且强大的,它不仅可以连接对象之间的通信,还支持各种扩展和自定义的用法。下面将介绍几种常见的信号和槽的扩展:

    1. 信号和槽的多参数传递

    • 信号和槽不仅支持传递一个参数,还可以传递多个参数。这使得你可以将更多的信息传递给槽函数。
    示例:传递多个参数
    signals:void dataProcessed(int value, QString message);public slots:void handleData(int value, QString message){qDebug() << "Received value:" << value << "Message:" << message;}void someFunction()
    {emit dataProcessed(42, "Data processed successfully!");
    }
    

    解释

    • 在信号 dataProcessed(int, QString) 中,我们可以传递两个参数:一个整数和一个字符串。
    • 在槽函数 handleData(int, QString) 中,我们可以接收到这两个参数,并进行处理。

    2. 信号和槽的返回值

    • 默认情况下,Qt 的信号和槽不支持返回值,因为信号发射后不会等待槽的返回。
    • 但你可以使用 自定义事件 或者通过信号/槽的另一个机制(比如返回的信号)来间接获取返回值。
    示例:信号返回值的间接处理
    signals:int requestData();  // 请求数据的信号public slots:int provideData(){return 42;  // 返回一个整数值}void someFunction()
    {int data = provideData();qDebug() << "Received data:" << data;
    }
    

    解释

    • requestData 信号通常用于请求数据。
    • provideData 槽提供了返回数据,但由于信号和槽机制本身不支持直接返回值,你需要通过其他方式(如信号或状态检查)间接获得数据。

    3. 使用 Lambda 表达式作为槽

    • 从 Qt5 开始,Qt 支持通过 Lambda 表达式来定义槽。这种方式可以更简洁地连接信号和槽,特别是当槽的实现比较简单时。
    示例:使用 Lambda 表达式作为槽
    connect(button, &QPushButton::clicked, this, [=]() {qDebug() << "Button clicked!";counter++;
    });
    

    解释

    • 通过 Lambda 表达式,我们可以直接在 connect() 函数中定义槽,避免了定义一个专门的槽函数。
    • 这里,按钮点击时会增加计数器并打印一条信息。

    4. 延迟信号和槽调用

    • 有时候你可能希望信号在某个特定时间或者延迟后发射,或者在槽中执行延迟操作。你可以通过 QTimer 等机制来实现延迟调用。
    示例:使用 QTimer 延迟调用槽
    signals:void timeoutSignal();public slots:void onTimeout(){qDebug() << "Timeout occurred!";}void startTimer()
    {QTimer::singleShot(1000, this, &Counter::onTimeout);  // 延迟 1 秒后调用 onTimeout 槽
    }
    

    解释

    • 使用 QTimer::singleShot() 可以在指定时间后自动发射一个信号,触发槽函数的调用。
    • 上述例子中,onTimeout() 槽会在 1 秒后被调用。

    5. 信号与多个槽连接

    • 一个信号可以连接多个槽,一个槽也可以响应多个信号。通过这种方式,可以实现更加复杂的事件响应机制。
    示例:信号与多个槽连接
    signals:void dataReceived(int value);public slots:void processData(int value){qDebug() << "Processing data:" << value;}void displayData(int value){qDebug() << "Displaying data:" << value;}void triggerSignals()
    {emit dataReceived(100);
    }void setupConnections()
    {connect(this, &Counter::dataReceived, this, &Counter::processData); // 数据处理connect(this, &Counter::dataReceived, this, &Counter::displayData); // 数据展示
    }
    

    解释

    • dataReceived(int) 信号连接到两个槽 processData(int)displayData(int),它们分别对信号做出不同的响应。
    • 每当 dataReceived 信号被发射时,两个槽都会被调用。

    6. 信号与槽的线程间通信

    • Qt 的信号与槽机制非常适合于多线程编程,特别是在不同线程间传递信号和处理数据时。
    • Qt 自动处理线程间的信号槽调用,它会将信号的传递放入接收线程的事件队列,从而避免直接在工作线程中操作 UI。
    示例:线程间的信号与槽
    class Worker : public QObject
    {Q_OBJECT
    public:void doWork(){emit workDone("Work completed");}signals:void workDone(QString result);public slots:void onWorkDone(QString result){qDebug() << result;}
    };void mainFunction()
    {Worker worker;QThread workerThread;worker.moveToThread(&workerThread);  // 将 worker 对象移动到 workerThread 中connect(&worker, &Worker::workDone, &worker, &Worker::onWorkDone);workerThread.start();// 发射信号emit worker.workDone("Task finished!");workerThread.quit();workerThread.wait();
    }
    

    解释

    • Worker 类中的 workDone 信号从工作线程发射,onWorkDone 槽在主线程中接收信号并处理。
    • 通过 moveToThread()Worker 对象移到另一个线程中,保证信号与槽机制可以跨线程工作。

    7. 信号和槽的优先级

    • Qt 支持为信号与槽连接设置优先级。这样可以控制多个槽的执行顺序。
    示例:信号与槽优先级
    connect(sender, SIGNAL(signalName()), receiver, SLOT(slotName()), Qt::HighPriority);
    

    解释

    • 信号与槽的连接可以设置优先级,默认为 Qt::NormalPriority,你可以将其设置为 Qt::HighPriorityQt::LowPriority,以控制槽的执行顺序。

    8. 手动发射信号

    • 除了自动发射信号,Qt 还允许你手动发射信号来触发相应的槽。
    示例:手动发射信号
    emit customSignal(param1, param2);
    

    解释

    • 在类中定义了一个信号,之后通过 emit 关键字手动发射信号,触发连接的槽。

    小结:

    • 多参数信号:信号和槽可以传递多个参数,增加事件传递的灵活性。
    • 返回值:Qt 的信号和槽不直接支持返回值,但可以通过其他方式间接获取结果。
    • Lambda 表达式:使用 Lambda 函数可以简化信号和槽的连接,减少代码量。
    • 延迟调用:通过 QTimer 等机制实现延迟信号或槽调用。
    • 线程间通信:Qt 支持跨线程的信号与槽通信,适合多线程编程。
    • 优先级控制:为信号和槽连接设置优先级,控制多个槽的执行顺序。

    信号和槽是 Qt 强大的事件驱动机制的核心,它提供了多种方式来扩展和定制行为,适应不同的应用场景。


    四、Lambda 表达式

    Lambda 表达式是一个匿名的函数对象,允许你在代码中定义一个没有名字的函数,并且可以将其作为参数传递给其他函数。它是一种简化的函数定义方式,尤其适用于短小的函数,或者仅在某些特定上下文中需要的函数。

    基本语法

    在 C++ 中,Lambda 表达式的基本语法如下:

    [capture](parameter_list) -> return_type { function_body }
    
    • capture:捕获外部变量的方式(如按值捕获、按引用捕获等)。
    • parameter_list:函数的参数列表(可以为空)。
    • return_type:函数的返回类型(可以省略,编译器自动推导)。
    • function_body:Lambda 表达式的函数体。

    1. 基本示例

    最简单的 Lambda 表达式没有参数、没有返回值,只执行一个简单的操作:

    #include <iostream>int main() {auto hello = []() {std::cout << "Hello, Lambda!" << std::endl;};hello();  // 调用 Lambdareturn 0;
    }
    

    解释

    • [] 表示 Lambda 表达式捕获外部变量,这里没有捕获任何外部变量。
    • () 表示参数列表,这里是空的,意味着 Lambda 没有参数。
    • {} 是函数体,其中 std::cout 打印一条消息。

    2. 带参数的 Lambda

    你可以为 Lambda 表达式提供参数,和普通函数一样:

    #include <iostream>int main() {auto add = [](int a, int b) -> int {return a + b;};int result = add(3, 4);  // 调用 Lambda,传入参数std::cout << "Sum: " << result << std::endl;return 0;
    }
    

    解释

    • int a, int b 是 Lambda 的参数列表。
    • -> int 表示返回类型是 int
    • return a + b; 计算并返回两个参数的和。

    3. 捕获外部变量

    Lambda 表达式可以捕获外部的变量,这样在 Lambda 中就可以使用这些变量。

    按值捕获(默认捕获方式)
    #include <iostream>int main() {int x = 10, y = 20;auto add = [x, y]() -> int {  // 按值捕获 x 和 yreturn x + y;};std::cout << "Sum: " << add() << std::endl;return 0;
    }
    

    解释

    • [x, y] 捕获了外部变量 xy 的值。
    • Lambda 可以在其体内使用捕获的值,但无法修改它们。
    按引用捕获
    #include <iostream>int main() {int x = 10, y = 20;auto add = [&x, &y]() -> int {  // 按引用捕获 x 和 yx = 30;  // 修改捕获的外部变量return x + y;};std::cout << "Sum: " << add() << std::endl;  // 打印修改后的结果std::cout << "x after Lambda: " << x << std::endl;  // 输出修改后的 xreturn 0;
    }
    

    解释

    • [&x, &y] 表示按引用捕获 xy。在 Lambda 内部修改 x 会影响外部变量。
    • 结果中 x 被修改为 30,y 依然保持 20。
    捕获所有外部变量
    #include <iostream>int main() {int x = 10, y = 20;auto add = [&]() -> int {  // 捕获所有外部变量的引用x = 30;  // 修改 xreturn x + y;};std::cout << "Sum: " << add() << std::endl;std::cout << "x after Lambda: " << x << std::endl;  // 打印修改后的 xreturn 0;
    }
    

    解释

    • [&] 捕获了所有外部变量的引用,可以在 Lambda 中修改这些变量。

    4. 返回类型推导

    C++11 引入了 Lambda 的返回类型推导机制,这样就不需要显式地指定 -> return_type,编译器会根据 Lambda 函数体自动推导返回类型。

    #include <iostream>int main() {auto add = [](int a, int b) {return a + b;  // 编译器推导返回类型为 int};std::cout << "Sum: " << add(5, 7) << std::endl;return 0;
    }
    

    解释

    • 编译器会根据 return a + b; 自动推导出返回类型是 int

    5. 捕获特定变量

    你可以只捕获某些外部变量,而忽略其他变量。例如:

    #include <iostream>int main() {int x = 10, y = 20, z = 30;auto add = [x, &z]() -> int {  // 只按值捕获 x,按引用捕获 zz = 40;  // 修改 zreturn x + z;};std::cout << "Sum: " << add() << std::endl;std::cout << "z after Lambda: " << z << std::endl;  // z 被修改return 0;
    }
    

    解释

    • 这里只捕获了 x 按值捕获和 z 按引用捕获,y 没有被捕获。

    6. 使用 Lambda 表达式作为回调函数

    Lambda 表达式常用于作为回调函数,可以作为参数传递给其他函数。

    #include <iostream>
    #include <algorithm>
    #include <vector>int main() {std::vector<int> vec = {5, 2, 8, 1, 3};// 使用 Lambda 表达式进行排序std::sort(vec.begin(), vec.end(), [](int a, int b) -> bool {return a < b;  // 按升序排序});for (int num : vec) {std::cout << num << " ";}std::cout << std::endl;return 0;
    }
    

    解释

    • 使用 Lambda 表达式作为排序函数传递给 std::sort(),Lambda 定义了元素的比较规则。

    在 Qt 中,我们可以使用 Lambda 表达式作为槽函数来响应信号事件。以下是一个完整的示例,展示了如何使用 Lambda 表达式处理 QPushButton 的点击事件。

    • 打开 Qt Creator,点击 File > New File or Project
    • 选择 Application > Qt Widgets Application
    • 输入项目名称和保存位置,然后点击 Next
    • 选择适当的 Qt 版本和构建工具,点击 Next
    • 完成项目设置后,点击 Finish

    代码示例:

    • Project Explorer 中,找到并打开 mainwindow.ui 文件(用于 GUI 布局)和 mainwindow.cpp 文件(用于逻辑实现)。

    • 修改 mainwindow.ui,添加一个 QPushButton 元素:

      • 在设计视图中,从左侧的 Widget Box 拖拽一个 QPushButton 到主窗口中。
      • 设置按钮的 objectName 属性为 pushButton,文本设置为 Click me
    • 打开 mainwindow.cpp,在 MainWindow 类中实现 Lambda 表达式作为槽函数的代码。

    mainwindow.cpp(主要逻辑):

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include <QPushButton>
    #include <QMessageBox>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
    {ui->setupUi(this);// 获取 QPushButton 控件QPushButton *button = ui->pushButton;// 使用 Lambda 表达式作为槽函数响应按钮点击事件connect(button, &QPushButton::clicked, [&]() {// 当按钮被点击时,弹出消息框QMessageBox::information(this, "Message", "Button was clicked!");});
    }MainWindow::~MainWindow()
    {delete ui;
    }
    

    mainwindow.h(头文件,定义槽函数):

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H#include <QMainWindow>namespace Ui {
    class MainWindow;
    }class MainWindow : public QMainWindow
    {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;
    };#endif // MAINWINDOW_H
    

    配置 main.cpp

    这是 Qt 应用程序的入口文件,通常会自动生成。我们不需要做太多修改,以下是 main.cpp 的默认代码:

    #include "mainwindow.h"
    #include <QApplication>int main(int argc, char *argv[])
    {QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
    }
    

    编译和运行

    1. 在 Qt Creator 中,点击 Build > Build Project(或者按 Ctrl+R)来构建项目。
    2. 构建成功后,点击 Run 按钮启动应用程序。
    3. 在弹出的窗口中,点击 Click me 按钮,你会看到一个消息框弹出,显示 Button was clicked!

    完成

    至此,你已经创建了一个使用 Lambda 表达式作为槽函数响应按钮点击事件的 Qt 项目。

    • 创建项目:使用 Qt Creator 创建一个新的 Qt Widgets 应用程序。
    • 设计界面:通过 UI 设计器添加一个 QPushButton
    • 编写代码:在 mainwindow.cpp 中使用 Lambda 表达式响应按钮的点击信号。
    • 构建和运行:编译并运行项目,验证按钮点击事件的处理。

    小结:

    • Lambda 表达式提供了一种简洁且高效的方式来定义小范围的函数,特别适合用作回调函数或在算法中传递函数。
    • 可以通过捕获外部变量、定义参数列表、推导返回类型等方式灵活使用 Lambda。
    • Lambda 表达式不仅可以简化代码,还能提高代码的可读性,特别是在复杂的函数传递场景中。

    总结

    Qt 的信号和槽机制为开发者提供了一种优雅、灵活且类型安全的方式来处理对象间的通信。通过理解和利用这一机制,可以显著提高应用程序的模块化、可维护性和可扩展性。

    • 松耦合:信号和槽使对象之间的通信更加松散,无需对象彼此了解。
    • 类型安全:编译时检查信号和槽的签名是否匹配,避免了运行时错误。
    • 灵活性:可以动态连接和断开信号和槽,支持多种连接模式(例如,一个信号连接多个槽,或多个信号连接一个槽)。

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/10714.html

    如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    【开源免费】基于SpringBoot+Vue.JS体育馆管理系统(JAVA毕业设计)

    本文项目编号 T 165 &#xff0c;文末自助获取源码 \color{red}{T165&#xff0c;文末自助获取源码} T165&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

    three.js+WebGL踩坑经验合集(6.1):负缩放,负定矩阵和行列式的关系(2D版本)

    春节忙完一轮&#xff0c;总算可以继续来写博客了。希望在春节假期结束之前能多更新几篇。 这一篇会偏理论多一点。笔者本没打算在这一系列里面重点讲理论&#xff0c;所以像相机矩阵推导这种网上已经很多优质文章的内容&#xff0c;笔者就一笔带过。 然而关于负缩放&#xf…

    [论文阅读] (37)CCS21 DeepAID:基于深度学习的异常检测(解释)

    祝大家新春快乐&#xff0c;蛇年吉祥&#xff01; 《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座&#xff0c;并分享给大家&#xff0c;希望您喜欢。由于作者的英文水平和学术能力不高&#xff0c;需要不断提升&#xff0c;所以还请大家批评指正&#xff0…

    AutoDL 云服务器:xfce4 远程桌面 终端乱码 + 谷歌浏览器

    /usr/bin/google-chrome-stable --no-sandbox --proxy-server"127.0.0.1:7890" 打开新的PowerShell ssh -p 54521 rootconnect.yza1.seetacloud.com /opt/TurboVNC/bin/vncserver -kill :1 rm -rf /tmp/.X1* USERroot /opt/TurboVNC/bin/vncserver :1 -desktop …

    Contrastive Imitation Learning

    机器人模仿学习中对比解码的一致性采样 摘要 本文中&#xff0c;我们在机器人应用的对比模仿学习中&#xff0c;利用一致性采样来挖掘演示质量中的样本间关系。通过在排序后的演示对比解码过程中&#xff0c;引入相邻样本间的一致性机制&#xff0c;我们旨在改进用于机器人学习…

    DeepSeek 遭 DDoS 攻击背后:DDoS 攻击的 “千层套路” 与安全防御 “金钟罩”

    当算力博弈升级为网络战争&#xff1a;拆解DDoS攻击背后的技术攻防战——从DeepSeek遇袭看全球网络安全新趋势 在数字化浪潮席卷全球的当下&#xff0c;网络已然成为人类社会运转的关键基础设施&#xff0c;深刻融入经济、生活、政务等各个领域。从金融交易的实时清算&#xf…

    【二叉搜索树】

    二叉搜索树 一、认识二叉搜索树二、二叉搜索树实现2.1插入2.2查找2.3删除 总结 一、认识二叉搜索树 二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称 BST&#xff09;是一种特殊的二叉树&#xff0c;它具有以下特征&#xff1a; 若它的左子树不为空&#xff0c;则…

    FreeRTOS学习 --- 中断管理

    什么是中断&#xff1f; 让CPU打断正常运行的程序&#xff0c;转而去处理紧急的事件&#xff08;程序&#xff09;&#xff0c;就叫中断 中断执行机制&#xff0c;可简单概括为三步&#xff1a; 1&#xff0c;中断请求 外设产生中断请求&#xff08;GPIO外部中断、定时器中断…

    使用 Ollama 和 Kibana 在本地为 RAG 测试 DeepSeek R1

    作者&#xff1a;来自 Elastic Dave Erickson 及 Jakob Reiter 每个人都在谈论 DeepSeek R1&#xff0c;这是中国对冲基金 High-Flyer 的新大型语言模型。现在他们推出了一款功能强大、具有开放权重的思想链推理 LLM&#xff0c;这则新闻充满了对行业意味着什么的猜测。对于那些…

    灵芝黄金基因组注释-文献精读109

    The golden genome annotation of Ganoderma lingzhi reveals a more complex scenario of eukaryotic gene structure and transcription activity 灵芝&#xff08;Ganoderma lingzhi&#xff09;的黄金基因组注释揭示了更复杂的真核基因结构和转录活性情况 摘要 背景 普遍…

    【回溯+剪枝】组合问题!

    文章目录 77. 组合解题思路&#xff1a;回溯剪枝优化 77. 组合 77. 组合 ​ 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 ​ 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,…

    Python的那些事第六篇:从定义到应用,Python函数的奥秘

    新月人物传记&#xff1a;人物传记之新月篇-CSDN博客 目录 一、函数的定义与调用 二、函数的参数 三、返回值&#xff08;return语句&#xff09; 四、作用域 五、匿名函数&#xff08;lambda表达式&#xff09; 六、总结 Python函数的奥秘&#xff1a;从定义到应用 编程…

    java后端之登录认证

    基础登录功能&#xff1a;根据提供的用户名和密码判断是否存在于数据库 LoginController.java RestController Slf4j public class LoginController {Autowiredprivate UserService userService;PostMapping("/login")public Result login(RequestBody User user) {…

    嵌入式知识点总结 Linux驱动 (七)-Linux驱动常用函数 uboot命令 bootcmd bootargs get_part env_get

    针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.ioremap 2.open 3.read 4.write 5.copy_to_user 6.copy_from_user 7.总结相关uboot命令以及函数 1.bootcmd 1.1.NAND Flash操作命令 2.bootargs 2.1 root 2.2 rootf…

    DS并查集(17)

    文章目录 前言一、何为并查集&#xff1f;二、并查集的实现&#xff1f;并查集的初始化查找元素所在的集合判断两个元素是否在同一个集合合并两个元素所在的集合获取并查集中集合的个数并查集的路径压缩 三、来两道题练练手&#xff1f;省份的数量等式方程的可满足性 总结 前言…

    程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<2>

    大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 今天我们来学习const修饰指针&#xff0c;包括const修饰变量&#xff0c;const修饰指针变量&#xff1b…

    DeepSeek 云端部署,释放无限 AI 潜力!

    1.简介 目前&#xff0c;OpenAI、Anthropic、Google 等公司的大型语言模型&#xff08;LLM&#xff09;已广泛应用于商业和私人领域。自 ChatGPT 推出以来&#xff0c;与 AI 的对话变得司空见惯&#xff0c;对我而言没有 LLM 几乎无法工作。 国产模型「DeepSeek-R1」的性能与…

    小程序的数据绑定与事件绑定

    1.数据绑定的基本原则 2.在data中定义页面的数据 3.Mustache语法的格式 &#xff08;其实可以把他理解为插值表达式&#xff09; 动态绑定属性 三元运算 算数运算 4.事件绑定 事件绑定基本使用

    实验一---典型环节及其阶跃响应---自动控制原理实验课

    一 实验目的 1.掌握典型环节阶跃响应分析的基本原理和一般方法。 2. 掌握MATLAB编程分析阶跃响应方法。 二 实验仪器 1. 计算机 2. MATLAB软件 三 实验内容及步骤 利用MATLAB中Simulink模块构建下述典型一阶系统的模拟电路并测量其在阶跃响应。 1.比例环节的模拟电路 提…

    Git进阶之旅:Git 多人合作

    项目克隆&#xff1a; git clone 仓库地址&#xff1a;把远程项目克隆到本地形成一个本地的仓库 克隆下来的仓库和远程仓库的名称一致 注意&#xff1a;git clone 远程仓库地址 远程仓库名&#xff1a;把远程仓库克隆下来&#xff0c;并自定义仓库名 多人协作&#xff1a; …