Is there a way to find out which widget consumed an event?
-
I have a pretty simple and straightforward widget hierarchy. There are a couple layouts, QSplitter, and another layout inside the splitter that contains QTabBar and my custom widget. My widget wants key events, but never gets them. Is there a way to find out which widget consumes the key events?
-
@jeremy_k, awesome suggestion, thank you! I didn't know about that method. And no, it returns another widget - precisely the one that does get the events according to my investigation.
Here's the hierarchy:
The leaf widgets that should receive focus are either one of the "WindowContainers" - they are displayed side by side. The widget that does receive focus is the QTabBar inside the QStackedLayout. Why does it grab focus?
There is no use of
WindowTransparentForInput
anywhere in the project.WindowContainer
callssetFocusPolicy(Qt::StrongFocus)
in its constructor.If I set policy to
NoFocus
on theQTabBar
, then aQListWidget
grabs focus (not shown in the hierarchy above - it's below the cropped area of interest). Now the problem is that I do want theQListWidget
to receive focus when clicked, but I don't want it to grab focus aggressively.P. S. Thank you for looking into my problem and responding, gentlemen.
Assuming that the right window flags and attributes are set, I can't see more than three options:
- (a) Someone else (i.e. the
QTabBar
) steals focus, so the custom widgets don't get get it. - (b) The custom widgets, for some reason, do not accept focus.
- (c) The custom widgets, for some reason, get key events but they don't consume them. So they get propagated down the parental chain.
You have already excluded (a) by calling
setFocusPolicy(Qt::NoFocus);
on theQTabBar
.
In addition, you canconnect (qApp, &QApplication::focusChanged, qApp, [&](Qwidget *old, QWidget *now){qDebug() << old->objectName() << now-objectName(); });
...to debug focus delivery.
To exclude (c), override QWidget's
virtual void keyPressEvent(QKeyEvent *event)
(if you haven't already), send a fake key event directly to your custom widget and see, who eventually consumes it.QKeyEvent ke(/* construct it according to what gets delivered to QTabBar */); QApplication::sendEvent(myCustomWidget, &ke);
You can also (for analysis only!) intercept the key events of the tab bar in an event filter and send them to a custom widget. That will probably result in a loop and crash the application. But surgeons have to carve the chest open before the bugfixing starts ;-)
- (a) Someone else (i.e. the
-
I have a pretty simple and straightforward widget hierarchy. There are a couple layouts, QSplitter, and another layout inside the splitter that contains QTabBar and my custom widget. My widget wants key events, but never gets them. Is there a way to find out which widget consumes the key events?
@Violet-Giraffe
Maybe with an application-leveleventFilter()
, not sure.
Does your widget have focus? Isn't that what is required for a widget to receive key events? -
@Violet-Giraffe
Maybe with an application-leveleventFilter()
, not sure.
Does your widget have focus? Isn't that what is required for a widget to receive key events?@JonB, it should have focus, it's a leaf widget that I clicked with a mouse.
So far I have discovered that
eventFilter()
for the central widget receives the events, and event filter for the splitter does not, puzzling. -
@JonB, it should have focus, it's a leaf widget that I clicked with a mouse.
So far I have discovered that
eventFilter()
for the central widget receives the events, and event filter for the splitter does not, puzzling.@Violet-Giraffe
I don't know, but try taking the splitter out temporarily? Put your widget on its own and verify it works there? -
@JonB, it should have focus, it's a leaf widget that I clicked with a mouse.
So far I have discovered that
eventFilter()
for the central widget receives the events, and event filter for the splitter does not, puzzling.@Violet-Giraffe said in Is there a way to find out which widget consumed an event?:
@JonB, it should have focus, it's a leaf widget that I clicked with a mouse.
Does QApplication::focusWidget() agree that the widget has focus?
-
I have a pretty simple and straightforward widget hierarchy. There are a couple layouts, QSplitter, and another layout inside the splitter that contains QTabBar and my custom widget. My widget wants key events, but never gets them. Is there a way to find out which widget consumes the key events?
@Violet-Giraffe
If the widget has focus, it should receive key events, unless disabled in the window flags. This flag controls it: ‚Qt::WindowTransparentForInput
. -
@Violet-Giraffe said in Is there a way to find out which widget consumed an event?:
@JonB, it should have focus, it's a leaf widget that I clicked with a mouse.
Does QApplication::focusWidget() agree that the widget has focus?
@jeremy_k, awesome suggestion, thank you! I didn't know about that method. And no, it returns another widget - precisely the one that does get the events according to my investigation.
Here's the hierarchy:
The leaf widgets that should receive focus are either one of the "WindowContainers" - they are displayed side by side. The widget that does receive focus is the QTabBar inside the QStackedLayout. Why does it grab focus?
There is no use of
WindowTransparentForInput
anywhere in the project.WindowContainer
callssetFocusPolicy(Qt::StrongFocus)
in its constructor.If I set policy to
NoFocus
on theQTabBar
, then aQListWidget
grabs focus (not shown in the hierarchy above - it's below the cropped area of interest). Now the problem is that I do want theQListWidget
to receive focus when clicked, but I don't want it to grab focus aggressively.P. S. Thank you for looking into my problem and responding, gentlemen.
-
@jeremy_k, awesome suggestion, thank you! I didn't know about that method. And no, it returns another widget - precisely the one that does get the events according to my investigation.
Here's the hierarchy:
The leaf widgets that should receive focus are either one of the "WindowContainers" - they are displayed side by side. The widget that does receive focus is the QTabBar inside the QStackedLayout. Why does it grab focus?
There is no use of
WindowTransparentForInput
anywhere in the project.WindowContainer
callssetFocusPolicy(Qt::StrongFocus)
in its constructor.If I set policy to
NoFocus
on theQTabBar
, then aQListWidget
grabs focus (not shown in the hierarchy above - it's below the cropped area of interest). Now the problem is that I do want theQListWidget
to receive focus when clicked, but I don't want it to grab focus aggressively.P. S. Thank you for looking into my problem and responding, gentlemen.
Assuming that the right window flags and attributes are set, I can't see more than three options:
- (a) Someone else (i.e. the
QTabBar
) steals focus, so the custom widgets don't get get it. - (b) The custom widgets, for some reason, do not accept focus.
- (c) The custom widgets, for some reason, get key events but they don't consume them. So they get propagated down the parental chain.
You have already excluded (a) by calling
setFocusPolicy(Qt::NoFocus);
on theQTabBar
.
In addition, you canconnect (qApp, &QApplication::focusChanged, qApp, [&](Qwidget *old, QWidget *now){qDebug() << old->objectName() << now-objectName(); });
...to debug focus delivery.
To exclude (c), override QWidget's
virtual void keyPressEvent(QKeyEvent *event)
(if you haven't already), send a fake key event directly to your custom widget and see, who eventually consumes it.QKeyEvent ke(/* construct it according to what gets delivered to QTabBar */); QApplication::sendEvent(myCustomWidget, &ke);
You can also (for analysis only!) intercept the key events of the tab bar in an event filter and send them to a custom widget. That will probably result in a loop and crash the application. But surgeons have to carve the chest open before the bugfixing starts ;-)
- (a) Someone else (i.e. the
-
Assuming that the right window flags and attributes are set, I can't see more than three options:
- (a) Someone else (i.e. the
QTabBar
) steals focus, so the custom widgets don't get get it. - (b) The custom widgets, for some reason, do not accept focus.
- (c) The custom widgets, for some reason, get key events but they don't consume them. So they get propagated down the parental chain.
You have already excluded (a) by calling
setFocusPolicy(Qt::NoFocus);
on theQTabBar
.
In addition, you canconnect (qApp, &QApplication::focusChanged, qApp, [&](Qwidget *old, QWidget *now){qDebug() << old->objectName() << now-objectName(); });
...to debug focus delivery.
To exclude (c), override QWidget's
virtual void keyPressEvent(QKeyEvent *event)
(if you haven't already), send a fake key event directly to your custom widget and see, who eventually consumes it.QKeyEvent ke(/* construct it according to what gets delivered to QTabBar */); QApplication::sendEvent(myCustomWidget, &ke);
You can also (for analysis only!) intercept the key events of the tab bar in an event filter and send them to a custom widget. That will probably result in a loop and crash the application. But surgeons have to carve the chest open before the bugfixing starts ;-)
@Axel-Spoerl
Great suggestions! I added theQApplication::focusChanged
signal to my UI debugging tool which displays the widget hierarchy of the application, now I can see focus changes in real time.It appears that my custom widget cannot get focus by clicking the mouse. Other widgets always get it instead (first the
QTabBar
, afterQTabBar
gotNoFocus
policy - now it isQListWidget
).However, If I press the Tab button enough times, it does get focus and starts receiving key events. But when I click on my widget,
QListWidget
gets focus again.
And I can't assignNoFocus
to all the widgets in the program, some of them should be focusable.You said "Assuming that the right window flags and attributes are set", what would be the right flags and attributes, and is it possible my problem is caused by not having the right ones?
One thing that may be important is that this widget calls
winId()
to become a native window. And I'm debugging on Windows. - (a) Someone else (i.e. the
-
@Axel-Spoerl
Great suggestions! I added theQApplication::focusChanged
signal to my UI debugging tool which displays the widget hierarchy of the application, now I can see focus changes in real time.It appears that my custom widget cannot get focus by clicking the mouse. Other widgets always get it instead (first the
QTabBar
, afterQTabBar
gotNoFocus
policy - now it isQListWidget
).However, If I press the Tab button enough times, it does get focus and starts receiving key events. But when I click on my widget,
QListWidget
gets focus again.
And I can't assignNoFocus
to all the widgets in the program, some of them should be focusable.You said "Assuming that the right window flags and attributes are set", what would be the right flags and attributes, and is it possible my problem is caused by not having the right ones?
One thing that may be important is that this widget calls
winId()
to become a native window. And I'm debugging on Windows.@Violet-Giraffe said in Is there a way to find out which widget consumed an event?:
this widget calls winId() to become a native window
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.my custom widget cannot get focus by clicking the mouse
Have you overridden
QWidget::event()
?
If so, does the overridereturn QWidget::event(event)
when a mouse event is delivered (and hopefully remains unconsumed)?
What doesqDebug() << myCustomWidget->testAttribute(Qt::WA_TransparentForMouseEvents)
say? -
@Violet-Giraffe said in Is there a way to find out which widget consumed an event?:
this widget calls winId() to become a native window
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.my custom widget cannot get focus by clicking the mouse
Have you overridden
QWidget::event()
?
If so, does the overridereturn QWidget::event(event)
when a mouse event is delivered (and hopefully remains unconsumed)?
What doesqDebug() << myCustomWidget->testAttribute(Qt::WA_TransparentForMouseEvents)
say?@Axel-Spoerl
You're right, it must be related to the pointer events, but in a way that I cannot yet figure out. The thing is that thisQWidget
which doesn't get focus is acting as a host to a native widget with its own window procedure. That procedure wants to processWM_POINTERDOWN
and does get it! Despite the host widget not getting focus. And the same procedure also wants to processWM_KEYDOWN
and doesn't get it. The host widget doesn't get key events, too, unless manually focused by Tab. -
@Axel-Spoerl
You're right, it must be related to the pointer events, but in a way that I cannot yet figure out. The thing is that thisQWidget
which doesn't get focus is acting as a host to a native widget with its own window procedure. That procedure wants to processWM_POINTERDOWN
and does get it! Despite the host widget not getting focus. And the same procedure also wants to processWM_KEYDOWN
and doesn't get it. The host widget doesn't get key events, too, unless manually focused by Tab.@Violet-Giraffe
...then all the fellow widgets are proven innocent and the issue is in that custom widget implementation. Please share the code or mark this post solved. -
@Violet-Giraffe
...then all the fellow widgets are proven innocent and the issue is in that custom widget implementation. Please share the code or mark this post solved.Yes, you're right that Qt is not to blame, but could you please help me figure out how to solve the problem? The host widget looks like this:
template <class WindowT> class WindowContainer : public QWidget, public WindowT { public: WindowContainer(QWidget * parent) : QWidget{parent} { winId(); /// ensure native window is created setFocusPolicy(Qt::StrongFocus); } protected: bool nativeEvent(const QByteArray & eventType, void * message, long * result) override { const auto msg = static_cast<MSG *>(message); if (msg->message == WM_SHOWWINDOW) { if (msg->wParam == TRUE) { RECT rc; ::GetClientRect(msg->hwnd, &rc); ::SetWindowPos(WindowT::hWnd, HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOZORDER); } else { ::SetWindowPos(WindowT::hWnd, HWND_TOP, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOZORDER); } } return QWidget::nativeEvent(eventType, message, result); } };
If I remove the
WM_SHOWWINDOW
handling from thenativeEvent()
, then the host widget becomes focusable and gets key events. But the native window (WindowT::hWnd
) isn't displayed. Do you understand what exactly is happening?Specifically, I do not understand why the native windows gets
WM_POINTERDOWN
but doesn't getWM_KEYDOWN
.It seems that I need to manually set focus to the host widget upon receiving
WM_POINTERDOWN
, but why? That feels like a hack, not a solution. -
Yes, you're right that Qt is not to blame, but could you please help me figure out how to solve the problem? The host widget looks like this:
template <class WindowT> class WindowContainer : public QWidget, public WindowT { public: WindowContainer(QWidget * parent) : QWidget{parent} { winId(); /// ensure native window is created setFocusPolicy(Qt::StrongFocus); } protected: bool nativeEvent(const QByteArray & eventType, void * message, long * result) override { const auto msg = static_cast<MSG *>(message); if (msg->message == WM_SHOWWINDOW) { if (msg->wParam == TRUE) { RECT rc; ::GetClientRect(msg->hwnd, &rc); ::SetWindowPos(WindowT::hWnd, HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOZORDER); } else { ::SetWindowPos(WindowT::hWnd, HWND_TOP, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOZORDER); } } return QWidget::nativeEvent(eventType, message, result); } };
If I remove the
WM_SHOWWINDOW
handling from thenativeEvent()
, then the host widget becomes focusable and gets key events. But the native window (WindowT::hWnd
) isn't displayed. Do you understand what exactly is happening?Specifically, I do not understand why the native windows gets
WM_POINTERDOWN
but doesn't getWM_KEYDOWN
.It seems that I need to manually set focus to the host widget upon receiving
WM_POINTERDOWN
, but why? That feels like a hack, not a solution.P. S. This hack works to fix focus, and the host widget starts getting
keyPressEvent()
. But itsnativeEvent()
is not getting anything when keys are pressed. And neither does the underlying native window.
??? -
P. S. This hack works to fix focus, and the host widget starts getting
keyPressEvent()
. But itsnativeEvent()
is not getting anything when keys are pressed. And neither does the underlying native window.
???@Violet-Giraffe
Oh, didn't know that the code intercepts native events.The native WM doesn't know about Qt's focus chain. Therefore, WM_POINTERDOWN is easier delivered to the right window because it can be derived from the pointer position. WM_KEYDOWN can get delivered to the wrong window, when the native WM doesn't know about the right focus window. Even though MS-Windows is pretty synchronous and reliable, there are chances that event orders get mixed up and the native WM just makes a wrong assumption about where to deliver key events. Eventually, the wrong
nativeEvent
handler is called.A potential (and fairly solid) workaround is to override
event
in the custom widget, intercept thefocusAboutToChange
andfocusIn
events, to forcefully propagate focus to the native window. Since that stuff heavily depends on the screen layout, I can't promise that it works. But if it works, it can reliably fix the problem. Code could look as follows:bool customWidget::event(QEvent* event) { switch (event->type()) { case QEvent::FocusIn: case QEvent::FocusAboutToChange: if (hasFocus() && ::GetFocus() != reinterpret_cast<HWND>(winId())) { QWidget* nativeParent = this; while (nativeParent) { if (nativeParent->isWindow()) break; nativeParent = nativeParent->nativeParentWidget(); } if (nativeParent && nativeParent != this && ::GetFocus() == reinterpret_cast<HWND>(nativeParent->winId())) { ::SetFocus(reinterpret_cast<HWND>(winId())); } break; } default: break; } return QWidget::event(event); }
-
@Violet-Giraffe
Oh, didn't know that the code intercepts native events.The native WM doesn't know about Qt's focus chain. Therefore, WM_POINTERDOWN is easier delivered to the right window because it can be derived from the pointer position. WM_KEYDOWN can get delivered to the wrong window, when the native WM doesn't know about the right focus window. Even though MS-Windows is pretty synchronous and reliable, there are chances that event orders get mixed up and the native WM just makes a wrong assumption about where to deliver key events. Eventually, the wrong
nativeEvent
handler is called.A potential (and fairly solid) workaround is to override
event
in the custom widget, intercept thefocusAboutToChange
andfocusIn
events, to forcefully propagate focus to the native window. Since that stuff heavily depends on the screen layout, I can't promise that it works. But if it works, it can reliably fix the problem. Code could look as follows:bool customWidget::event(QEvent* event) { switch (event->type()) { case QEvent::FocusIn: case QEvent::FocusAboutToChange: if (hasFocus() && ::GetFocus() != reinterpret_cast<HWND>(winId())) { QWidget* nativeParent = this; while (nativeParent) { if (nativeParent->isWindow()) break; nativeParent = nativeParent->nativeParentWidget(); } if (nativeParent && nativeParent != this && ::GetFocus() == reinterpret_cast<HWND>(nativeParent->winId())) { ::SetFocus(reinterpret_cast<HWND>(winId())); } break; } default: break; } return QWidget::event(event); }
@Axel-Spoerl, thank you!
So basically, there is no way to figure out why the native window doesn't want to receive focus? And no way to figure out why theQWidget
host doesn't want to receive focus on mouse click either? These are probably two different issues, because solving the latter doesn't fix the former. -
@Axel-Spoerl, thank you!
So basically, there is no way to figure out why the native window doesn't want to receive focus? And no way to figure out why theQWidget
host doesn't want to receive focus on mouse click either? These are probably two different issues, because solving the latter doesn't fix the former.@Violet-Giraffe
What I love (under more) about C++: Where there is smoke, there is fire.
If the research takes a little longer, the answer is probably 42 ;-)But to isolate the problem, we'd need to see and understand the full picture, rather than a few snippets of code.
What is the purpose of the custom widget, how is it implemented and how does it interact with the other widgets?
Are eventFilters and event overrides in place? Are any of the parent widgets heavy consumers of mouse and key events?
What's the reason and the purpose for descending into the rabbit hole of native events? What's happening there, that cannot be seen or done from the Qt event loop? Just the simple coincidence that (a) native events are intercepted and (b) a custom widget doesn't get certain events delivered, would make me suspicious. And please forgive my bluntness: Disclosing the native adventure only after eliminating other paths, is even more food for doubt. -
-
@Violet-Giraffe said in Is there a way to find out which widget consumed an event?:
this widget calls winId() to become a native window
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.my custom widget cannot get focus by clicking the mouse
Have you overridden
QWidget::event()
?
If so, does the overridereturn QWidget::event(event)
when a mouse event is delivered (and hopefully remains unconsumed)?
What doesqDebug() << myCustomWidget->testAttribute(Qt::WA_TransparentForMouseEvents)
say?Hi,
@Axel-Spoerl said in Is there a way to find out which widget consumed an event?:
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.Aren't you contradicting point 4 of Native Widgets VS Alien Widgets in QWidget's documentation ?
-
Hi,
@Axel-Spoerl said in Is there a way to find out which widget consumed an event?:
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.Aren't you contradicting point 4 of Native Widgets VS Alien Widgets in QWidget's documentation ?
@SGaist
You are actually right. I was referring to the properties of a QWidget, not to what happens on the WM side. At the point when I wrote the comment, I was unaware that there is native stuff involved. But the extended context shows my statement was imprecise. Thanks for bringing that up. -
Hi,
@Axel-Spoerl said in Is there a way to find out which widget consumed an event?:
Just for the records:
QWidget::winId()
is a const method that just returns the native window ID (if the platform supports it). The call doesn't change anything on the widget and it doesn't "become" anything else.Aren't you contradicting point 4 of Native Widgets VS Alien Widgets in QWidget's documentation ?
@SGaist said in Is there a way to find out which widget consumed an event?:
Aren't you contradicting point 4 of Native Widgets VS Alien Widgets in QWidget's documentation ?
Thanks for the clarification, that's exactly what I meant. And after Axel's response I started thinking maybe that is no longer the case in new versions of Qt or something, so it's great we cleared that up