✎Qt-doc—QEvent事件系统

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了✎Qt-doc—QEvent事件系统相关的知识,希望对你有一定的参考价值。

目录

事件系统(The Event System)

在Qt中,事件是从抽象QEvent类派生出来的对象,表示应用程序内部或应用程序需要了解的外部活动的结果所发生的事情。事件可以由QObject子类的任何实例接收和处理,但它们与小部件特别相关。本文档描述了在典型应用程序中如何传递和处理事件。

事件是如何传递的(How Events are Delivered)

当事件发生时,Qt通过构造适当的QEvent子类的实例来创建一个事件对象来表示它,并通过调用其event()函数将其传递给QObject的特定实例(或其子类之一)。此函数不处理事件本身;根据所传递的事件类型,它为该特定类型的事件调用事件处理程序,并根据事件是被接受还是被忽略来发送响应

一些事件,如QMouseEvent和QKeyEvent,来自窗口系统;有些,如QTimerEvent,来自其他来源;有些来自应用程序本身。

事件类型(Event Types)

大多数事件类型都有特殊的类,特别是QResizeEvent、QPaintEvent、QMouseEvent,QKeyEvent和QCloseEvent。每个类都子类化QEvent并添加特定于事件的函数。例如,QResizeEvent添加了size()和oldSize(),使小部件能够发现其尺寸是如何更改的。

有些类支持多个实际事件类型。QMouseEvent支持鼠标按钮按下、双击、移动和其他相关操作。

每个事件都有一个在QEvent::Type中定义的关联类型,这可以作为运行时类型信息的方便来源,以快速确定给定事件对象是从哪个子类构造的。

由于项目需要以多种复杂的方式做出反应,Qt的事件交付机制非常灵活。QCoreApplication::notify()的文档简明扼要地讲述了整个故事;《Qt季刊》的文章《Another Look at Events》则不那么简洁地重复了它。在这里,我们将对95%的应用程序进行足够的解释。

事件处理器(Event Handlers)

传递事件的正常方式是调用虚函数。例如,通过调用QWidget::paintEvent()来传递QPaintEvent。这个虚功能负责做出适当的反应,通常通过重新绘制小部件。如果在虚函数的实现中没有执行所有必要的工作,则可能需要调用基类的实现。

例如,以下代码处理自定义复选框小部件上的鼠标左键单击,同时将所有其他按钮单击传递给基类QCheckBox:

void MyCheckBox::mousePressEvent(QMouseEvent *event) 
    if (event->button() == Qt::LeftButton) 
        // handle left mouse button here
     else 
        // pass on other buttons to base class
        QCheckBox::mousePressEvent(event);
    

如果要替换基类的函数,则必须自己实现所有内容。然而,如果你只想扩展基类的功能,那么你可以实现你想要的,并调用基类来获得你不想处理的任何情况的默认行为。

有时,没有这样一个特定于事件的功能,或者特定于事件功能不够。最常见的例子是按Tab键。通常,QWidget会截取这些内容以移动键盘焦点,但一些小部件本身需要Tab键。

这些对象可以重新实现常规事件处理程序QObject::event(),并在常规处理之前或之后进行事件处理,或者可以完全替换函数。一个既解释Tab又具有特定于应用程序的自定义事件的非常不寻常的小部件可能包含以下event()函数:

bool MyWidget::event(QEvent *event) 
    if (event->type() == QEvent::KeyPress) 
        QKeyEvent *ke = static_cast<QKeyEvent *>(event);
        if (ke->key() == Qt::Key_Tab) 
            // special tab handling here
            return true;
        
     else if (event->type() == MyCustomEventType) 
        MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
        // custom event handling here
        return true;
    
    return QWidget::event(event);

请注意,QWidget::event()仍然针对所有未处理的情况进行调用,返回值指示事件是否已处理;如果值为true,则会阻止将事件发送到其他对象

事件过滤器(Event Filters)

有时,对象需要查看并可能拦截传递给另一个对象的事件。例如,对话框通常希望过滤某些小部件的按键;例如,修改Return键处理。

QObject::installEventFilter()函数通过设置事件过滤器来启用此功能,从而使指定的过滤器对象在其QObject::eventFilter()函数中接收目标对象的事件。事件过滤器在目标对象之前处理事件,允许它根据需要检查和丢弃事件。可以使用QObject::removeEventFilter()函数删除现有的事件过滤器。

当调用过滤器对象的eventFilter()实现时,它可以接受或拒绝事件,并允许或拒绝对事件的进一步处理。如果所有事件过滤器都允许进一步处理某个事件(每次返回false),则会将事件发送到目标对象本身。如果其中一个停止处理(通过返回true),则目标和任何后续事件筛选器根本看不到该事件。

bool FilterObject::eventFilter(QObject *object, QEvent *event) 
    if (object == target && event->type() == QEvent::KeyPress) 
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        if (keyEvent->key() == Qt::Key_Tab) 
            // Special tab handling
            return true;
         else
            return false;
    
    return false;

上面的代码显示了拦截发送到特定目标小部件的Tab键按下事件的另一种方法。在这种情况下,过滤器处理相关事件并返回true,以阻止进一步处理这些事件。所有其他事件都被忽略,过滤器返回false,以允许通过安装在其上的任何其他事件过滤器将它们发送到目标小部件。

还可以通过在QApplication或QCoreApplication对象上安装事件过滤器来过滤整个应用程序的所有事件。这样的全局事件过滤器被调用的时机总会先于特定对象的过滤器。这是非常强大的,但它也会减慢整个应用程序中每个单个事件的事件传递;通常应使用讨论的其他技术。

发送事件(Sending Events)

许多应用程序都想创建和发送自己的事件。通过构造合适的事件对象并使用QCoreApplication::sendEvent()QCoreApplication::postEvent()

sendEvent()立即处理事件。当它返回时,事件过滤器和/或对象本身已经处理了事件。对于许多事件类,有一个名为isAccepted()的函数,它告诉你事件是被上次调用的处理程序接受还是拒绝。

postEvent()将事件发布到队列中,以便稍后分派。下次Qt的主事件循环运行时,它会通过一些优化来调度所有发布的事件。例如,如果有多个调整大小事件,则将它们压缩为一个。这同样适用于绘制事件:QWidget::update()调用postEvent(),它通过避免多次重新绘制来消除闪烁并提高速度。

postEvent()也在对象初始化期间使用,因为发布的事件通常会在对象初始化完成后很快发送。在实现小部件时,重要的是要认识到事件可以在其生命周期的早期传送,因此,在其构造函数中,请确保在它可能接收到事件之前尽早初始化成员变量。

要创建自定义类型的事件,你需要定义一个事件号,它必须大于QEvent::User,并且你可能需要子类化QEvent,以便传递有关自定义事件的特定信息。

QEvent类描述(QEvent Detailed Description)

QEvent

Qt的主事件循环(QCoreApplication::exec())从事件队列中获取本机窗口系统事件,将其转换为QEvents,并将转换后的事件发送给QObjects。

通常,事件来自底层窗口系统(spontaneous() 返回true),但也可以使用QCoreApplication::sendEvent()QCoreApplication::postEvent()spontaneous() 返回false)。

QObjects通过调用其QObject::event()函数来接收事件。该函数可以在子类中重新实现,以自定义事件处理并添加其他事件类型;QWidget::event()是一个著名的例子。默认情况下,事件被分派给事件处理程序,如QObject::timerEvent()和QWidget::mouseMoveEvent()。QObject::installEventFilter()允许一个对象拦截发往另一个对象的事件。

基本QEvent只包含一个事件类型参数和一个“accept”标志。用accept()设置“接受标志”,用ignore()清除标志。它是默认设置的,但不要依赖于此,因为子类可能会选择在其构造函数中清除它。

QEvent的子类包含描述特定事件的附加参数。

接受还是忽略(Accept or Ignore?)

Another Look at Events

可以传播的事件有一个accept()和一个ignore()函数,你可以调用它们来告诉Qt你“接受”或“忽略”该事件。如果事件处理程序对事件调用accept(),则不会进一步传播该事件;如果事件处理程序调用ignore(),Qt将尝试查找另一个接收器。

如果你和大多数Qt开发人员一样,那么你可能从未真正费心在程序中调用accept()和ignore()。这是正确的。Qt是这样设计的,你通常不需要调用它们。默认值为“accept”,QWidget中的默认事件处理程序实现调用ignore()。如果要接受事件,只需重新实现事件处理程序并避免调用QWidget实现即可。如果你想忽略该事件,只需将其传递给QWidget实现即可。以下代码片段说明了这一点:

void MyFancyWidget::keyPressEvent(QKeyEvent *event) 
	if (event->key() == Key_Escape)
		doEscape();
	else
		QWidget::keyPressEvent(event);

在本例中,如果用户按Esc键,我们将调用doEscape()并接受事件(默认)。事件不会传播到父窗口小部件。如果用户按下任何其他键,我们将调用QWidget的默认实现:

void QWidget::keyPressEvent(QKeyEvent *event) 
	event->ignore();

由于ignore()调用,事件将传播到父小部件。


到目前为止,我们假设基类是QWidget。然而,通过用其他基类替换QWidget,同样的习惯用法可以在任何级别上使用。例如:

void MyFancyLineEdit::keyPressEvent(QKeyEvent *event) 
	if (event->key() == Key_SysReq)
		doSystemRequest();
	else
		QLineEdit::keyPressEvent(event);

如果出于某种原因,你在event()中而不是在特定的处理程序(如keyPressEvent())中处理事件,则过程会有所不同。event()函数返回一个布尔值,告诉调用方事件是否被接受(true表示“接受”)。对event()中的事件调用accept()或ignore()是毫无意义的。“accept”标志是特定事件处理程序和event()之间的通信机制,而event()的bool返回值用于与QApplication::notify()通信。QWidget中的默认event()实现将“accept”标志转换为bool,如下所示:

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;

到目前为止,所说的不仅适用于键盘事件,还适用于鼠标、滚轮、平板电脑和上下文菜单事件。

结束事件的工作方式不同。调用QCloseEvent::ignore()将取消关闭操作,而accept()将告诉Qt继续正常关闭操作。为了避免混淆,最好在closeEvent()的重新实现中显式调用accept()或ignore(),如下所示:

void MainWindow::closeEvent(QCloseEvent *event) 
	if (userReallyWantsToQuit())
		event->accept();
	else
		event->ignore();

以上是关于✎Qt-doc—QEvent事件系统的主要内容,如果未能解决你的问题,请参考以下文章

✎Qt-doc—QEvent事件系统

✎Qt-doc—QEvent事件系统

Qt之QEvent(所有事件的翻译)

Qt 之 QEvent

Qt 之 QEvent

OSX - Qt 应用程序在接收系统事件 QEvent::FileOpen 时崩溃