地址:http://qtdocs.sourceforge.net/index.php/%E4%BA%8B%E4%BB%B6%E5%92%8C%E4%BA%8B%E4%BB%B6%E8%BF%87%E6%BB%A4%E5%99%A8
在Qt中,事件是作为对象处理的,所有事件对象继承自抽象类QEvent。此类用来表示程序内部发生或者来自于外部但应用程序应该知道的动作。事件能够能过被 QObject 的子类接受或者处理,但是通常用在与组件有关的应用中。本文档主要阐述了在一个典型应用中的事件接收与处理。
目录[隐藏]
|
当一个事件产生时,Qt 通过实例化一个 QEvent 的合适的子类来表示它,然后通过调用 event() 函数发送给 QObject 的实例(或者它的子类)。
event() 函数本身并不会处理事件,根据事件类型,它将调用相应的事件处理函数,并且返回事件被接受还是被忽略。
一些事件,比如 QMouseEvent 和 QKeyEvent,来自窗口系统;有的,比如 QTimerEvent,来自于其他事件源;另外一些则来自应用程序本身。
大部分事件类型有专门的类,比如 QResizeEvent, QPaintEvent, QMouseEvent, QKeyEvent 和 QCloseEvent。它们都是 QEvent 的子类,并且添加了自己特定的事件处理函数。比如 QResizeEvent 事件添加了 size()和 oldSize() 函数,使组件获知自身大小的改变。
有些事件支持不止一个事件类型。比如 QMouseEvent 鼠标事件,可以表示鼠标的按下,双击,移动,以及其它的一些操作。
每一个事件都有其相关联的类型,由 QEvent::Type 定义。我们能够很方便地在运行时用这些类型来判断该事件是哪一个子类。
因为程序响应方式的多样性和复杂性,Qt 的事件传递机制是富有弹性很灵活的。QCoreApplication::notify() 的相关文档阐述大部分内容;Qt Quarterly 中的文章 Another Look at Events 也进行了简要描述。在这里我们的阐述对于 95% 的程序而言来说已经足够了。
通常事件的处理需要调用一个虚函数。比如,QPaintEvent 事件的处理需要调用 QWidget::paintEvent() 函数。这个虚函数负责做出适当的响应,通常是用来重绘组件。如果你在自己的函数中并不打算实现所有的处理,你可以调用基类的实现。
例如,下面的代码用来处理鼠标左键点击一个自定义的选择框的操作,而其他的点击事件则被传递给基类 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); } }
如果你想代替基类的处理,你必须自己实现所有的功能。但是,如果你只想扩展子基类的功能,你只需要实现你自己需要的那部分,剩下的让基类来替你处理。
少数情况下,Qt 可能没有指定专门的处理函数,或者指定的处理函数不能满足要求。通常对 Tab 键的处理就会发生这种情况。一般地,Tab 键用来移动焦点,但是一些控件需要 Tab 键作其它的事情。
这些对象可以通过重新实现 QObject::event() 来满足需要,它们可以在通用处理调用之前或之后来加入自己的处理,或者完全将事件处理替换为自己的事件处理函数。一个非常罕见的控件或许既要处理 Tab 键,又要调用程序特定的事件类型。那么,我们就可以使用以下代码实现。
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
表示事件被阻止发送到其他的对象。
有时一个对象需要查看或者是截获传递给其他对象的事件。例如,对话框通常会过滤按键,比如修改回车键的处理。
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 对象上安装过滤器,我们也可以过滤发生在应用程序层次上的所有事件。这种全局的过滤器在其它对象过滤器调用之前被调用。它很强大,但是会减缓整个程序的事件传递时间;应当使用所讨论的另外的技术实现这一目标。
一些程序可以创建或者发送自己的事件。你可以像 Qt 事件循环所做的那样,构造合适的事件对象,并且通过 QCoreApplication::sendEvent() 和 QCoreApplication::postEvent() 来发送它们。
sendEvent() 立即处理事件。当它返回时,事件过滤器或者对象本身已经处理了该事件。大部分事件都有一个函数 isAccepted()
告诉你该事件在最后一个处理器被调用的时候,被接受了还是被拒绝了。
postEvent() 将事件放到一个队列里面稍后发送。当下一次 Qt 主循环到来时,它将作一些优化,并且发送队列中的所有事件。例如,如果有好几个改变大小的事件,它们将被合并成一个。重绘事件也是同样的处理,QWidget::update() 调用 postEvent(),可以避免多次重绘,消除闪烁并提高速度。
postEvent() 也被用在对象的初始化中。因为队列中的事件会在对象初始化之后很快立刻被分派。当实现一个控件时,意识到事件在它的生命周期里很早就被发送是很重要的;所以在构造函数中,我们应该尽早地初始化成员变量,要在它们有机会接收事件之前。
创建自定义的事件,我们需要定义一个事件标识,这个事件标识必须比大于 QEvent::User 的值,并且你还需要创建 QEvent 子类,传递自定义事件特定信息。详细信息请参见 QEvent 相关文档。