Qt消息机制和事件

一,事件

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

  Qt 程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始 Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler)。

在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如

n keyPressEvent()

n keyReleaseEvent()

n mouseDoubleClickEvent()

n mouseMoveEvent()

n mousePressEvent()

n mouseReleaseEvent() 等。

  这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。下面来看一个例子:

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 代码来格式化文字。

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

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

语句将会使用x替换 %1,y替换 %2,因此,生成的QString为[x, y]。

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

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

为什么要点击鼠标之后才能在mouseMoveEvent()函数中显示鼠标坐标值?

  这是因为QWidget中有一个mouseTracking属性,该属性用于设置是否追踪鼠标。只有鼠标被追踪时,mouseMoveEvent()才会发出。如果mouseTracking是 false(默认即是),组件在至少一次鼠标点击之后,才能够被追踪,也就是能够发出mouseMoveEvent()事件。如果mouseTracking为 true,则mouseMoveEvent()直接可以被发出。

知道了这一点,我们就可以在main()函数中添加如下代码:

label->setMouseTracking(true);

在运行程序就没有这个问题了。

二,event()

  事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。

  如上所述,event()函数主要用于事件的分发。所以,如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。例如,我们希望在一个QWidget组件中监听 tab 键的按下,那么就可以继承QWidget,并重写它的event()函数,来达到这个目的:

bool CustomWidget::event(QEvent *e)

{

if (e->type() == QEvent::KeyPress) {

QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);

if (keyEvent->key() == Qt::Key_Tab) {

qDebug() << "You press tab.";

return true;

}

}

return QWidget::event(e);

}

CustomWidget是一个普通的QWidget子类。我们重写了它的event()函数,这个函数有一个QEvent对象作为参数,也就是需要转发的事件对象。函数返回值是 bool 类型。

  如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。

  在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播

  我们可以通过使用QEvent::type()函数可以检查事件的实际类型,其返回值是QEvent::Type类型的枚举。我们处理过自己感兴趣的事件之后,可以直接返回 true,表示我们已经对此事件进行了处理;对于其它我们不关心的事件,则需要调用父类的event()函数继续转发,否则这个组件就只能处理我们定义的事件了。为了测试这一种情况,我们可以尝试下面的代码:

bool CustomTextEdit::event(QEvent *e)

{

if (e->type() == QEvent::KeyPress)

{

QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);

if (keyEvent->key() == Qt::Key_Tab)

{

qDebug() << "You press tab.";

return true;

}

}

return false;

}

  CustomTextEdit是QTextEdit的一个子类。我们重写了其event()函数,却没有调用父类的同名函数。这样,我们的组件就只能处理 Tab 键,再也无法输入任何文本,也不能响应其它事件,比如鼠标点击之后也不会有光标出现。这是因为我们只处理的KeyPress类型的事件,并且如果不是KeyPress事件,则直接返回 false,鼠标事件根本不会被转发,也就没有了鼠标事件。

  通过查看QObject::event()的实现,我们可以理解,event()函数同前面的章节中我们所说的事件处理器有什么联系:

//!!! Qt5

bool QObject::event(QEvent *e)

{

switch (e->type()) {

case QEvent::Timer:

timerEvent((QTimerEvent*)e);

break;

case QEvent::ChildAdded:

case QEvent::ChildPolished:

case QEvent::ChildRemoved:

childEvent((QChildEvent*)e);

break;

// ...

default:

if (e->type() >= QEvent::User) {

customEvent(e);

break;

}

return false;

}

return true;

}

  这是 Qt 5 中QObject::event()函数的源代码(Qt 4 的版本也是类似的)。我们可以看到,同前面我们所说的一样,Qt 也是使用QEvent::type()判断事件类型,然后调用了特定的事件处理器。比如,如果event->type()返回值是QEvent::Timer,则调用timerEvent()函数。可以想象,QWidget::event()中一定会有如下的代码:

switch (event->type()) {

case QEvent::MouseMove:

mouseMoveEvent((QMouseEvent*)event);

break;

// ...

}

  事实也的确如此。timerEvent()和mouseMoveEvent()这样的函数,就是我们前面章节所说的事件处理器 event handler。也就是说,event()函数中实际是通过事件处理器来响应一个具体的事件。这相当于event()函数将具体事件的处理“委托”给具体的事件处理器。而这些事件处理器是 protected virtual 的,因此,我们重写了某一个事件处理器,即可让 Qt 调用我们自己实现的版本。

  由此可以见,event()是一个集中处理不同类型的事件的地方。如果你不想重写一大堆事件处理器,就可以重写这个event()函数,通过QEvent::type()判断不同的事件。鉴于重写event()函数需要十分小心注意父类的同名函数的调用,一不留神就可能出现问题,所以一般还是建议只重写事件处理器(当然,也必须记得是不是应该调用父类的同名处理器)。这其实暗示了event()函数的另外一个作用:屏蔽掉某些不需要的事件处理器。正如我们前面的CustomTextEdit例子看到的那样,我们创建了一个只能响应 tab 键的组件。这种作用是重写事件处理器所不能实现的。

时间: 2024-10-13 05:11:35

Qt消息机制和事件的相关文章

Qt消息机制和事件(二)

三,事件过滤器 有时候,对象需要查看.甚至要拦截发送到另外对象的事件.例如,对话框可能想要拦截按键事件,不让别的组件接收到:或者要修改回车键的默认处理. 通过前面的章节,我们已经知道,Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发.显然,我们可以在event()函数中实现拦截的操作.由于event()函数是 protected 的,因此,需要继承已有类.如果组件很多,就需要重写很多个event()函数.这当然相当麻烦,更不用说重写event()函数还

Qt 事件处理机制

Qt 事件处理机制 因为这篇文章写得特别好,将Qt的事件处理机制能够阐述的清晰有条理,并且便于学习.于是就装载过来了(本文做了排版,并删减了一些冗余的东西,希望原主勿怪),以供学习之用. 简介 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent.Qt是以事件驱动UI工具集.Signals/Slots在多线程中的实现也是依赖于Qt的事件处理机制.在Qt中,事件被封装成一个个对象,所有的事件都继承抽象基类QEvent. Qt事件处理机制 产生事件:输入设备,键盘鼠标等.keyPr

Atitit.事件机制 与 消息机制的联系与区别

1. 消息/事件机制是几乎所有开发语言都有的机制,在某些语言称之为消息(Event),有些地方称之为(Message).1 2. 发布/订阅模式1 3. 事件是侵入式设计,霸占你的主循环:消息是非侵入式设计,将主循环该怎样设计的自由留给用户.1 3.1. 事件越如 框架,消息相像类库2 3.2. 事件比消息封装的更高一层2 4. 消息队列和事件触发的区别,本质是没啥区别的2 5. 事件机制与消息机制2 1. 消息/事件机制是几乎所有开发语言都有的机制,在某些语言称之为消息(Event),有些地方

事件和消息机制的理解

消息/事件机制是几乎所有开发语言都有的机制,并不是deviceone的独创,在某些语言称之为消息(Event),有些地方称之为(Message). 其实原理是类似的,只不过有些实现的方式要复杂一点.我们deviceone统一就叫消息. 消息基础概念 还有一些初学者不太熟悉这个机制,我们先简单介绍一些基础概念,如果熟悉的人可以跳过这个部分.一个/条消息可以理解为是一个数据结构,包含以下几个基本部分: 消息源 :就是消息的来源,发出这个消息的对象 消息名 :就是消息的唯一标示 消息数据:消息发出后附

ble协议栈cc2540x-1.3.2之通过key处理过程看任务、事件、消息机制

一   事件机制 每一个任务都会有很多种事件,包括系统消息.任务事件等等. 二   事件的触发启动 函数1:启动一个定时器.定时delaytime时间.之后触发TaskID的event事件.延时时间以ms为单位. osal_start_timerEx( TaskID, event,delaytime); 函数2:置 tasksEvents[]数组的事件标志位为1.系统轮询这个数组确定是否触发事件 osal_set_event( simpleBLEPeripheral_TaskID, SBP_ST

0182 JavaScript执行机制:单线程,同步任务和异步任务,执行栈,消息队列,事件循环

以下代码执行的结果是什么? [结果是1 2 3 ] console.log(1); setTimeout(function () { console.log(3); }, 1000); console.log(2); 以下代码执行的结果是什么? [结果是1 2 3 ] console.log(1); setTimeout(function () { console.log(3); }, 0); console.log(2); 1.3.1 JS 是单线程 单线程就意味着,所有任务需要排队,前一个任

【转】Qt 事件处理机制 (下篇)

转自:http://mobile.51cto.com/symbian-272816.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生.分发.接受和处理事件. 继续我们上一篇文章继续介绍,Qt 事件处理机制 (上篇) 介绍了Qt框架的事件处理机制:事件的产生.分发.接受和处理,并以视窗系统鼠标点击QWidget为例,对代码进行了剖析,向大家分析了Qt框架如何通过Event Loop处理进入处理消息队列循环,如何一步一步委派给平台相关的

【转】解读Qt 事件处理机制(上篇)

[转自]:http://mobile.51cto.com/symbian-272812.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生.分发.接受和处理事件. 本篇来介绍Qt 事件处理机制 .深入了解事件处理系统对于每个学习Qt人来说非常重要,可以说,Qt是以事件驱动的UI工具集. 大家熟知Signals/Slots在多线程的实现也依赖于Qt的事件处理机制. 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent.

QT开发(十二)——QT事件处理机制

QT开发(十二)--QT事件处理机制 一.QT事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下和松开 鼠标事件: 鼠标移动,鼠标按键的按下和松开 拖放事件: 用鼠标进行拖放 滚轮事件: 鼠标滚轮滚动 绘屏事件: 重绘屏幕的某些部分 定时事件: 定时器到时 焦点事件: 键盘焦点移动 进入和离开事件: 鼠标移入widget之内,或是移出 移动事件: widget的