在Qt/PyQt中将自定义QEvent传播到父窗口小部件

Dav*_*cic 11 events qt pyqt event-propagation

拳头,对问题的长度道歉.

我试图将自定义Qt事件从子窗口小部件传播到顶部父窗口小部件,以便根据事件类型而不是链接信号触发某些操作.

Qt的文档表明,每一个事件与公布postEvent()accept()ignore()方法可以传播(这意味着每个QEvent子类).

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

蟒蛇

我在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_()
Run Code Online (Sandbox Code Playgroud)

在这个例子中Bar.someEventHandler(),只有当事件self.parent()作为第一个参数发布时才会触发,如下所示:

def doEvent(self):
    QApplication.postEvent(self.parent(), QEvent(12345))
Run Code Online (Sandbox Code Playgroud)

这是可以理解的,因为事件直接传递给接收对象.

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
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

与python中的相同,只有在事件直接传递给对象时才有效.

void Foo::doEvent() {
    QEvent *event = new QEvent(QEvent::User);
    QApplication::postEvent(this->parentWidget(), event);
}
Run Code Online (Sandbox Code Playgroud)

也许我错过了这一点,是否有可能只向上传播Key和Mouse事件?

Gre*_*g S 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();
            }
        }
...
Run Code Online (Sandbox Code Playgroud)

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

 default:
        res = d->notify_helper(receiver, e);
        break;
Run Code Online (Sandbox Code Playgroud)

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

我希望有所帮助.

  • 相关来源[这里](http://qt.gitorious.org/qt/qt/blobs/4.7/src/corelib/kernel/qcoreapplication.cpp) (2认同)

Dav*_*cic 5

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

在Qt notfy_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)
Run Code Online (Sandbox Code Playgroud)

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

import sys
if __name__=='__main__':
    app = MyApp(sys.argv)
Run Code Online (Sandbox Code Playgroud)