每当将 QWidget 添加到 QApplication 的小部件树时,如何接收事件?
Posted
技术标签:
【中文标题】每当将 QWidget 添加到 QApplication 的小部件树时,如何接收事件?【英文标题】:How to receive event whenever a QWidget is added to QApplication's widget tree? 【发布时间】:2016-08-25 22:31:57 【问题描述】:我想在应用程序范围内检查一些东西。具体来说,我想检查添加到应用程序中的每个小部件。
类似的事情可以在 javascript/html 中完成,您可以在其中添加 DOM 突变侦听器,该侦听器会在 DOM 更改时触发。
我可以在QApplication
上收听添加的小部件吗?具体来说,捕获添加到QApplication
对象的小部件作为子级或添加到任何***小部件或它们的子级、子级等的小部件...
如果不可能,最好的解决方法是什么?
【问题讨论】:
您真的需要检查应用程序中的每一个小部件吗? @bnaecker 是的。我需要能够“等待小部件” - 例如。当出现符合某些抽象描述的小部件时触发事件。只有另一种方法是在一个间隔上循环整个 qapplication 并找到小部件。这很讨厌。我还缓存了一些小部件选择器,我想在添加/删除小部件时更新缓存。 什么是“抽象描述”?这些小部件是特定类的吗?例如,您需要检查所有QLabel
对象,还是您自己的任何自定义类?还是整个应用程序中的任何小部件?
@bnaecker 抽象描述是一个接受QWidget*
并返回真或假的谓词。所以是的,正如我在我的问题和对您的评论的最后回复中所说,我想检查在 qapplication 中添加或删除的 any 小部件。但这个问题特别是关于添加。
我不是在问“抽象描述”的定义,而是你的实际谓词函数是什么。这可能有助于减少问题的规模,并可能提出更好的解决方案。
【参考方案1】:
最稳定的解决方案可能是每隔一段时间遍历对象层次结构,从QApplication
作为根开始,并使用谓词检查每个对象。这将是低效的,这就是为什么我要求提供有关您实际要查询的对象的更多信息。另一方面,对于一个测试框架,你可能不太关心效率。
不过,也许可以做些别的事情。您可以在任何QObject
上安装事件过滤对象,这些对象定义了它们在接收到来自 Qt 事件系统的事件时如何响应。您可以在根 QApplication
对象上安装一个事件过滤器(并在创建的任何子对象上递归),以检查事件是否为 QChildEvent
以及 added()
是否为它返回 true。如果这是真的,这意味着添加了一个新的孩子,那么您也可以将此事件过滤器安装到该孩子上。所有其他事件都将原封不动地传递下去。您可以阅读有关安装事件过滤器的更多信息here. 本质上,过滤器是一个 QObject
子类,它定义了 eventFilter()
函数,对于应该过滤(停止)的事件返回 true,否则返回 false。当这返回 true 时,在新创建的对象上运行您的谓词并在其上安装相同的事件过滤器。
需要注意的一个问题是eventFilter()
函数只接收QObject
s。您可以通过调用isWidgetType()
来了解对象是否为QWidget
,但您不知道更多。因此,只要您的谓词只能使用为这些通用基类定义的方法和数据,就可以了。
【讨论】:
事件过滤器是个好主意,但这不会覆盖应用程序中现有的事件过滤器吗? 你会捕获你感兴趣的事件,做你的检查,在新的孩子上安装事件过滤器,然后返回 false。这意味着您拦截事件以检查其类型,但实际上并未过滤 事件。对象会照常处理它。 我实现了,完美!绝对比轮询更快、更流畅。 我刚刚发现了一个缺陷。根据文档在 QEvent::ChildAdded 案例中,孩子尚未完全构造。您知道如何等待构建小部件吗? 我认为这意味着QObject
或QWidget
基类构造函数在发送此事件时已被调用,但派生类的完整构造函数尚未运行。因此,您似乎应该将其视为这些基类之一(并调用方法或检查数据),但不能将其视为任何派生类。【参考方案2】:
根据@bnaecker 的回答,这里有一些代码:
AddChildEventFilter.h
#include <QObject>
class QEvent;
class AddChildEventFilter: public QObject
Q_OBJECT
public:
AddChildEventFilter(QObject* parent=0);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
;
AddChildEventFilter.cpp
#include "AddChildEventFilter.h"
#include <QEvent>
#include <QDebug>
#include <QWidget>
#include <QChildEvent>
AddChildEventFilter::AddChildEventFilter(QObject* parent):QObject(parent)
bool AddChildEventFilter::eventFilter(QObject* obj, QEvent* event)
if(QWidget* widget = dynamic_cast<QWidget*>(obj))
if(QChildEvent* chevent = dynamic_cast<QChildEvent*>(event))
if(chevent->added())
QObject* child = chevent->child();
qDebug()<<"Child added: "<<child->metaObject()->className()<<"to"<<widget->metaObject()->className();
child->installEventFilter(new AddChildEventFilter(child));
return false;
用法:
#include "AddChildEventFilter.h"
#include <QWidget>
#include <QApplication>
void PrintAllEvents()
for(QWidget* widget: QApplication::allWidgets())
widget->installEventFilter(new AddChildEventFilter(widget));
【讨论】:
这很好,但请注意您不需要在AddChildEventFilter.cpp
中执行dynamic_cast
s。每个QEvent
都对其类型进行编码,可通过type()
方法访问。所以你可以和if (chevent->type() == QEvent::ChildEvent) ...
比较。我也会使用if (obj->isWidgetType())
而不是第一个dynamic_cast
。
@bnaecker 我知道这些,但我需要实际访问子类'(例如child()
)方法,所以我需要转换它。
按照 bnaecker 的建议检查案例,然后使用 'static_cast' 而不是 'dynamic_cast' 会不会更有效?
@VaidotasStrazdas 是的,我有点懒得浏览文档并首先根据用于 Qt 事件的枚举器对事件类型进行 IF 检查。由于我注意到这并没有影响性能,因此我没有对其进行优化。以上是关于每当将 QWidget 添加到 QApplication 的小部件树时,如何接收事件?的主要内容,如果未能解决你的问题,请参考以下文章