原文: http://zetcode.com/gui/qt5/eventsandsignals/

在 Qt5 C++ 编程教程的这一部分中,我们讨论事件和信号。

事件是任何 GUI 程序中的重要组成部分。 所有 GUI 应用都是事件驱动的。 应用会对在其生命周期内生成的不同事件类型做出反应。 事件主要由应用的用户生成。 但是它们也可以通过其他方式生成,例如互联网连接,窗口管理器或计时器。 在事件模型中,有三个参与者:

  • 事件来源
  • 事件对象
  • 事件目标

事件源是状态更改的对象。 它生成事件。事件对象(事件)将状态更改封装在事件源中。事件目标是要通知的对象。 事件源对象将处理事件的任务委托给事件目标。

当我们调用应用的exec()方法时,应用进入主循环。 主循环获取事件并将其发送到对象。 Qt 具有独特的信号和槽机制。 该信号和槽机制是 C++ 编程语言的扩展。

信号和槽用于对象之间的通信。 当发生特定事件时,会发出信号。槽是正常的 C++ 方法; 发出与其连接的信号时调用它。

点击

第一个示例显示了一个非常简单的事件处理示例。 我们有一个按钮。 通过单击按钮,我们终止该应用。

click.h

  1. #pragma once
  2. #include <QWidget>
  3. class Click : public QWidget {
  4. public:
  5. Click(QWidget *parent = 0);
  6. };

这是头文件。

click.cpp

  1. #include <QPushButton>
  2. #include <QApplication>
  3. #include <QHBoxLayout>
  4. #include "click.h"
  5. Click::Click(QWidget *parent)
  6. : QWidget(parent) {
  7. QHBoxLayout *hbox = new QHBoxLayout(this);
  8. hbox->setSpacing(5);
  9. QPushButton *quitBtn = new QPushButton("Quit", this);
  10. hbox->addWidget(quitBtn, 0, Qt::AlignLeft | Qt::AlignTop);
  11. connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit);
  12. }

我们在窗口上显示一个QPushButton

  1. connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit);

connect()方法将信号连接到槽。 当我们单击退出按钮时,会生成clicked信号。 qApp是指向应用对象的全局指针。 它在<QApplication>头文件中定义。 发出点击信号时,将调用quit()方法。

main.cpp

  1. #include <QApplication>
  2. #include "click.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. Click window;
  6. window.resize(250, 150);
  7. window.setWindowTitle("Click");
  8. window.show();
  9. return app.exec();
  10. }

这是主文件。

Qt5 中的事件和信号 - 图1

图:点击

按键

在以下示例中,我们对按键进行反应。

keypress.h

  1. #pragma once
  2. #include <QWidget>
  3. class KeyPress : public QWidget {
  4. public:
  5. KeyPress(QWidget *parent = 0);
  6. protected:
  7. void keyPressEvent(QKeyEvent * e);
  8. };

这是keypress.h头文件。

keypress.cpp

  1. #include <QApplication>
  2. #include <QKeyEvent>
  3. #include "keypress.h"
  4. KeyPress::KeyPress(QWidget *parent)
  5. : QWidget(parent)
  6. { }
  7. void KeyPress::keyPressEvent(QKeyEvent *event) {
  8. if (event->key() == Qt::Key_Escape) {
  9. qApp->quit();
  10. }
  11. }

如果我们按 Escape 键,则应用终止。

  1. void KeyPress::keyPressEvent(QKeyEvent *e) {
  2. if (e->key() == Qt::Key_Escape) {
  3. qApp->quit();
  4. }
  5. }

在 Qt5 中使用事件的一种方法是重新实现事件处理器。 QKeyEvent是一个事件对象,其中包含有关发生的情况的信息。 在我们的例子中,我们使用事件对象来确定实际按下了哪个键。

main.cpp

  1. #include <QApplication>
  2. #include "keypress.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. KeyPress window;
  6. window.resize(250, 150);
  7. window.setWindowTitle("Key press");
  8. window.show();
  9. return app.exec();
  10. }

这是主文件。

QMoveEvent

QMoveEvent类包含移动事件的事件参数。 移动事件将发送到已移动的窗口小部件。

move.h

  1. #pragma once
  2. #include <QMainWindow>
  3. class Move : public QWidget {
  4. Q_OBJECT
  5. public:
  6. Move(QWidget *parent = 0);
  7. protected:
  8. void moveEvent(QMoveEvent *e);
  9. };

这是move.h头文件。

move.cpp

  1. #include <QMoveEvent>
  2. #include "move.h"
  3. Move::Move(QWidget *parent)
  4. : QWidget(parent)
  5. { }
  6. void Move::moveEvent(QMoveEvent *e) {
  7. int x = e->pos().x();
  8. int y = e->pos().y();
  9. QString text = QString::number(x) + "," + QString::number(y);
  10. setWindowTitle(text);
  11. }

在我们的代码编程示例中,我们对移动事件做出反应。 我们确定窗口客户区左上角的当前 x,y 坐标,并将这些值设置为窗口标题。

  1. int x = e->pos().x();
  2. int y = e->pos().y();

我们使用QMoveEvent对象来确定xy值。

  1. QString text = QString::number(x) + "," + QString::number(y);

我们将整数值转换为字符串。

  1. setWindowTitle(text);

setWindowTitle()方法将文本设置为窗口的标题。

main.cpp

  1. #include <QApplication>
  2. #include "move.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. Move window;
  6. window.resize(250, 150);
  7. window.setWindowTitle("Move");
  8. window.show();
  9. return app.exec();
  10. }

这是主文件。

Qt5 中的事件和信号 - 图2

图:QMoveEvent

断开信号

可以从槽断开信号。 下一个示例显示了我们如何完成此任务。

disconnect.h

  1. #pragma once
  2. #include <QWidget>
  3. #include <QPushButton>
  4. class Disconnect : public QWidget {
  5. Q_OBJECT
  6. public:
  7. Disconnect(QWidget *parent = 0);
  8. private slots:
  9. void onClick();
  10. void onCheck(int);
  11. private:
  12. QPushButton *clickBtn;
  13. };

在头文件中,我们声明了两个槽。 slots不是 C++ 关键字,它是 Qt5 扩展名。 在代码编译之前,这些扩展由预处理器处理。 当我们在类中使用信号和时隙时,必须在类定义的开头提供Q_OBJECT宏。 否则,预处理器会抱怨。

disconnect.cpp

  1. #include <QTextStream>
  2. #include <QCheckBox>
  3. #include <QHBoxLayout>
  4. #include "disconnect.h"
  5. Disconnect::Disconnect(QWidget *parent)
  6. : QWidget(parent) {
  7. QHBoxLayout *hbox = new QHBoxLayout(this);
  8. hbox->setSpacing(5);
  9. clickBtn = new QPushButton("Click", this);
  10. hbox->addWidget(clickBtn, 0, Qt::AlignLeft | Qt::AlignTop);
  11. QCheckBox *cb = new QCheckBox("Connect", this);
  12. cb->setCheckState(Qt::Checked);
  13. hbox->addWidget(cb, 0, Qt::AlignLeft | Qt::AlignTop);
  14. connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick);
  15. connect(cb, &QCheckBox::stateChanged, this, &Disconnect::onCheck);
  16. }
  17. void Disconnect::onClick() {
  18. QTextStream out(stdout);
  19. out << "Button clicked" << endl;
  20. }
  21. void Disconnect::onCheck(int state) {
  22. if (state == Qt::Checked) {
  23. connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick);
  24. } else {
  25. disconnect(clickBtn, &QPushButton::clicked, this,
  26. &Disconnect::onClick);
  27. }
  28. }

在我们的示例中,我们有一个按钮和一个复选框。 复选框用于将槽与单击的信号按钮断开连接。 此示例必须从命令行执行。

  1. connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick);
  2. connect(cb, &QCheckBox::stateChanged, this, &Disconnect::onCheck);

在这里,我们将信号连接到用户定义的槽。

  1. void Disconnect::onClick() {
  2. QTextStream out(stdout);
  3. out << "Button clicked" << endl;
  4. }

如果单击“单击”按钮,则将"Button clicked"文本发送到终端窗口。

  1. void Disconnect::onCheck(int state) {
  2. if (state == Qt::Checked) {
  3. connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick);
  4. } else {
  5. disconnect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick);
  6. }
  7. }

onCheck()槽内,我们根据单击状态将onClick()槽与单击按钮连接或断开。

main.cpp

  1. #include <QApplication>
  2. #include "disconnect.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. Disconnect window;
  6. window.resize(250, 150);
  7. window.setWindowTitle("Disconnect");
  8. window.show();
  9. return app.exec();
  10. }

这是主文件。

计时器

计时器用于执行单发或重复性任务。 一个使用计时器的好例子是时钟。 每秒,我们必须更新显示当前时间的标签。

timer.h

  1. #pragma once
  2. #include <QWidget>
  3. #include <QLabel>
  4. class Timer : public QWidget {
  5. public:
  6. Timer(QWidget *parent = 0);
  7. protected:
  8. void timerEvent(QTimerEvent *e);
  9. private:
  10. QLabel *label;
  11. };

这是头文件。

timer.cpp

  1. #include "timer.h"
  2. #include <QHBoxLayout>
  3. #include <QTime>
  4. Timer::Timer(QWidget *parent)
  5. : QWidget(parent) {
  6. QHBoxLayout *hbox = new QHBoxLayout(this);
  7. hbox->setSpacing(5);
  8. label = new QLabel("", this);
  9. hbox->addWidget(label, 0, Qt::AlignLeft | Qt::AlignTop);
  10. QTime qtime = QTime::currentTime();
  11. QString stime = qtime.toString();
  12. label->setText(stime);
  13. startTimer(1000);
  14. }
  15. void Timer::timerEvent(QTimerEvent *e) {
  16. Q_UNUSED(e);
  17. QTime qtime = QTime::currentTime();
  18. QString stime = qtime.toString();
  19. label->setText(stime);
  20. }

在我们的示例中,我们在窗口上显示当前本地时间。

  1. label = new QLabel("", this);

为了显示时间,我们使用标签小部件。

  1. QTime qtime = QTime::currentTime();
  2. QString stime = qtime.toString();
  3. label->setText(stime);

在这里,我们确定当前的本地时间。 我们将其设置为标签小部件。

  1. startTimer(1000);

我们启动计时器。 每 1000ms 会生成一个计时器事件。

  1. void Timer::timerEvent(QTimerEvent *e) {
  2. Q_UNUSED(e);
  3. QTime qtime = QTime::currentTime();
  4. QString stime = qtime.toString();
  5. label->setText(stime);
  6. }

要处理计时器事件,我们必须重新实现timerEvent()方法。

main.cpp

  1. #include <QApplication>
  2. #include "timer.h"
  3. int main(int argc, char *argv[]) {
  4. QApplication app(argc, argv);
  5. Timer window;
  6. window.resize(250, 150);
  7. window.setWindowTitle("Timer");
  8. window.show();
  9. return app.exec();
  10. }

这是主文件。

Qt5 中的事件和信号 - 图3

图:计时器

本章专门介绍 Qt5 中的事件和信号。