(三)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):设置部件的文本(通常用于 QLabel、QLineEdit 等)。
    • 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():停止定时器。

示例:使用系统信号与槽

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

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

在这个例子中:

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

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


二、自定义信号和槽

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

自定义信号和槽的步骤

  1. 定义信号

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

    • 槽通常在类的 public、protected 或 private 部分定义,并使用 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.h 和 counter.cpp 文件

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

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

在 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,然后点击 Next 和 Finish。

在 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.h 和 counter.cpp,以便编译器能够找到它们。

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

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

    6. 构建和运行

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

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

    小结

    • 在 Qt Creator 中创建一个 Qt Widgets 应用程序项目。
    • 创建 counter.h 和 counter.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::HighPriority 或 Qt::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] 捕获了外部变量 x 和 y 的值。
    • 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] 表示按引用捕获 x 和 y。在 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.tpcf.cn/web/67974.html

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

    相关文章

    蓝桥杯备赛题目练习(一)

    一. 口算练习题 ## 题目描述 王老师正在教简单算术运算。细心的王老师收集了 i 道学生经常做错的口算题&#xff0c;并且想整理编写成一份练习。 编排这些题目是一件繁琐的事情&#xff0c;为此他想用计算机程序来提高工作效率。王老师希望尽量减少输入的工作量&#xff0c;比…

    深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据

    深入探讨&#xff1a;服务器如何响应前端请求及后端如何查看前端提交的数据 一、服务器如何响应前端请求 前端与后端的交互主要通过 HTTP 协议实现。以下是详细步骤&#xff1a; 1. 前端发起 HTTP 请求 GET 请求&#xff1a;用于从服务器获取数据。POST 请求&#xff1a;用…

    毫秒级响应的VoIP中的系统组合推荐

    在高并发、低延迟、毫秒级响应的 VoIP 场景中&#xff0c;选择合适的操作系统组合至关重要。以下是针对 Ubuntu linux-lowlatency、CentOS Stream kernel-rt 和 Debian 自定义 PREEMPT_RT 的详细对比及推荐&#xff1a; 1. 系统组合对比 特性Ubuntu linux-lowlatencyCentO…

    【LeetCode 刷题】回溯算法(4)-排列问题

    此博客为《代码随想录》二叉树章节的学习笔记&#xff0c;主要内容为回溯算法排列问题相关的题目解析。 文章目录 46.全排列47.全排列 II 46.全排列 题目链接 class Solution:def permute(self, nums: List[int]) -> List[List[int]]:res, path [], []used [0] * len(n…

    基于多智能体强化学习的医疗AI中RAG系统程序架构优化研究

    一、引言 1.1 研究背景与意义 在数智化医疗飞速发展的当下,医疗人工智能(AI)已成为提升医疗服务质量、优化医疗流程以及推动医学研究进步的关键力量。医疗 AI 借助机器学习、深度学习等先进技术,能够处理和分析海量的医疗数据,从而辅助医生进行疾病诊断、制定治疗方案以…

    深度学习篇---张量数据流动处理

    文章目录 前言第一部分&#xff1a;张量张量的基本概念1.维度标量&#xff08;0维&#xff09;向量&#xff08;1维&#xff09;矩阵&#xff08;2维&#xff09;三维张量 2.形状 张量运算1.基本运算加法减法乘法除法 2.广播3.变形4.转置5.切片6.拼接7.矩阵分解8.梯度运算&…

    Redis --- 秒杀优化方案(阻塞队列+基于Stream流的消息队列)

    下面是我们的秒杀流程&#xff1a; 对于正常的秒杀处理&#xff0c;我们需要多次查询数据库&#xff0c;会给数据库造成相当大的压力&#xff0c;这个时候我们需要加入缓存&#xff0c;进而缓解数据库压力。 在上面的图示中&#xff0c;我们可以将一条流水线的任务拆成两条流水…

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

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

    2025年大年初一篇,C#调用GPU并行计算推荐

    C#调用GPU库的主要目的是利用GPU的并行计算能力&#xff0c;加速计算密集型任务&#xff0c;提高程序性能&#xff0c;支持大规模数据处理&#xff0c;优化资源利用&#xff0c;满足特定应用场景的需求&#xff0c;并提升用户体验。在需要处理大量并行数据或进行复杂计算的场景…

    Unity 2D实战小游戏开发跳跳鸟 - 计分逻辑开发

    上文对障碍物的碰撞逻辑进行了开发,接下来就是进行跳跳鸟成功穿越过障碍物进行计分的逻辑开发,同时将对应的分数以UI的形式显示告诉玩家。 计分逻辑 在跳跳鸟通过障碍物的一瞬间就进行一次计分,计分后会同步更新分数的UI显示来告知玩家当前获得的分数。 首先我们创建一个用…

    langchain基础(二)

    一、输出解析器&#xff08;Output Parser&#xff09; 作用&#xff1a;&#xff08;1&#xff09;让模型按照指定的格式输出&#xff1b; &#xff08;2&#xff09;解析模型输出&#xff0c;提取所需的信息 1、逗号分隔列表 CommaSeparatedListOutputParser&#xff1a;…

    游戏AI,让AI 玩游戏有什么作用?

    让 AI 玩游戏这件事远比我们想象的要早得多。追溯到 1948 年&#xff0c;图灵和同事钱伯恩共同设计了国际象棋程序 Turochamp。之所以设计这么个程序&#xff0c;图灵是想说明&#xff0c;机器理论上能模拟人脑能做的任何事情&#xff0c;包括下棋这样复杂的智力活动。 可惜的是…

    鸿蒙物流项目之基础结构

    目录&#xff1a; 1、项目结构2、三种包的区别和使用场景3、静态资源的导入4、颜色样式设置5、修改项目名称和图标6、静态包基础目录7、组件的抽离8、在功能模块包里面引用静态资源包的组件 1、项目结构 2、三种包的区别和使用场景 3、静态资源的导入 放在har包中&#xff0c;那…

    Cursor 与多语言开发:全栈开发的利器

    引言 全栈开发要求开发者跨越前端、后端、数据库甚至数据科学等多个技术领域&#xff0c;而不同技术栈往往需要切换工具和思维方式。Cursor 作为一款 AI 驱动的智能编程助手&#xff0c;凭借其对 20 编程语言 和主流框架的深度支持&#xff0c;正在成为全栈开发的“瑞士军刀”…

    算法设计-0-1背包动态规划(C++)

    一、问题阐述 0-1 背包问题的目标是在给定背包容量 W 的情况下&#xff0c;从 n 个物品中选择一些物品放入背包&#xff0c;使得背包中物品的总价值最大。每个物品只能选择一次&#xff08;即要么放入背包&#xff0c;要么不放入&#xff09;。 二、代码 #include <iostr…

    51c视觉~CV~合集10

    我自己的原文哦~ https://blog.51cto.com/whaosoft/13241694 一、CV创建自定义图像滤镜 热图滤镜 这组滤镜提供了各种不同的艺术和风格化光学图像捕捉方法。例如&#xff0c;热滤镜会将图像转换为“热图”&#xff0c;而卡通滤镜则提供生动的图像&#xff0c;这些图像看起来…

    全栈开发:使用.NET Core WebAPI构建前后端分离的核心技巧(二)

    目录 配置系统集成 分层项目使用 筛选器的使用 中间件的使用 配置系统集成 在.net core WebAPI前后端分离开发中&#xff0c;配置系统的设计和集成是至关重要的一部分&#xff0c;尤其是在管理不同环境下的配置数据时&#xff0c;配置系统需要能够灵活、可扩展&#xff0c…

    上海路网道路 水系铁路绿色住宅地工业用地面图层shp格式arcgis无偏移坐标2023年

    标题和描述中提到的资源是关于2023年上海市地理信息数据的集合&#xff0c;主要包含道路、水系、铁路、绿色住宅区以及工业用地的图层数据&#xff0c;这些数据以Shapefile&#xff08;shp&#xff09;格式存储&#xff0c;并且是适用于ArcGIS软件的无偏移坐标系统。这个压缩包…

    Rust HashMap :当储物袋遇上物品清单

    开场白&#xff1a;哈希映射的魔法本质 在Rust的奇幻世界里&#xff0c;HashMap就像魔法师的储物袋&#xff1a; 键值对存储 → 每个物品都有专属咒语&#xff08;键&#xff09;和实体&#xff08;值&#xff09;快速查找 → 念咒瞬间召唤物品动态扩容 → 自动伸展的魔法空间…

    Spring Boot统一异常拦截实践指南

    Spring Boot统一异常拦截实践指南 一、为什么需要统一异常处理 在Web应用开发中&#xff0c;异常处理是保证系统健壮性和用户体验的重要环节。传统开发模式中常见的痛点包括&#xff1a; 异常处理逻辑分散在各个Controller中错误响应格式不统一敏感异常信息直接暴露给客户端…