11

拳头,为问题的长度道歉。

我正在尝试将自定义 Qt 事件从子小部件传播到顶级父小部件,以便根据事件类型而不是链接信号触发一些操作。

Qt 文档建议可以传播带有和方法发布的每个事件(意味着每个postEvent()accept()类)。ignore()QEvent

我试图覆盖customEvents方法而不是events但无济于事。

Python

我已经使用 PyQt4(Qt 版本是 4.6)在 Python 中尝试过这个。

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++ 中的类似示例:

foob​​ar.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

foob​​ar.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);
}

主文件

#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 事件向上传播?

4

3 回答 3

18

为了回答您的问题,我花了一些时间浏览 Qt 源代码,我最终想到的是:将事件传播到父小部件是由 执行的,基本上是一个包含各种事件QApplication::notify的长语句。switch例如,这是如何完成的QEvent::WhatsThisClicked

...
case QEvent::WhatsThisClicked:
        {
            QWidget *w = static_cast<QWidget *>(receiver);
            while (w) {
                res = d->notify_helper(w, e);
                if ((res && e->isAccepted()) || w->isWindow())
                    break;
                w = w->parentWidget();
            }
        }
...

现在,重点是:这不会针对用户定义的事件(以及许多其他显式处理的标准 Qt 事件)执行,因为该default子句是:

 default:
        res = d->notify_helper(receiver, e);
        break;

并且notify_helper不传播事件。所以,我的回答是:显然,用户定义的事件不会传播到父小部件,你必须自己做(或者更好:覆盖QApplication::notify(它是一个虚拟公共成员)并为你的事件添加事件传播)。

我希望这会有所帮助。

于 2010-07-06T08:06:18.360 回答
5

在 Python 中重新实现QApplication.notify将传播自定义事件。

在 Qtnotfy_helper中确保调用事件过滤器(QApplications 和接收器),但我跳过了它,因为我不需要它们并且notfy_helper是私有成员。

from PyQt4.QtCore import QEvent
from PyQt4.QtGui import QApplication

class MyApp(QApplication):
    def notify(self, receiver, event):
        if event.type() > QEvent.User:
            w = receiver
            while(w):
                # Note that this calls `event` method directly thus bypassing
                # calling qApplications and receivers event filters
                res = w.event(event);
                if res and event.isAccepted():
                    return res
                w = w.parent()
        return super(MyApp, self).notify(receiver, event)

而不是使用 QApplication 的实例,我们使用我们的子类的实例。

import sys
if __name__=='__main__':
    app = MyApp(sys.argv)
于 2010-07-07T11:02:48.613 回答
1

作为 rebus 答案的替代方案,此代码段实现了事件的手动传播:

def _manual_propagate(target, evt):
    app = QtGui.QApplication.instance()
    while target:
        app.sendEvent(target, evt)
        if not evt.isAccepted():
            if hasattr(target, 'parent'):
                target = target.parent()
        else:
            target = None
    return evt.isAccepted()

请注意,这使用了 sendEvent,因此必须在目标对象所在的线程上调用。这个限制可以通过更多的间接来解决。

请注意,您需要自己调用 _manual_propagate / 而不是 / sendEvent。这是 rebus 技术的“不太自动化”版本。

此外,由于此版本使用 sendEvent,因此可以正确调用对象上的事件过滤器。


至少在 Qt 4.8 中没有传播自定义事件的相关原因是:

//qobject.cpp
bool QObject::event(QEvent *e)
{
    switch (e->type()) {
    //several cases skipped
    default:
        if (e->type() >= QEvent::User) {
            customEvent(e);
            break;
        }
        return false;
    }
    return true;
}

//more skips

/*!
    This event handler can be reimplemented in a subclass to receive
    custom events. Custom events are user-defined events with a type
    value at least as large as the QEvent::User item of the
    QEvent::Type enum, and is typically a QEvent subclass. The event
    is passed in the \a event parameter.

    \sa event(), QEvent
*/
void QObject::customEvent(QEvent * /* event */)
{
}

QObject::event在接收到自定义事件时调用另一个方法,然后break退出它的switch语句并执行return true;'。这return true向调用者发出信号(通常QCoreApplication::notify表示事件已被处理。

于 2013-05-02T16:51:02.070 回答