【Qt笔记】事件

事件(event)是由系统或者 Qt 自己在不一样的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口须要从新绘制的时候,都会发出一个相应的事件。一些事件在对用户操做作出响应时发出,如键盘事件等;另外一些事件则是由系统自动发出,如计时器事件。框架

 

事件也就是咱们一般说的“事件驱动(event drive)”程序设计的基础概念。事件的出现,使得程序代码不会按照原始的线性顺序执行。想一想看,从最初的 C 语言开始,咱们的程序就是以一种线性的顺序执行代码:这一条语句执行以后,开始执行下一条语句;这一个函数执行事后,开始执行下一个函数。这种相似“批处理”的程序设计风格显然不适合于处理复杂的用户交互。咱们来想象一下用户交互的情景:咱们设计了一堆功能放在界面上,用户点击了“打开文件”,因而开始执行打开文件的操做;用户点击了“保存文件”,因而开始执行保存文件的操做。咱们不知道用户究竟想进行什么操做,所以也就不能预测接下来将会调用哪个函数。若是咱们设计了一个“文件另存为”的操做,若是用户不点击,这个操做将永远不会被调用。这就是所谓的“事件驱动”,咱们的程序的执行顺序再也不是线性的,而是由一个个事件驱动着程序继续执行。没有事件,程序将阻塞在那里,不执行任何代码。函数

在 Qt 中,事件的概念彷佛同信号槽相似。的确如此,通常来讲,使用 Qt 组件时,咱们并不会把主要精力放在事件上。由于在 Qt 中,咱们关心的更多的是事件关联的一个信号。好比,对于QPushButton的鼠标点击,咱们不须要关心这个鼠标点击事件,而是关心它的clicked()信号的发出。这与其余的一些 GUI 框架不一样:在 Swing 中,你所要关心的是JButtonActionListener这个点击事件。由此看出,相比于其余 GUI 框架,Qt 给了咱们额外的选择:信号槽。this

可是,Qt 中的事件和信号槽却并非能够相互替代的。信号由具体的对象发出,而后会立刻交给由connect()函数链接的槽进行处理;而对于事件,Qt 使用一个事件队列对全部发出的事件进行维护,当新的事件产生时,会被追加到事件队列的尾部。前一个事件完成后,取出后面的事件进行处理。可是,必要的时候,Qt 的事件也能够不进入事件队列,而是直接处理。信号一旦发出,对应的槽函数必定会被执行。可是,事件则可使用“事件过滤器”进行过滤,对于有些事件进行额外的处理,另外的事件则不关心。总的来讲,若是咱们使用组件,咱们关心的是信号槽;若是咱们自定义组件,咱们关心的是事件。由于咱们能够经过事件来改变组件的默认操做。好比,若是咱们要自定义一个可以响应鼠标事件的EventLabel,咱们就须要重写QLabel的鼠标事件,作出咱们但愿的操做,有可能还得在恰当的时候发出一个相似按钮的clicked()信号(若是咱们但愿让这个EventLabel可以被其它组件使用)或者其它的信号。设计

在前面咱们也曾经简单提到,Qt 程序须要在main()函数建立一个QCoreApplication对象,而后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数以后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将建立一个事件对象。Qt 中全部事件类都继承于QEvent。在事件对象建立完毕后,Qt 将这个事件对象传递给QObjectevent()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler)。关于这一点,咱们会在之后的章节中详细说明。code

在全部组件的父类QWidget中,定义了不少事件处理的回调函数,如keyPressEvent()keyReleaseEvent()mouseDoubleClickEvent()mouseMoveEvent()mousePressEvent()mouseReleaseEvent()等。这些函数都是 protected virtual 的,也就是说,咱们能够在子类中从新实现这些函数。下面来看一个例子:对象

#include <QApplication>
#include <QMouseEvent>
#include <QLabel>

class EventLabel : public QLabel
{
protected:
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
};

void EventLabel::mouseMoveEvent(QMouseEvent *event)
{
    this->setText(QString("<center><h1>Move: (%1, %2)</h1></center>")
                  .arg(QString::number(event->x()), QString::number(event->y())));
}

void EventLabel::mousePressEvent(QMouseEvent *event)
{
    this->setText(QString("<center><h1>Press: (%1, %2)</h1></center>")
                  .arg(QString::number(event->x()), QString::number(event->y())));
}

void EventLabel::mouseReleaseEvent(QMouseEvent *event)
{
    QString msg;
    msg.sprintf("<center><h1>Release: (%d, %d)</h1></center>",
                event->x(), event->y());
    this->setText(msg);
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    EventLabel *label = new EventLabel;
    label->setWindowTitle("MouseEvent Demo");
    label->resize(300, 200);
    label->show();

    return a.exec();
}

咱们编译运行上面的代码,就能够理解到有关事件的使用方法。继承

EventLabel继承了QLabel,覆盖了mousePressEvent()mouseMoveEvent()MouseReleaseEvent()三个函数。咱们并无添加什么功能,只是在鼠标按下(press)、鼠标移动(move)和鼠标释放(release)的时候,把当前鼠标的坐标值显示在这个Label上面。因为QLabel是支持 HTML 代码的,所以咱们直接使用了 HTML 代码来格式化文字。队列

QStringarg()函数能够自动替换掉QString中出现的占位符。其占位符以 % 开始,后面是占位符的位置,例如 %1,%2 这种。事件

QString("[%1, %2]").arg(x, y);

语句将会使用 x 替换 %1,y 替换 %2,所以,这个语句生成的QString为 [x, y]。get

mouseReleaseEvent()函数中,咱们使用了另一种QString的构造方法。咱们使用相似 C 风格的格式化函数sprintf()来构造QString

运行上面的代码,当咱们点击了一下鼠标以后,label 上将显示鼠标当前坐标值。

为何要点击鼠标以后才能在mouseMoveEvent()函数中显示鼠标坐标值?这是由于QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()才会发出。若是mouseTracking是 false(默认便是),组件在至少一次鼠标点击以后,才可以被追踪,也就是可以发出mouseMoveEvent()事件。若是mouseTracking为 true,则mouseMoveEvent()直接能够被发出。知道了这一点,咱们就能够在main()函数中直接设置下:

EventLabel *label = new EventLabel;
label->setWindowTitle("MouseEvent Demo");
label->resize(300, 200);
label->setMouseTracking(true);
label->show();

这样子就没有这个问题了。

相关文章
相关标签/搜索