(三)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.mzph.cn/web/67974.shtml

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

    相关文章

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

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

    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;那…

    51c视觉~CV~合集10

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

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

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

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

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

    使用 Elastic Cloud Hosted 优化长期数据保留:确保政府合规性和效率

    作者&#xff1a;来自 Elastic Jennie Davidowitz 在数字时代&#xff0c;州和地方政府越来越多地承担着管理大量数据的任务&#xff0c;同时确保遵守严格的监管要求。这些法规可能因司法管辖区而异&#xff0c;通常要求将数据保留较长时间 —— 有时从一年到七年不等。遵守刑事…

    Oracle Primavera P6 最新版 v24.12 更新 2/2

    目录 一. 引言 二. P6 EPPM 更新内容 1. 用户管理改进 2. 更轻松地标准化用户设置 3. 摘要栏标签汇总数据字段 4. 将里程碑和剩余最早开始日期拖到甘特图上 5. 轻松访问审计数据 6. 粘贴数据时排除安全代码 7. 改进了状态更新卡片视图中的筛选功能 8. 直接从活动电子…

    linux本地部署deepseek-R1模型

    国产开源大模型追平甚至超越了CloseAI的o1模型&#xff0c;大国崛起时刻&#xff01;&#xff01;&#xff01; DeepSeek R1 本地部署指南   在人工智能技术飞速发展的今天&#xff0c;本地部署AI模型成为越来越多开发者和企业关注的焦点。本文将详细介绍如何在本地部署DeepS…

    C基础寒假练习(2)

    一、输出3-100以内的完美数&#xff0c;(完美数&#xff1a;因子和(因子不包含自身)数本身 #include <stdio.h>// 函数声明 int isPerfectNumber(int num);int main() {printf("3-100以内的完美数有:\n");for (int i 3; i < 100; i){if (isPerfectNumber…

    有限元分析学习——Anasys Workbanch第一阶段笔记梳理

    第一阶段笔记主要源自于哔哩哔哩《ANSYS-workbench 有限元分析应用基础教程》 张晔 主要内容导图&#xff1a; 笔记导航如下&#xff1a; Anasys Workbanch第一阶段笔记(1)基本信息与结果解读_有限元分析变形比例-CSDN博客 Anasys Workbanch第一阶段笔记(2)网格单元与应力奇…

    html基本结构和常见元素

    html5文档基本结构 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>文档标题</title> </head> <body>文档正文部分 </body> </html> html文档可分为文档头和文档体…

    Cursor如何使用Google Gemini以及碰到的坑

    Cursor如何使用Google Gemini以及碰到的坑 Cursor介绍下载安装Google Gemini介绍Google Gemini 官网申请Google Gemini API网址 配置Cursor使用Google Gemini打开Corsur设置 Cursor介绍 ‌Cursor是一款基于人工智能的代码编辑器&#xff0c;旨在帮助开发者更高效地编写代码。‌…

    【云安全】云原生-K8S-简介

    K8S简介 Kubernetes&#xff08;简称K8S&#xff09;是一种开源的容器编排平台&#xff0c;用于管理容器化应用的部署、扩展和运维。它由Google于2014年开源并交给CNCF&#xff08;Cloud Native Computing Foundation&#xff09;维护。K8S通过提供自动化、灵活的功能&#xf…

    【C++】线程池实现

    目录 一、线程池简介线程池的核心组件实现步骤 二、C11实现线程池源码 三、线程池源码解析1. 成员变量2. 构造函数2.1 线程初始化2.2 工作线程逻辑 3. 任务提交(enqueue方法)3.1 方法签名3.2 任务封装3.3 任务入队 4. 析构函数4.1 停机控制 5. 关键技术点解析5.1 完美转发实现5…