Qt5 中的事件和信号
最后修改于 2023 年 10 月 18 日
在本 Qt5 C++ 编程教程中,我们讨论事件和信号。
事件是任何 GUI 程序中重要的一部分。 所有的 GUI 应用程序都是事件驱动的。 一个应用程序会响应在其生命周期中产生的不同事件类型。 事件主要由应用程序的用户生成。 但它们也可以通过其他方式生成,例如互联网连接、窗口管理器或计时器。 在事件模型中,有三个参与者
- 事件源
- 事件对象
- 事件目标
事件源 是其状态发生变化的对象。 它生成事件。 事件对象 (Event) 封装了事件源中的状态变化。 事件目标 是希望收到通知的对象。 事件源对象将处理事件的任务委托给事件目标。
当我们调用应用程序的 exec
方法时,应用程序进入主循环。 主循环获取事件并将它们发送给对象。 Qt 具有独特的信号和槽机制。 这种信号和槽机制是 C++ 编程语言的扩展。
信号和槽用于对象之间的通信。 当特定事件发生时,会发出一个 信号。 槽 是一个普通的 C++ 方法; 当连接到它的信号发出时,它会被调用。
Qt5 点击示例
第一个例子展示了一个非常简单的事件处理例子。 我们有一个按钮。 通过点击按钮,我们终止应用程序。
#pragma once #include <QWidget> class Click : public QWidget { public: Click(QWidget *parent = nullptr); };
这是头文件。
#include <QPushButton> #include <QApplication> #include <QHBoxLayout> #include "click.h" Click::Click(QWidget *parent) : QWidget(parent) { auto *hbox = new QHBoxLayout(this); hbox->setSpacing(5); auto *quitBtn = new QPushButton("Quit", this); hbox->addWidget(quitBtn, 0, Qt::AlignLeft | Qt::AlignTop); connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit); }
我们在窗口上显示一个 QPushButton
。
connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit);
connect
方法将一个信号连接到一个槽。 当我们点击退出按钮时,会生成 clicked
信号。 qApp
是一个指向应用程序对象的全局指针。 它定义在 <QApplication>
头文件中。 当 clicked 信号发出时,会调用 quit
方法。
#include <QApplication> #include "click.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Click window; window.resize(250, 150); window.setWindowTitle("Click"); window.show(); return app.exec(); }
这是主文件。

Qt5 按键
在下面的例子中,我们响应按键。
#pragma once #include <QWidget> class KeyPress : public QWidget { public: KeyPress(QWidget *parent = 0); protected: void keyPressEvent(QKeyEvent * e); };
这是 keypress.h
头文件。
#include <QApplication> #include <QKeyEvent> #include "keypress.h" KeyPress::KeyPress(QWidget *parent) : QWidget(parent) { } void KeyPress::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { qApp->quit(); } }
如果我们按下 Escape 键,应用程序将终止。
void KeyPress::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { qApp->quit(); } }
在 Qt5 中使用事件的一种方法是重新实现一个事件处理程序。 QKeyEvent
是一个事件对象,它保存有关发生了什么的信息。 在我们的例子中,我们使用事件对象来确定实际按下了哪个键。
#include <QApplication> #include "keypress.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); KeyPress window; window.resize(250, 150); window.setWindowTitle("Key press"); window.show(); return app.exec(); }
这是主文件。
QMoveEvent
QMoveEvent
类包含移动事件的事件参数。 移动事件被发送到已移动的小部件。
#pragma once #include <QMainWindow> class Move : public QWidget { Q_OBJECT public: Move(QWidget *parent = 0); protected: void moveEvent(QMoveEvent *e); };
这是 move.h
头文件。
#include <QMoveEvent> #include "move.h" Move::Move(QWidget *parent) : QWidget(parent) { } void Move::moveEvent(QMoveEvent *e) { int x = e->pos().x(); int y = e->pos().y(); QString text = QString::number(x) + "," + QString::number(y); setWindowTitle(text); }
在我们的代码编程示例中,我们响应移动事件。 我们确定窗口客户区左上角的当前 x、y 坐标,并将这些值设置为窗口的标题。
int x = e->pos().x(); int y = e->pos().y();
我们使用 QMoveEvent
对象来确定 x
、y
值。
QString text = QString::number(x) + "," + QString::number(y);
我们将整数值转换为字符串。
setWindowTitle(text);
setWindowTitle
方法将文本设置为窗口的标题。
#include <QApplication> #include "move.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Move window; window.resize(250, 150); window.setWindowTitle("Move"); window.show(); return app.exec(); }
这是主文件。

断开信号
信号可以与槽断开连接。 下一个例子展示了我们如何实现这一点。
#pragma once #include <QWidget> #include <QPushButton> class Disconnect : public QWidget { Q_OBJECT public: Disconnect(QWidget *parent = 0); private slots: void onClick(); void onCheck(int); private: QPushButton *clickBtn; };
在头文件中,我们声明了两个槽。 slots
不是一个 C++ 关键字,它是 Qt5 的扩展。 这些扩展由预处理器处理,在代码编译之前。 当我们在类中使用信号和槽时,我们必须在类定义的开头提供 Q_OBJECT
宏。 否则,预处理器会报错。
#include <QTextStream> #include <QCheckBox> #include <QHBoxLayout> #include "disconnect.h" Disconnect::Disconnect(QWidget *parent) : QWidget(parent) { QHBoxLayout *hbox = new QHBoxLayout(this); hbox->setSpacing(5); clickBtn = new QPushButton("Click", this); hbox->addWidget(clickBtn, 0, Qt::AlignLeft | Qt::AlignTop); QCheckBox *cb = new QCheckBox("Connect", this); cb->setCheckState(Qt::Checked); hbox->addWidget(cb, 0, Qt::AlignLeft | Qt::AlignTop); connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); connect(cb, &QCheckBox::stateChanged, this, &Disconnect::onCheck); } void Disconnect::onClick() { QTextStream out(stdout); out << "Button clicked" << endl; } void Disconnect::onCheck(int state) { if (state == Qt::Checked) { connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); } else { disconnect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); } }
在我们的例子中,我们有一个按钮和一个复选框。 复选框将一个槽连接到按钮的点击信号并断开连接。 此示例必须从命令行执行。
connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); connect(cb, &QCheckBox::stateChanged, this, &Disconnect::onCheck);
在这里,我们将信号连接到我们用户定义的槽。
void Disconnect::onClick() { QTextStream out(stdout); out << "Button clicked" << endl; }
如果我们点击“Click”按钮,我们会将“Button clicked”文本发送到终端窗口。
void Disconnect::onCheck(int state) { if (state == Qt::Checked) { connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); } else { disconnect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); } }
在 onCheck
槽中,我们根据接收到的状态连接或断开 onClick
槽与 Click 按钮的连接。
#include <QApplication> #include "disconnect.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Disconnect window; window.resize(250, 150); window.setWindowTitle("Disconnect"); window.show(); return app.exec(); }
这是主文件。
计时器
计时器用于实现单次或重复任务。 我们使用计时器的一个很好的例子是时钟; 每秒我们都必须更新显示当前时间的标签。
#pragma once #include <QWidget> #include <QLabel> class Timer : public QWidget { public: Timer(QWidget *parent = 0); protected: void timerEvent(QTimerEvent *e); private: QLabel *label; };
这是头文件。
#include "timer.h" #include <QHBoxLayout> #include <QTime> Timer::Timer(QWidget *parent) : QWidget(parent) { QHBoxLayout *hbox = new QHBoxLayout(this); hbox->setSpacing(5); label = new QLabel("", this); hbox->addWidget(label, 0, Qt::AlignLeft | Qt::AlignTop); QTime qtime = QTime::currentTime(); QString stime = qtime.toString(); label->setText(stime); startTimer(1000); } void Timer::timerEvent(QTimerEvent *e) { Q_UNUSED(e); QTime qtime = QTime::currentTime(); QString stime = qtime.toString(); label->setText(stime); }
在我们的例子中,我们在窗口上显示当前的本地时间。
label = new QLabel("", this);
为了显示时间,我们使用一个标签小部件。
QTime qtime = QTime::currentTime(); QString stime = qtime.toString(); label->setText(stime);
在这里,我们确定当前的本地时间。 我们将其设置为标签小部件。
startTimer(1000);
我们启动计时器。 每 1000 毫秒生成一个计时器事件。
void Timer::timerEvent(QTimerEvent *e) { Q_UNUSED(e); QTime qtime = QTime::currentTime(); QString stime = qtime.toString(); label->setText(stime); }
要使用计时器事件,我们必须重新实现 timerEvent
方法。
#include <QApplication> #include "timer.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Timer window; window.resize(250, 150); window.setWindowTitle("Timer"); window.show(); return app.exec(); }
这是主文件。

本章专门讨论 Qt5 中的事件和信号。