Qt5 C++14教程--事件和信号

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

事件是任何GUI程序中的一个重要部分。所有的GUI应用程序都是事件驱动的。一个应用程序对其生命周期中产生的不同事件类型作出反应。事件主要由应用程序的用户产生。但它们也可以由其他方式产生,例如,互联网连接、窗口管理器或计时器。

在事件模型中,有三个参与者:

事件源

事件对象

事件目标

事件源是状态改变的对象。它产生事件。事件对象(Event)封装了事件源的状态变化。事件目标是希望被通知的对象。事件源对象将处理事件的任务委托给事件目标。

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

信号和槽被用于对象之间的通信。当一个特定的事件发生时,会发出一个信号。槽是一个普通的C++方法;当与之相连的信号被发出时,它被调用。

Qt5点击示例

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

click.h
#pragma once

#include <QWidget>

class Click : public QWidget {

  public:
    Click(QWidget *parent = nullptr);
};
This is the header file.

click.cpp
#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方法将一个信号连接到一个槽。当我们单击Quit按钮时,将生成clicked信号。qApp是指向应用程序对象的全局指针。它在<QtGui/QApplication>头文件中定义。当clicked信号被发射时,将调用quit方法。

main.cpp
#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();
}

按键事件

在以下示例中,我们对按键事件作出反应。

keypress.h
#pragma once

#include <QWidget>

class KeyPress : public QWidget {

  public:
    KeyPress(QWidget *parent = 0);

  protected:
    void keyPressEvent(QKeyEvent * e);
};
This is the keypress.h header file.



keypress.cpp
#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();
   }
}

如果按下Esc键,应用程序将终止。

void KeyPress::keyPressEvent(QKeyEvent *e) {

   if (e->key() == Qt::Key_Escape) {
       qApp->quit();
   }
}

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

main.cpp
#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类包含移动事件的事件参数。移动事件被发送到已移动的小部件。

move.h
#pragma once

#include <QMainWindow>

class Move : public QWidget {

  Q_OBJECT

  public:
    Move(QWidget *parent = 0);

  protected:
    void moveEvent(QMoveEvent *e);
};
This is the move.h header file.

move.cpp

#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方法将文本设置为窗口的标题。

main.cpp
#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();
}

断开一个信号

可以将信号从槽中断开连接。下面的示例展示了如何实现这一点。

disconnect.h
#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 宏。否则,预处理器会报错。

disconnect.cpp
#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);
  }
}

在我们的示例中,我们有一个按钮和一个复选框。复选框将一个槽连接和断开连接到按钮的 clicked 信号。该示例必须从命令行中执行。

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 槽内部,我们根据接收到的状态连接或断开连接到 Click 按钮的 onClick 槽。

main.cpp
#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();
}

定时器

定时器用于实现单次或重复性任务。一个常见的例子是时钟,每秒钟我们需要更新显示当前时间的标签。

timer.h
#pragma once

#include <QWidget>
#include <QLabel>

class Timer : public QWidget {

  public:
    Timer(QWidget *parent = 0);

  protected:
    void timerEvent(QTimerEvent *e);

  private:
    QLabel *label;
};

timer.cpp

#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方法。

main.cpp
#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();
}

这是主文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值