Распространить пользовательский QEvent на родительский виджет в Qt/PyQt
кулак, извиняюсь за длину вопроса.
я пытаюсь распространить пользовательское событие 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);
}
возможно, я пропустил точку, возможно ли, что только события ключа и мыши распространяются вверх?
3 ответов
Я потратил некоторое время на просмотр источника 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
не распространяет события. Итак, мой ответ: по-видимому, пользовательские события не распространяются на родительские виджеты ,вам придется сделать это самостоятельно (или лучше: override QApplication::notify
(это виртуальный публичный член) и добавьте распространение событий для ваших событий).
надеюсь, это поможет.
повторная реализация QApplication.notify
в Python, который будет распространять пользовательские события.
В Qt notfy_helper
гарантирует, что фильтры событий вызываются (как QApplications, так и receivers), но я пропустил это, поскольку они мне не нужны и 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)
в качестве альтернативы ответу ребуса этот фрагмент реализует ручное распространение события:
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 самостоятельно. Это" менее автоматическая " версия техники ребуса.
кроме того, потому что эта версия использует 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
, что событие было обработано.