How to propagate the wheelEvent to multiple widgets of my centralwidget
-
Hello all,
I am making a C++ desktop application with Qt 6.2.4.
Basically, I have a MainWindow which has a QVBoxLayout which has 2 custom widgets (let's call them MyWidget which has the wheelEvent function overrided).
Code extract:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setSpacing(0); MyWidget* widget1 = new MyWidget(this); MyWidget* widget2 = new MyWidget(this); vLayout->addWidget(widget1); vLayout->addWidget(widget2); QWidget* centralWidget = new QWidget(); centralWidget->setLayout(vLayout); setCentralWidget(centralWidget); }When my mouse is on the first MyWidget and I use the wheel with the key Ctrl pressed, the action is only performed on this widget (the focused one), but I want that all MyWidget receive the event.
How to do so ? I try to implement the eventFilter function in the MainWindow class, but I don't succeed on catching the event at the highest level in order to propagate it.
Code extract:
bool MainWindow::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::Wheel) { if (QWidget *widget = qobject_cast<QWidget*>(obj); widget && widget->layout()) { QString test = widget->objectName(); QLayout *layout = widget->layout(); int nb = layout->count(); for (int i{0}; i < layout->count(); ++i) { if (QLayoutItem *item = layout->itemAt(i); item->widget()) { QCoreApplication::sendEvent(item->widget(), event); } } return true; } } return QMainWindow::eventFilter(obj, event); }Hope I am enough clear.
-
Hello all,
I am making a C++ desktop application with Qt 6.2.4.
Basically, I have a MainWindow which has a QVBoxLayout which has 2 custom widgets (let's call them MyWidget which has the wheelEvent function overrided).
Code extract:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setSpacing(0); MyWidget* widget1 = new MyWidget(this); MyWidget* widget2 = new MyWidget(this); vLayout->addWidget(widget1); vLayout->addWidget(widget2); QWidget* centralWidget = new QWidget(); centralWidget->setLayout(vLayout); setCentralWidget(centralWidget); }When my mouse is on the first MyWidget and I use the wheel with the key Ctrl pressed, the action is only performed on this widget (the focused one), but I want that all MyWidget receive the event.
How to do so ? I try to implement the eventFilter function in the MainWindow class, but I don't succeed on catching the event at the highest level in order to propagate it.
Code extract:
bool MainWindow::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::Wheel) { if (QWidget *widget = qobject_cast<QWidget*>(obj); widget && widget->layout()) { QString test = widget->objectName(); QLayout *layout = widget->layout(); int nb = layout->count(); for (int i{0}; i < layout->count(); ++i) { if (QLayoutItem *item = layout->itemAt(i); item->widget()) { QCoreApplication::sendEvent(item->widget(), event); } } return true; } } return QMainWindow::eventFilter(obj, event); }Hope I am enough clear.
@TikkiTxuck
While you wait for someone who knows better than I. I believe wheel events go to the focussed window first, and if that consumes them (yours does) they do not get propagated higher up the tree to your man window.You may have to install the
eventFilter()on the application rather than the main window? -
Hi and welcome to devnet,
Might be a silly question but did you install the filter on your widgets ?
-
@TikkiTxuck
While you wait for someone who knows better than I. I believe wheel events go to the focussed window first, and if that consumes them (yours does) they do not get propagated higher up the tree to your man window.You may have to install the
eventFilter()on the application rather than the main window?@JonB I tried to install it on
qApp, onQCoreApplication::instance()and onQApplication::instance(), but none of them was working. Certainly because I miss something, but I don't know what :/ -
@JonB I tried to install it on
qApp, onQCoreApplication::instance()and onQApplication::instance(), but none of them was working. Certainly because I miss something, but I don't know what :/@TikkiTxuck Shouldn't you install it in the top widget where you want to catch the events?
-
Hi and welcome to devnet,
Might be a silly question but did you install the filter on your widgets ?
@SGaist Thanks for the welcoming !
You mean calling
installEventFilteron each widget which must receive the propagated event ? Can you provide some code example please ? -
@JonB I tried to install it on
qApp, onQCoreApplication::instance()and onQApplication::instance(), but none of them was working. Certainly because I miss something, but I don't know what :/@TikkiTxuck
I knocked up the following simplest, standalone example from your code:#include <QDebug> #include <QLabel> #include <QVBoxLayout> #include <QWidget> #include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->setSpacing(0); QLabel* widget1 = new QLabel("widget1"); QLabel* widget2 = new QLabel("widget2"); vLayout->addWidget(widget1); vLayout->addWidget(widget2); QWidget* centralWidget = new QWidget(); centralWidget->setLayout(vLayout); setCentralWidget(centralWidget); installEventFilter(this); } bool MainWindow::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::Wheel) { qDebug() << "Wheelevent"; return true; } return QMainWindow::eventFilter(obj, event); }I run Qt 5.15 under Linux (Xorg desktop). I do not have Qt6. This code debugs out "Wheelevent" when I roll the wheel anywhere over the main window, whether inside the
QLabels or not.So either it's different at Qt6 (doubt it), I have misunderstood and this code does not represent your situation (I think it does) or where you say
MyWidget which has the wheelEvent function overrided
maybe if your override "eats" the wheel event (i.e. returns
true) then that prevents the propagation you are wanting?
UPDATE
I tried subclassing theQLabels and overriding theeventFilter(). I still always received theMainWindows wheel event, after the label's wheel event, regardless of whether the latter'seventFilter()returned true or false. So it seems you should too? -
@SGaist Thanks for the welcoming !
You mean calling
installEventFilteron each widget which must receive the propagated event ? Can you provide some code example please ?@TikkiTxuck said in How to propagate the wheelEvent to multiple widgets of my centralwidget:
You mean calling installEventFilter on each widget
No, calling installEventFilter on top most widget (your main window for example)...
-
Ok! I make some progress base on your help and come to the following code:
#include <QtWidgets> class MyWidget : public QWidget { public: MyWidget(QWidget *parent = nullptr) : QWidget(parent) { } void paintEvent(QPaintEvent *event) override { QPainter painter(this); painter.fillRect(rect(), Qt::white); } protected: void wheelEvent(QWheelEvent *event) override { qDebug() << "Wheel event on widget:" << objectName(); event->ignore(); } }; class EventFilter : public QObject { Q_OBJECT public: EventFilter(QObject *parent = nullptr) : QObject(parent) {} protected: bool eventFilter(QObject *obj, QEvent *event) override { if (event->type() == QEvent::Wheel) { QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event); // Propagate the wheel event to other widgets in the layout QList<QWidget*> widgets = obj->findChildren<QWidget*>(); for (QWidget *widget : widgets) { if (widget != obj) { QApplication::sendEvent(widget, new QWheelEvent( wheelEvent->position(), wheelEvent->globalPosition(), wheelEvent->pixelDelta(), wheelEvent->angleDelta(), wheelEvent->buttons(), wheelEvent->modifiers(), wheelEvent->phase(), wheelEvent->inverted() )); } } event->accept(); return true; } return QObject::eventFilter(obj, event); } }; class MyMainWindow : public QMainWindow { public: MyMainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { setObjectName("MainWindow"); centralWidget_ = new QWidget; centralWidget_->setObjectName("CentralWidget"); layout_ = new QVBoxLayout(centralWidget_); for (int i = 0; i < 4; ++i) { MyWidget *widget = new MyWidget(this); widget->setObjectName("Widget " + QString::number(i)); layout_->addWidget(widget); } setCentralWidget(centralWidget_); installEventFilter(&eventFilter_); } private: QWidget *centralWidget_; QVBoxLayout *layout_; EventFilter eventFilter_; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MyMainWindow mainWindow; mainWindow.show(); return app.exec(); } #include "main.moc"As excepted, my event is propagated to the other widgets but the widget that originally received the event receive it twice. How to fix this ?
-
Ok! I make some progress base on your help and come to the following code:
#include <QtWidgets> class MyWidget : public QWidget { public: MyWidget(QWidget *parent = nullptr) : QWidget(parent) { } void paintEvent(QPaintEvent *event) override { QPainter painter(this); painter.fillRect(rect(), Qt::white); } protected: void wheelEvent(QWheelEvent *event) override { qDebug() << "Wheel event on widget:" << objectName(); event->ignore(); } }; class EventFilter : public QObject { Q_OBJECT public: EventFilter(QObject *parent = nullptr) : QObject(parent) {} protected: bool eventFilter(QObject *obj, QEvent *event) override { if (event->type() == QEvent::Wheel) { QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(event); // Propagate the wheel event to other widgets in the layout QList<QWidget*> widgets = obj->findChildren<QWidget*>(); for (QWidget *widget : widgets) { if (widget != obj) { QApplication::sendEvent(widget, new QWheelEvent( wheelEvent->position(), wheelEvent->globalPosition(), wheelEvent->pixelDelta(), wheelEvent->angleDelta(), wheelEvent->buttons(), wheelEvent->modifiers(), wheelEvent->phase(), wheelEvent->inverted() )); } } event->accept(); return true; } return QObject::eventFilter(obj, event); } }; class MyMainWindow : public QMainWindow { public: MyMainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { setObjectName("MainWindow"); centralWidget_ = new QWidget; centralWidget_->setObjectName("CentralWidget"); layout_ = new QVBoxLayout(centralWidget_); for (int i = 0; i < 4; ++i) { MyWidget *widget = new MyWidget(this); widget->setObjectName("Widget " + QString::number(i)); layout_->addWidget(widget); } setCentralWidget(centralWidget_); installEventFilter(&eventFilter_); } private: QWidget *centralWidget_; QVBoxLayout *layout_; EventFilter eventFilter_; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MyMainWindow mainWindow; mainWindow.show(); return app.exec(); } #include "main.moc"As excepted, my event is propagated to the other widgets but the widget that originally received the event receive it twice. How to fix this ?
@TikkiTxuck
Make sure none of the child widgets propagate the event back to their parent?
Start with a central widget which contains no child widgets. Does that work or duplicate? Then add a single child widget and check that. Etc. -
@TikkiTxuck
Make sure none of the child widgets propagate the event back to their parent?
Start with a central widget which contains no child widgets. Does that work or duplicate? Then add a single child widget and check that. Etc.@JonB In the code I provide in my previous message, the MyWidget seems to execute the code assiocated to the wheelevent, and then the event is propagated to the parent widget (the central widget is this case). And the central widget re-propagate the event to all its children (including the original widget).
How to avoid this first process, the one before sending the event to the parent ?