qt事件机制(转)
学习了一段时间的Qt之后,发现Qt的事件机制和其他语言的机制有些不同。Qt除了能够通过信号和槽机制来实现一些Action动作之外,还可以用对象所带的事件,或者用户自定义的事件来实现对象的一些行为处理。 现在,我们从头开始讲解。 到底什么是事件呢?
事件起源: 基于事件如何被产生与分发,可以把事件分为以下三类。 Spontaneous 事件——自发事件 由窗口系统产生,它们被放到系统队列中,通过事件循环逐个处理。 Posted 事件 由Qt或是应用程序产生,它们被Qt组成队列,再通过事件循环处理。 Sent 事件 由Qt或是应用程序产生,但它们被直接发送到目标对象。 Qt事件循环的过程 当我们在main()函数的末尾调用QApplication::exec()时,程序进入了Qt的事件循环,大概来讲,事件循环如下面所示:
while (!exit_was_called)
{
while(!posted_event_queue_is_empty)
{
process_next_posted_event();
}
while(!spontaneous_event_queue_is_empty)
{
process_next_spontaneous_event();
}
while(!posted_event_queue_is_empty)
{
process_next_posted_event();
}
} 首 先,事件循环处理所有的posted事件,直到队列空。
然后再处理所有的spontaneous事件,最后它处理所有的因为处理spontaneous事 件而产生的posted事件。
send 事件并不在事件循环内处理,它们都直接被发送到了目标对象。
现在看一下实践中的paint 事件是如何工作的。
当一个widget第一次可见,或是被遮挡后再次变为可见,
窗口系统产生一个(spontaneous) paint事件,要求程序重画widget,事件循环最终从事件队列中捡选这个事件并把它分发到那个需要重画的widget。
并不是所有的paint事件都是由窗口系统产生的。当你调用QWidget::update()去强行重画widget,这个widget会post 一个paint 事件给自己。这个paint事件被放入队列,最终被事件循环分发之。 假 如你很不耐烦,等不及事件循环去重画一个widget, 理论上,你应该直接调用paintEvent()强制进行立即的重画。但实际上这不总是可行的,因为paintEvent()函数是protected的 (很可能访问不了)。它也绕开了任何存在的事件过滤器。因为这些原因,Qt提供了一个机制,直接sending事件而不是posting 。
QWidget::repaint()就使用了这个机制来强制进行立即重画。 posting 相对于sending的一个优势是,它给了Qt一个压缩(compress)事件的机会。假如你在一个widget上连续地调用update() 十次,因update()而产生的这十个事件,将会自动地被合并为一个单独的事件,但是QPaintEvents事件附带的区域信息也合并了。
可压缩的事件类型包括:paint,move,resize,layout hint,language change。
最后要注意,你可以在任何时候调用QApplication::sendPostedEvent(),强制Qt产生一个对象的posted事件。 人工合成的事件 QT应用程序可以产生他们自己的事件,或是预定义类型,或是自定义类型。 这可以通过创建QEvent类或它的子类的实例,并且调用QApplication:postEvent()或QApplication::sendEvent()来实现。
这两个函数需要一个 QObject* 与一个QEvent * 作为参数,假如你调用postEvent(),你必须用 new 操作符来创建事件对象,Qt会它被处理后帮你删除它。假如你用sendEvent(), 你应该在栈上来创建事件。下面举两个例子: 一是posting 事件 QApplication::postEvent(mainWin, new QKeyEvent(QEvent::KeyPress,Key_X,'X',)); 二是sending 事件 QKeyEvent event(QEvent::KeyPress, Key_X, 'X', );
QApplication::sendEvent(mainWin, &event);
Qt应用程序很少直接调用postEvent()或是sendEvnet(),因为大多数事件会在必要时被Qt或是窗口系统自动产生
。在大多数的情况下,当你想发送一个事件时,Qt已经为了准备好了一个更高级的函数来为你服务。(例如
update()与repaint())。 定制事件类型 qt允许你创建自己的事件类型,这在多线程的程序中尤其有用。在单线程的程序也相当有用,它可以作为
对象间的一种通讯机制。为什么你应该用事件而不是其他的标准函数调用,或信号、槽的主要原因是:事件既可用于同步也可用于异步(依赖于你是调用sendEvent()或是postEvents()),函数调用或是槽调用总是同步的。事件的另外一个好处是它可以被过滤。
演示如何post一个定制事件的代码片段:
const QEvent::Type MyEvent = (QEvent::Type);
...
QApplication::postEvent(obj, new QCustomEvent(MyEvent));
事件必须是QCustomEvent类型(或子类)的。构造函数的参数是事件的类型,1024以下被Qt保留。其他可被程序使用。为处理定制事件类型,要重新实现customEvent()函数:
void MyLineEdit::customEvent(QCustomEvent *event)
{
if (event->type() == MyEvent) {
myEvent();
} else {
QLineEdit::customEvent(event);
}
}
QcustomEvent类有一个void *的成员,可用于特定的目的。你也可以子类化QCustomEvent,加上别的成员,但是你也需要在customEvent()中转换QCustomeEvent到你特有的类型。 事件处理与过滤 Qt中的事件可以在五个不同的层次上被处理 .重新实现一个特定的事件handler QObject与QWidget提供了许多特定的事件handlers,分别对应于不同的事件类型。(如paintEvent()对应paint事件) .重新实现QObject::event() event()函数是所有对象事件的入口,QObject和QWidget中缺省的实现是简单地把事件推入特定的事件handlers。 .在QObject安装上事件过滤器 事件过滤器是一个对象,它接收别的对象的事件,在这些事件到达指定目标之间。 .在aApp上安装一个事件过滤器 它会监视程序中发送到所有对象的所有事件。 .重新实现QApplication:notify() Qt的事件循环与sendEvent()调用这个函数来分发事件,通过重写它,你可以在别人之前看到事件。 特定对象的事件处理 一些事件类型可以被传递。这意味着假如目标对象不处理一个事件,Qt会试着寻找另外的事件接收者。用新的目标来调用QApplication::notify()。举例来讲,key事件是传递的,假如拥有焦点的Widget不处理特定键,Qt会分发相同的事件给父widget,然后是父亲的父亲,直到最顶层widget。
那么何时接收该事件,何时忽略呢? 通过accept( )函数和ignore( )函数。 可被传递的事件有一个accept()函数和一个ignore()函数,你可以用它们来告诉Qt,你“接收”或是 “忽略”这个事件。假如事件handler调用accept(),这个事件将不会再被传递。假如事件handler调用 ignore(),Qt会试着查找另外的事件接收者。 像大多数的开发者一样,你可能不会被调用accept()或是ignore()所烦恼。缺省情况下是“接收”,在 QWidget中的缺省实现是调用ignore(),假如你希望接收事件,你需要做的是重新实现事件handler,避免 调用QWidget的实现。假如你想“忽略”事件,只需简单地传递它到QWidget的实现。下面的代码演示了这一点:
void MyFancyWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Key_Escape) {
doEscape();
} else {
QWidget::keyPressEvent(event);
}
}
在上面的例子里,假如用户按了"ESC"键,我们会调用doEscape()并且事件被“接收”了(这是缺省的情况), 事件不会被传递到父widget,假如用户按了别的键,则调用QWidget的缺省实现。
void QWidget::keyPressEvent(QKeyEvent *event)
{
event->ignore();
}
应该感谢ignore(),事件会被传递到父widget中去。
讨论到目前为至,我们都假设基类是QWidget,然而,同样的规则也可以应用到别的层次中,只要用QWidget 代替基类即可。举例来说:
void MyFancyLineEdit::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Key_SysReq) {
doSystemRequest();
} else {
QLineEdit::keyPressEvent(event);
}
}
由于某些原因,你会在event()中处理事件,而不是在特定的handler中,如keyPressEvent(),这个过程会有些不同。event() 会返回一个布尔值,来告诉调用者是否事件被accept或ignore,(true表示accept),从event()中调用accept()或是 ignore()是没有意义的。“Accept”标记是event()与特定事件handler之间的一种通讯机制。而从event()返回的布尔值却是 用来与QApplication:notify()通讯的。在QWidgetk中缺省的event()实现是转换“Accept”标记为一个布尔值,如下 所示:
bool QWidget::event(QEvent *event)
{
switch (e->type()) {
case QEvent::KeyPress:
keyPressEvent((QKeyEvent *)event);
if (!((QKeyEvent *)event)->isAccepted())
return false;
break;
case QEvent::KeyRelease:
keyReleaseEvent((QKeyEvent *)event);
if (!((QKeyEvent *)event)->isAccepted())
return false;
break;
...
}
return true;
}
到现在为至,我们所说的内容不仅仅适用于key事件,也适用于mouse,wheel,tablet,context menu等事件。
Close事件有点不同,调用QCloseEvent:ignore()取消了关闭操作,而accept()告诉Qt继续执行正常的关闭操作。为了避免混乱,最好是在closeEvent()的新实现中明确地进行accept()与ignore()的调用:
void MainWindow::closeEvent(QCloseEvent *event)
{
if (userReallyWantsToQuit()) {
event->accept();
} else {
event->ignore();
}
} 转自:http://www.cnblogs.com/li-hao/archive/2011/11/13/2247662.html
qt事件机制(转)的更多相关文章
- Qt事件机制浅析(定义,产生,异步事件循环,转发,与信号的区别。感觉QT事件与Delphi的事件一致,而信号则与Windows消息一致)
Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...
- 详解 QT 源码之 Qt 事件机制原理
QT 源码之 Qt 事件机制原理是本文要介绍的内容,在用Qt写Gui程序的时候,在main函数里面最后依据都是app.exec();很多书上对这句的解释是,使 Qt 程序进入消息循环.下面我们就到ex ...
- Qt事件机制---信号通过事件实现,事件可以过滤,事件更底层,事件是基础,信号是扩展。
转:http://www.cnblogs.com/findumars/p/8001484.html Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和.先处理自己队列中的消息,然后再处 ...
- Qt事件机制(是动作发生后,一种通知对象的消息,是被动与主动的总和。先处理自己队列中的消息,然后再处理系统消息队列中的消息)
Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...
- Qt事件机制浅析
Qt事件机制 Qt程序是事件驱动的, 程序的每个动作都是由幕后某个事件所触发.. Qt事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. Qt事件的类型很多, 常见的qt的事件如下: 键盘事 ...
- Qt 事件机制
[1]事件 事件是可以被控件识别的操作.如按下确定按钮.选择某个单选按钮或复选框. 每种控件有自己可识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件等等. 事件就是用户对窗口 ...
- [Qt] 事件机制(一)
事件主要分为两种: 在与用户交互时发生.比如按下鼠标(mousePressEvent),敲击键盘(keyPressEvent)等 系统自动发生,比如计时器事件(timerEvent)等 每种事件对应一 ...
- qt事件机制---事件范例
在笔记qt课程04笔记中
- [Qt] 事件机制(四)
滚轮事件:滚动滚轮实现窗口大小缩放 widget.h中增加: protected: void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; void ...
随机推荐
- Python监听键盘和鼠标事件
我们可以利用windows提供的api函数来实现对系统键盘事件和鼠标事件的监听,主要利用的是SetWindowsHookEx函数,这个函数可以允许调用者传入一个钩子函数也叫回调函数,当指定的事件发生时 ...
- .Net Core 3 骚操作 之 用 Windows 桌面应用开发 Asp.Net Core 网站
前言 曾经在开发 Asp.Net 网站时就在想,为什么一定要把网站挂到 IIS 上?网站项目的 Main 函数哪儿去了?后来才知道这个 Main 函数在 w3wp.exe 里,这也是 IIS 的主进程 ...
- Linux内核参数详解
所谓Linux服务器内核参数优化(适合Apache.Nginx.Squid等多种web应用,特殊的业务有可能需要做略微调整),主要是指在Linux系统中针对业务服务应用而进行的系统内核参数调整,优化并 ...
- 删除lvm时出现"Logical volume contains a filesystem in use"
问题描述: k8s环境中需要重新创建lvm:/dev/mapper/test-vg-test-storage,该lvm挂载在/data/prometheus下面,在删除出现"Logical ...
- 【转帖】为什么有了Compose和Swarm,还会有Kubernetes的出现?
为什么有了Compose和Swarm,还会有Kubernetes的出现? https://www.cnblogs.com/chenqionghe/p/11474486.html 图非常好 一.k8s设 ...
- Linux 线程间的同步与互斥
在线程并发执行的时候,我们需要保证临界资源的安全访问,防止线程争抢资源,造成数据二义性. 线程同步: 条件变量 为什么使用条件变量? 对临界资源的时序可控性,条件满足会通知其他等待操作临界资源的线程, ...
- 配置多用户SMB挂载
在 system1 通过 SMB 共享目录 /devops ,并满足下列要求: 1.共享名为 devops 2.共享目录 devops 只能 group8.example.com 域中的客户端使用 3 ...
- Hive 系列(二)—— Linux 环境下 Hive 的安装部署
一.安装Hive 1.1 下载并解压 下载所需版本的 Hive,这里我下载版本为 cdh5.15.2.下载地址:http://archive.cloudera.com/cdh5/cdh/5/ # 下载 ...
- Java web服务端参数校验Javax.validation (springboot)
一.基本使用 Javax.validation是spring集成自带的一个参数校验接口.可通过添加注解来设置校验条件. 下面以springboot项目为例进行说明.创建web项目后,不需要再添加其他的 ...
- Vue项目开发相关问题总结
Vue项目开发相关问题总结 一.创建一个项目(两种方式) 1.通过CLI命令行创建,具体步骤如下: (1)Node 版本要求 Vue CLI 需要 Node.js 8.9 或更高版本 (推荐 8.11 ...