拳头,对问题的长度道歉 .
我试图将自定义Qt事件从子窗口小部件传播到顶部父窗口小部件,以便根据事件类型而不是链接信号触发某些操作 .
Qt docs建议用 postEvent()
发布的每个具有 accept()
和 ignore()
方法的事件都可以传播(意味着每个 QEvent
子类) .
我试图覆盖 customEvents
方法而不是 events
但无济于事 .
Python
我在Python中使用PyQt4尝试了这个(Qt版本是4.6) .
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Foo(QWidget):
def doEvent(self):
QApplication.postEvent(self, QEvent(12345))
def event(self, event):
event.ignore()
return False
class Bar(QWidget):
def __init__(self, *args, **kwargs):
super(Bar, self).__init__(*args, **kwargs)
self.foo = Foo(self)
layout = QHBoxLayout()
layout.addWidget(self.foo)
self.setLayout(layout)
def event(self, event):
if event.type() == 12345:
self.someEventHandler()
return True
def someEventHandler(self):
print 'Handler in {0}'.format(self.__class__.__name__)
if __name__=='__main__':
app = QApplication([''])
bar = Bar()
bar.show()
bar.foo.doEvent()
app.exec_()
在这个示例中, Bar.someEventHandler()
只会在事件以 self.parent()
作为第一个参数发布时触发,如下所示:
def doEvent(self):
QApplication.postEvent(self.parent(), QEvent(12345))
这是可以理解的,因为事件直接传递给接收对象 .
C.
C中的类似例子:
foobar.h
#ifndef FOOBAR_H
#define FOOBAR_H
#include <QtGui>
class Foo : public QWidget
{
Q_OBJECT
public:
Foo(QWidget *parent = 0);
void doEvent();
bool event(QEvent *);
};
class Bar : public QWidget
{
Q_OBJECT
public:
Bar(QWidget *parent = 0);
Foo *foo;
bool event(QEvent *);
};
#endif // FOOBAR_H
foobar.cpp
#include "foobar.h"
Foo::Foo(QWidget *parent)
: QWidget(parent) {}
void Foo::doEvent() {
QEvent *event = new QEvent(QEvent::User);
QApplication::postEvent(this, event);
}
bool Foo::event(QEvent *event)
{
event->ignore();
return QWidget::event(event);
}
Bar::Bar(QWidget *parent)
: QWidget(parent)
{
foo = new Foo(this);
}
bool Bar::event(QEvent *event)
{
if (event->type() == QEvent::User) {
qDebug() << "Handler triggered";
return true;
}
return QWidget::event(event);
}
main.cpp
#include <QtGui>
#include "foobar.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Bar bar(0);
bar.show();
bar.foo->doEvent();
return app.exec();
}
与python中的相同,只有在事件直接传递给对象时才有效 .
void Foo::doEvent() {
QEvent *event = new QEvent(QEvent::User);
QApplication::postEvent(this->parentWidget(), event);
}
也许我错过了这一点,是否有可能只向上传播Key和Mouse事件?
3 回答
我花了一些时间浏览Qt源代码来回答你的问题,我最终提出的是:事件传播到父窗口小部件是由
QApplication::notify
执行的,基本上是一个带有各种事件的长switch
语句 . 例如,这是QEvent::WhatsThisClicked
的完成方式:现在,重点是:这是为用户定义的事件(以及许多其他显式处理的标准Qt事件)执行的 not ,因为
default
子句是:并且
notify_helper
不会传播事件 . 所以,我的答案是:显然,用户定义的事件不会传播到父窗口小部件,你必须自己做(或更好:覆盖QApplication::notify
(它是一个虚拟公共成员)并为你的事件添加事件传播) .我希望有所帮助 .
在Python中重新实现
QApplication.notify
,它将传播自定义事件 .在Qt
notfy_helper
确保调用事件过滤器(QApplications和接收器),但我跳过它,因为我不需要它们,notfy_helper
是私有成员 .而不是使用QApplication的实例,我们使用我们的子类的实例 .
作为rebus答案的替代方案,此代码段实现了事件的手动传播:
请注意,这使用sendEvent,因此必须在目标对象所在的线程上调用 . 这种限制可以通过更多间接来解决 .
请注意,您需要自己调用_manual_propagate /而不是/ sendEvent . 这是rebus技术的“自动化程度较低”版本 .
此外,由于此版本使用sendEvent,因此可以正确调用对象上的事件过滤器 .
自定义事件不会传播的相关原因,至少在Qt 4.8中,是这样的:
也就是说,
QObject::event
在收到自定义事件时调用另一个方法,然后break
从它的switch
语句中调用并执行return true;'
. 这个return true
向呼叫者发出信号(通常QCoreApplication::notify
表示该事件已被处理 .