(三)QT——信号与槽机制——计数器程序
目录
前言
信号(Signal)与槽(Slot)的定义
一、系统自带的信号和槽
二、自定义信号和槽
三、信号和槽的扩展
四、Lambda 表达式
总结
前言
信号与槽机制是 Qt 中的一种重要的通信机制,用于不同对象之间的事件响应和交互。简单来说,信号(signal)和槽(slot)是 Qt 中对象之间沟通的桥梁。
信号与槽的特性
- 解耦:发出信号的对象和接收信号的对象之间没有直接依赖,提供了松耦合的设计。
- 类型安全:信号和槽通过参数类型匹配来确保类型安全。
- 支持多个槽:一个信号可以连接多个槽,多个信号也可以连接同一个槽。
连接方式
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
被连接起来。当按钮被点击时,标签的文本会被更新。
重要概念
- 信号:表示一个事件或状态的变化(例如按钮点击)。
- 槽:是对信号的响应,通常是一个成员函数,可以执行与信号相关的动作。
- 连接:通过
connect
函数来将信号和槽关联起来,使得当信号发生时,槽能自动执行。
这种机制极大地方便了对象间的通信,也使得 Qt 中的事件驱动编程更加简洁和高效。
一、系统自带的信号和槽
在 Qt 中,系统自带了许多信号和槽,这些信号和槽提供了对常见事件的处理,比如用户输入、窗口状态变化等。以下是一些常见的系统自带的信号和槽:
常见的系统信号
-
QPushButton
clicked()
:当按钮被点击时发出的信号。pressed()
:当按钮被按下时发出的信号。released()
:当按钮被释放时发出的信号。
-
QLineEdit
textChanged(const QString &text)
:当文本改变时发出的信号。editingFinished()
:当用户完成编辑时发出的信号(例如按下 Enter 键)。returnPressed()
:当用户按下 Enter 键时发出的信号。
-
QComboBox
currentIndexChanged(int index)
:当选中的项改变时发出的信号。activated(int index)
:当某个选项被激活时发出的信号。
-
QCheckBox
toggled(bool checked)
:当复选框的状态改变时发出的信号(勾选或取消勾选)。
-
QSlider
valueChanged(int value)
:当滑动条的值发生变化时发出的信号。sliderPressed()
:当滑动条被按下时发出的信号。sliderReleased()
:当滑动条被释放时发出的信号。
-
QMainWindow
closeEvent(QCloseEvent *event)
:当窗口关闭时发出的信号。resizeEvent(QResizeEvent *event)
:当窗口被调整大小时发出的信号。moveEvent(QMoveEvent *event)
:当窗口位置发生改变时发出的信号。
-
QTimer
timeout()
:当定时器超时时发出的信号。
-
QFileDialog
fileSelected(const QString &file)
:当用户选择了文件时发出的信号。directoryEntered(const QString &dir)
:当用户进入一个目录时发出的信号。
-
QApplication
aboutToQuit()
:当应用程序即将退出时发出的信号。
常见的系统槽
-
QWidget
setText(const QString &text)
:设置部件的文本(通常用于QLabel
、QLineEdit
等)。setChecked(bool checked)
:设置复选框的状态(用于QCheckBox
)。resize(int width, int height)
:调整部件的大小。setVisible(bool visible)
:设置部件是否可见。
-
QPushButton
setEnabled(bool enabled)
:设置按钮是否启用。setText(const QString &text)
:设置按钮的文本。
-
QLineEdit
clear()
:清除输入框中的内容。setText(const QString &text)
:设置输入框的文本。
-
QSlider
setValue(int value)
:设置滑动条的值。setOrientation(Qt::Orientation orientation)
:设置滑动条的方向(水平或垂直)。
-
QComboBox
setCurrentIndex(int index)
:设置当前选中的项。
-
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 中,除了使用系统自带的信号和槽外,你也可以自定义信号和槽,以便实现更灵活的事件处理和对象间的通信。
自定义信号和槽的步骤
-
定义信号
- 信号通常在类的
public
部分定义,并使用signals
关键字声明。 - 信号可以有参数,类型和个数可以根据需要定义。
- 信号通常在类的
-
定义槽
- 槽通常在类的
public
、protected
或private
部分定义,并使用slots
关键字声明。 - 槽函数是普通的成员函数,可以接收信号传递的参数。
- 槽通常在类的
-
连接信号与槽
- 使用
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 中,信号和槽可以是同一对象中的方法,也可以是不同对象之间的通信。信号与槽的连接方式有两种:
-
传统连接方式(基于指针和成员函数的连接)
connect(sender, SIGNAL(signalName()), receiver, SLOT(slotName()));
-
新的连接方式(使用函数指针和类型安全的连接)
connect(sender, &Sender::signalName, receiver, &Receiver::slotName);
推荐使用新的连接方式,它提供类型检查,避免了传统连接方式可能带来的错误。
信号和槽的类型
-
无参数的信号和槽:信号和槽不接受任何参数,直接触发某些操作。
signals:void someSignal();public slots:void someSlot();
-
带参数的信号和槽:信号传递数据,槽接受这些数据进行处理。
signals:void countChanged(int count);public slots:void updateLabel(int count);
-
返回值:Qt 的信号与槽机制不允许槽函数有返回值,因为信号发射后并不会等待槽返回。槽函数只能执行动作。
-
多个信号连接到一个槽:一个槽可以响应多个信号。
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 中创建项目
- 启动 Qt Creator,选择
File
->New File or Project
。 - 选择
Application
->Qt Widgets Application
,然后点击Choose
。 - 输入项目名称(例如
CounterApp
),选择一个存储路径,然后点击Next
。 - 在
Qt Kit Selection
中,选择适合的 Qt Kit(通常是默认的)。然后点击Next
。 - 选择生成的文件名、类名(例如
MainWindow
),然后点击Finish
完成项目创建。
2. 创建 counter.h
和 counter.cpp
文件
接下来,在 Qt Creator 中创建一个新的文件来包含 CounterWidget
类。
2.1 创建 counter.h
文件
- 右键点击项目目录中的
Header Files
文件夹,选择New
->C++ Header File
。 - 输入文件名为
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
文件
- 右键点击项目目录中的
Source Files
文件夹,选择New
->C++ Source File
。 - 输入文件名为
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. 构建和运行
- 构建项目:点击 Qt Creator 上方的绿色播放按钮,或者选择
Build
->Build Project
来编译项目。 - 运行项目:构建完成后,点击绿色播放按钮来运行程序。
你将看到一个窗口,其中有一个按钮,点击按钮会增加计数器值,并通过 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
。
- 在设计视图中,从左侧的 Widget Box 拖拽一个
-
打开
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();
}
编译和运行
- 在 Qt Creator 中,点击 Build > Build Project(或者按
Ctrl+R
)来构建项目。 - 构建成功后,点击 Run 按钮启动应用程序。
- 在弹出的窗口中,点击 Click me 按钮,你会看到一个消息框弹出,显示
Button was clicked!
。
完成
至此,你已经创建了一个使用 Lambda 表达式作为槽函数响应按钮点击事件的 Qt 项目。
- 创建项目:使用 Qt Creator 创建一个新的 Qt Widgets 应用程序。
- 设计界面:通过 UI 设计器添加一个
QPushButton
。- 编写代码:在
mainwindow.cpp
中使用 Lambda 表达式响应按钮的点击信号。- 构建和运行:编译并运行项目,验证按钮点击事件的处理。
小结:
- Lambda 表达式提供了一种简洁且高效的方式来定义小范围的函数,特别适合用作回调函数或在算法中传递函数。
- 可以通过捕获外部变量、定义参数列表、推导返回类型等方式灵活使用 Lambda。
- Lambda 表达式不仅可以简化代码,还能提高代码的可读性,特别是在复杂的函数传递场景中。
总结
Qt 的信号和槽机制为开发者提供了一种优雅、灵活且类型安全的方式来处理对象间的通信。通过理解和利用这一机制,可以显著提高应用程序的模块化、可维护性和可扩展性。
- 松耦合:信号和槽使对象之间的通信更加松散,无需对象彼此了解。
- 类型安全:编译时检查信号和槽的签名是否匹配,避免了运行时错误。
- 灵活性:可以动态连接和断开信号和槽,支持多种连接模式(例如,一个信号连接多个槽,或多个信号连接一个槽)。
相关文章:
(三)QT——信号与槽机制——计数器程序
目录 前言 信号(Signal)与槽(Slot)的定义 一、系统自带的信号和槽 二、自定义信号和槽 三、信号和槽的扩展 四、Lambda 表达式 总结 前言 信号与槽机制是 Qt 中的一种重要的通信机制,用于不同对象之间的事件响…...
玩转大语言模型——配置图数据库Neo4j(含apoc插件)并导入GraphRAG生成的知识图谱
系列文章目录 玩转大语言模型——使用langchain和Ollama本地部署大语言模型 玩转大语言模型——ollama导入huggingface下载的模型 玩转大语言模型——langchain调用ollama视觉多模态语言模型 玩转大语言模型——使用GraphRAGOllama构建知识图谱 玩转大语言模型——完美解决Gra…...
从0开始,来看看怎么去linux排查Java程序故障
一,前提准备 最基本前提:你需要有liunx环境,如果没有请参考其它文献在自己得到local建立一个虚拟机去进行测试。 有了虚拟机之后,你还需要安装jdk和配置环境变量 1. 安装JDK(以OpenJDK 17为例) 下载JDK…...
Java实现LFU缓存策略实战
LFU算法原理在Java中示例实现集成Caffeine的W-TinyLFU策略缓存实战总结LFU与LRU稍有不同,LFU是根据数据被访问的频率来决定去留。尽管它考虑了数据的近期使用,但它不会区分数据的首次访问和后续访问,淘汰那些访问次数最少的数据。 这种缓存策略主要用来处理以下场景: 数据…...
LeetCode--84. 柱状图中最大的矩形【单调栈】
84. 柱状图中最大的矩形 正文 题目如下 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 这道题暴力很简单,但是时间复杂度是O(N^2)…...
感悟人生路
匆匆复匆匆,新春时光沙漏里,过了又来,只是那时和此时。累了,行过百公里,灯光交汇处,都是向往幸福之所。一路长虹,速度跟上节奏,福祉盈门,出入平安。 跨越时空ÿ…...
Autogen_core源码:_agent_instantiation.py
目录 _agent_instantiation.py代码代码解释代码示例示例 1:使用 populate_context 正确设置上下文示例 2:尝试在上下文之外调用 current_runtime 和 current_agent_id示例 3:模拟 AgentRuntime 使用 AgentInstantiationContext _agent_instan…...
开源智慧园区管理系统如何重塑企业管理模式与运营效率
内容概要 在如今快速发展的商业环境中,企业面临着日益复杂的管理挑战。开源智慧园区管理系统应运而生,旨在通过技术创新来应对这些挑战。它不仅是一个简单的软件工具,而是一个全面整合大数据、物联网和智能化功能的综合平台,为企…...
网络工程师 (9)文件管理
一、树形目录结构 (一)定义与构成 树形目录结构由一个根目录和若干层子文件夹(或称为子目录)组成,它像一棵倒置的树。这棵树的根称为根文件夹(也叫根目录),从根向下,每一…...
Java小白入门教程:内置数据类型(四类八种)和引用数据类型
目录 一、内置数据类型(四类八种) 1. 整数类型(四种子类型) 2. 浮点类型(两种子类型) 3. 字符类型(一种子类型) 4. 布尔类型(一种子类型) 二、引用数据类…...
pytorch图神经网络处理图结构数据
人工智能例子汇总:AI常见的算法和例子-CSDN博客 图神经网络(Graph Neural Networks,GNNs)是一类能够处理图结构数据的深度学习模型。图结构数据由节点(vertices)和边(edges)组成&a…...
CRC校验详解
CRC校验即循环冗余校验(Cyclic Redundancy Check),是基于数据计算一组效验码,用于核对数据传输过程中是否被更改或传输错误。首先看两个概念,后续会用到。 模2除法:也叫模2运算,就是结果除以2后取余数。模2除法每一位除的结果不影响其它位,即不向上一位借位,所以实际…...
使用where子句筛选记录
默认情况下,SearchCursor将返回一个表或要素类的所有行.然而在很多情况下,常常需要某些条件来限制返回行数. 操作方法: 1.打开IDLE,加载先前编写的SearchCursor.py脚本 2.添加where子句,更新SearchCursor()函数,查找记录中有<>文本的<>字段 with arcpy.da.Searc…...
人工智能导论--第1章-知识点与学习笔记
请根据教材内容,完成进行下面的作业任务。必须包含有教材的具体内容,不能是生成式AI系统的生成内容。 参考教材1.1节的内容介绍,谈谈你对“智能”的认识。思维能力是智能的重要特征之一,结合教材1.1.2节内容,从思维的…...
半导体SAP管理系统:数字化转型的驱动力
在当今全球科技竞争日益激烈的背景下,半导体行业作为信息技术的基石,其生产效率、质量控制和成本优化直接关系到企业的市场竞争力和可持续发展。随着数字化转型的深入,半导体企业纷纷寻求高效、智能的管理系统以提升运营效率。SAP管理系统&am…...
Cursor 简介:AI 如何改变编程体验
在软件开发领域,效率和质量始终是开发者们追求的目标。随着人工智能技术的飞速发展,编程工具也在不断进化,Cursor 便是这一趋势下的产物。它不仅仅是一个代码编辑器,更是一个集成了 AI 能力的智能编程助手,旨在通过 AI…...
苯乙醇苷类化合物的从头生物合成-文献精读108
Complete pathway elucidation of echinacoside in Cistanche tubulosa and de novo biosynthesis of phenylethanoid glycosides 管花肉苁蓉中松果菊苷全生物合成途径解析及苯乙醇苷类化合物的从头生物合成 摘要 松果菊苷(ECH)是最具代表性的苯乙醇苷…...
CSS:跑马灯
<div class"swiper-container"><div class"swiper-wrapper"><!-- 第一组 --><div class"item" v-for"item in cardList" :key"first-item.id"><img :src"item.image" alt""…...
简单的爱心跳动表白网页(附源码)
一:准备工作 在开始之前,确保已经具备基础的 HTML、CSS 和 JavaScript 知识。同时,也要准备好一个代码编辑器,比如 VS Code 或 Sublime Text。接下来,我们需要创建三个文件:index.html、styles.css 和 scr…...
遗传算法【Genetic Algorithm(GA)】求解函数最大值(MATLAB and Python实现)
一、遗传算法基础知识 来自B站视频的笔记: 【超容易理解】手把手逐句带你解读并实现遗传算法的MATLAB编程(结合理论基础)_哔哩哔哩_bilibili 1、遗传算法 使用“适者生存”的原则,在遗传算法的每一代中,…...
HTB:Administrator[WriteUP]
目录 连接至HTB服务器并启动靶机 信息收集 使用rustscan对靶机TCP端口进行开放扫描 将靶机TCP开放端口号提取并保存 使用nmap对靶机TCP开放端口进行脚本、服务扫描 使用nmap对靶机TCP开放端口进行漏洞、系统扫描 使用nmap对靶机常用UDP端口进行开放扫描 使用nmap对靶机…...
正则表达式中常见的贪婪词
1. * 含义:匹配前面的元素零次或者多次。示例:对于正则表达式 a*,在字符串 "aaaa" 中,它会匹配整个 "aaaa",因为它会尽可能多地匹配 a 字符。代码示例(Python):…...
高阶C语言|深入理解字符串函数和内存函数
文章目录 前言1.求字符串长度1.1 字符串长度函数:strlen模拟实现 2.长度不受限制的字符串函数2.1 字符串拷贝函数:strcpy模拟实现 2.2 字符串连接函数:strcat模拟实现 2.3 字符串比较函数:strcmp模拟实现 3.长度受限制的字符串函数…...
grpc 和 http 的区别---二进制vsJSON编码
gRPC 和 HTTP 是两种广泛使用的通信协议,各自适用于不同的场景。以下是它们的详细对比与优势分析: 一、核心特性对比 特性gRPCHTTP协议基础基于 HTTP/2基于 HTTP/1.1 或 HTTP/2数据格式默认使用 Protobuf(二进制)通常使用 JSON/…...
基于 RAG 的聊天机器人的追踪、日志和指标:结合 Elastic 的 OpenTelemetry 分发
作者:来自 Elastic Bahubali Shetti 如何使用 Elasticsearch 观察基于 OpenAI RAG 的应用程序。使用 OpenTelemetry 对应用程序进行检测,收集日志、跟踪、指标,并了解 LLM 在 Kubernetes 和 Docker 上使用 OpenTelemetry 的 Elastic 分发的表…...
6.二分算法
二分 二分算法,也称为二分查找或折半查找,是一种在有序数组中查找特定元素的高效算法。以下是 C 中二分算法的相关内容: 算法原理 二分算法的基本思想是将有序数组分成两部分,然后将目标值与中间元素进行比较。如果目标值等于中…...
Android Studio 正式版 10 周年回顾,承载 Androider 的峥嵘十年
Android Studio 1.0 宣发于 2014 年 12 月,而现在时间来到 2025 ,不知不觉间 Android Studio 已经陪伴 Androider 走过十年历程。 Android Studio 10 周年,也代表着了我的职业生涯也超十年,现在回想起来依然觉得「唏嘘」ÿ…...
“LoRA技术中参数初始化策略:为何A参数采用正态分布而B参数初始化为0”
在LoRA(Low-Rank Adaptation)中,参数A和B的初始化策略是经过精心设计的,以确保模型训练的稳定性和有效性。具体来说,参数A通常被初始化为正态分布,而参数B则初始化为0。这样的设计有以下几个优点࿱…...
PyTorch中的movedim、transpose与permute
在PyTorch中,movedim、transpose 和 permute这三个操作都可以用来重新排列张量(tensor)的维度,它们功能相似却又有所不同。 movedim 🔗 torch.movedim 用途:将张量的一个或多个维度移动到新的位置。参数&…...
灰色预测模型
特点: 利用少量、不完全的信息 预测的是指数型的数值 预测的是比较近的数据 灰色生成数列原理: 累加生成: 累减生成:通过累减生成还原成原始数列。 加权相邻生成:(会更接近每月中旬,更推荐…...
Java 16进制 10进制 2进制数 相互的转换
在 Java 中,进行进制之间的转换时,除了功能的正确性外,效率和安全性也很重要。为了确保高效和相对安全的转换,我们通常需要考虑: 性能:使用内置的转换方法,如 Integer.toHexString()、Integer.…...
CPU 100% 出现系统中断 怎么解决
CPU 100% 出现系统中断 怎么解决 电脑开机时会掉帧,切换到桌面时就会卡顿,然后打开任务管理器就会看到系统中断的cpu占用率达到100%,过一段时间再打开还是会有显示100%的占用率,这个问题怎么解决? 文章目录 CPU 100% …...
Python-基于mediapipe,pyautogui,cv2和numpy的电脑手势截屏工具(进阶版)
前言:在我们的日常生活中,手机已经成为我们每天工作,学习,生活的一个不可或缺的部分。众所周知:为了我们的使用方便,手机里面的很多功能非常人性化,既便捷又高效,其中就有手机的截屏方式,它们花样繁多,如三指截屏,手势截屏等。那么怎么在电脑里面也实现这个功能呢?…...
设计转换Apache Hive的HQL语句为Snowflake SQL语句的Python程序方法
首先,根据以下各类HQL语句的基本实例和官方文档记录的这些命令语句各种参数设置,得到各种HQL语句的完整实例,然后在Snowflake的官方文档找到它们对应的Snowflake SQL语句,建立起对应的关系表。在这个过程中要注意HQL语句和Snowfla…...
2025年人工智能技术:Prompt与Agent的发展趋势与机遇
文章目录 一、Prompt与Agent的定义与区别(一)定义(二)区别二、2025年Prompt与Agent的应用场景(一)Prompt的应用场景(二)Agent的应用场景三、2025年Prompt与Agent的适合群体(一)Prompt适合的群体(二)Agent适合的群体四、2025年Prompt与Agent的发展机遇(一)Prompt的…...
[论文总结] 深度学习在农业领域应用论文笔记14
当下,深度学习在农业领域的研究热度持续攀升,相关论文发表量呈现出迅猛增长的态势。但繁荣背后,质量却不尽人意。相当一部分论文内容空洞无物,缺乏能够落地转化的实际价值,“凑数” 的痕迹十分明显。在农业信息化领域的…...
STM32 ADC单通道配置
硬件电路 接线图: ADC基本结构图 代码配置 根据基本结构框图 1.定义结构体变量 //定义结构体变量 GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体变量 ADC_InitTypeDef ADC_InitStructure; //定义ADC结构体变量 2.开启RCC时钟 ADC、GPIO的时钟&#x…...
Go学习:Go语言中if、switch、for语句与其他编程语言中相应语句的格式区别
Go语言中的流程控制语句逻辑结构与其他编程语言类似,格式有些不同。Go语言的流程控制中,包括if、switch、for、range、goto等语句,没有while循环。 目录 1. if 语句 2. switch语句 3. for语句 4. range语句 5. goto语句(不常用…...
Day50:字典的合并
在 Python 中,字典是一个可变的数据类型,经常需要将多个字典合并成一个字典。合并字典的方式有多种,今天我们将学习几种常见的方法。 1. 使用 update() 方法合并字典 update() 方法可以用来将一个字典中的键值对添加到另一个字典中。如果目…...
【C++动态规划 离散化】1626. 无矛盾的最佳球队|2027
本文涉及知识点 C动态规划 离散化 LeetCode1626. 无矛盾的最佳球队 假设你是球队的经理。对于即将到来的锦标赛,你想组合一支总体得分最高的球队。球队的得分是球队中所有球员的分数 总和 。 然而,球队中的矛盾会限制球员的发挥,所以必须选…...
LeetCode 热题 HOT 100
你好呀,欢迎来到 Dong雨 的技术小栈 🌱 在这里,我们一同探索代码的奥秘,感受技术的魅力 ✨。 👉 我的小世界:Dong雨 📌 分享我的学习旅程 🛠️ 提供贴心的实用工具 💡 记…...
从未标记图像中生成有标记图像特征的半监督分割方法
今天看到一篇文章很有意思,给大家分享一下。现在传统半监督分割网络训练时往往有标注数据与未标注数据分开训练,导致模型不好。这篇文章作者提出了一个很有意思的想法。它通过通道注意力从未标记的特征中重新加载标记的特征。这篇文章是AllSpark。 大家感…...
随笔 | 写在一月的最后一天
. 前言 这个月比预想中过的要快更多。突然回看这一个月,还有点不知从何提笔。 整个一月可以总结为以下几个关键词: 期许,保持期许出现休息 . 期许 关于期许,没有什么时候比一年伊始更适合设立目标和计划的了。但令人惭愧的…...
AI大模型开发原理篇-2:语言模型雏形之词袋模型
基本概念 词袋模型(Bag of Words,简称 BOW)是自然语言处理和信息检索等领域中一种简单而常用的文本表示方法,它将文本看作是一组单词的集合,并忽略文本中的语法、词序等信息,仅关注每个词的出现频率。 文本…...
DDD - 领域驱动设计分层架构:构建可演化的微服务架构
文章目录 引言1. 什么是DDD分层架构?1.1 DDD分层架构的演变1.2 四层架构的起源与问题1.3 依赖倒置和五层架构 2. DDD分层架构的核心层次2.1 用户接口层(User Interface Layer)2.2 应用层(Application Layer)2.3 领域层…...
Spring Boot项目如何使用MyBatis实现分页查询及其相关原理
写在前面:大家好!我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教。我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油,冲鸭&#x…...
CSS 值和单位详解:从基础到实战
CSS 值和单位详解:从基础到实战 1. 什么是 CSS 的值?示例代码:使用颜色关键字和 RGB 函数 2. 数字、长度和百分比2.1 长度单位绝对长度单位相对长度单位 2.2 百分比 3. 颜色3.1 颜色关键字3.2 十六进制 RGB 值3.3 RGB 和 RGBA 值3.4 HSL 和 H…...
搭建自己的专属AI——使用Ollama+AnythingLLM+Python实现DeepSeek本地部署
前言 最近DeepSeek模型非常火,其通过对大模型的蒸馏得到的小模型可以较轻松地在个人电脑上运行,这也使得我们有机会在本地构建一个专属于自己的AI,进而把AI“调教”为我们希望的样子。本篇文章中我将介绍如何使用OllamaAnythingLLMPython实现…...
【AI绘画】MidJourney关键词{Prompt}全面整理
AI绘画整理,MidJourney关键词。喜欢AI绘画的朋友必备,建议收藏,后面用到时供查阅使用。 1、光线与影子篇 中 英 闪耀的霓虹灯 shimmeringneon lights 黑暗中的影子 shadows in the dark 照亮城市的月光 moonlightilluminatingthe cit…...
如何将IP切换到海外:详细指南
在现代互联网应用中,IP地址成为了网络通信和数据交换的基础。然而,很多时候,由于区域限制或隐私保护的需求,用户可能需要将自己的IP地址切换到海外。无论是为了绕过地域限制访问内容,还是为了提高隐私安全,…...