Track Cursor Events With Window Click Through
-
wrote on 22 Dec 2023, 07:10 last edited by HOSHI
Hello. I am currently trying to capture cursor events so that I can draw some sort of cursor trail. I also want it disabled under specific conditions, such as when the mouse shape is currently precision (the + shaped cursor). To do this, I need to have a full screen, completely transparent window on top of everything so that I can capture events. At the same time, it needs to be click-through because otherwise the program would be simply useless. While I can achieve these two independently, but I cannot do them both at the same time.
The code is as follows:MainOverlay::MainOverlay(QWidget *parent): QMainWindow(parent), ui(new Ui::MainOverlay){ ui->setupUi(this); // this->setMouseTracking(true); this->installEventFilter(this); this->setAttribute(Qt::WA_TranslucentBackground); // this->setAttribute(Qt::WA_AlwaysStackOnTop, true); this->setAttribute(Qt::WA_TransparentForMouseEvents); //10th line this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); this->showFullScreen(); } bool MainOverlay::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::HoverMove) { auto cur = this->cursor(); auto str = QString("%1, %2 (%3)").arg(cur.pos().x()).arg(cur.pos().y()).arg(cur.shape()); qInfo() << str; } return false; } MainOverlay::~MainOverlay(){ delete ui; }
If I comment out the 10th line, I can no longer click through the window, but the output successfully prints out the location of the cursor. If I don't comment it out, then the window becomes click-through but the program no longer receives cursor events.
What can I do to do both?
Another semi-related question: I want this window to be an overlay that does not interfere with the user much. Currently, the window is shown in the taskbar, and is in whatever the alt-tab window is called. How can I hide the window from there? I would like to have the program to function like an overlay.
-
Hello. I am currently trying to capture cursor events so that I can draw some sort of cursor trail. I also want it disabled under specific conditions, such as when the mouse shape is currently precision (the + shaped cursor). To do this, I need to have a full screen, completely transparent window on top of everything so that I can capture events. At the same time, it needs to be click-through because otherwise the program would be simply useless. While I can achieve these two independently, but I cannot do them both at the same time.
The code is as follows:MainOverlay::MainOverlay(QWidget *parent): QMainWindow(parent), ui(new Ui::MainOverlay){ ui->setupUi(this); // this->setMouseTracking(true); this->installEventFilter(this); this->setAttribute(Qt::WA_TranslucentBackground); // this->setAttribute(Qt::WA_AlwaysStackOnTop, true); this->setAttribute(Qt::WA_TransparentForMouseEvents); //10th line this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); this->showFullScreen(); } bool MainOverlay::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::HoverMove) { auto cur = this->cursor(); auto str = QString("%1, %2 (%3)").arg(cur.pos().x()).arg(cur.pos().y()).arg(cur.shape()); qInfo() << str; } return false; } MainOverlay::~MainOverlay(){ delete ui; }
If I comment out the 10th line, I can no longer click through the window, but the output successfully prints out the location of the cursor. If I don't comment it out, then the window becomes click-through but the program no longer receives cursor events.
What can I do to do both?
Another semi-related question: I want this window to be an overlay that does not interfere with the user much. Currently, the window is shown in the taskbar, and is in whatever the alt-tab window is called. How can I hide the window from there? I would like to have the program to function like an overlay.
@HOSHI
No matter how often I count, your 10th line is number 7 for me. But that's my fault, I am sure :-)This line is making the widget unresponsive to mouse events. I am not sure, if this is what you want. I am also a bit confused about "cursor events". I guess you mean mouse events.
What also comes to mind, is that the event filter always returns
false
.
That tells the event handler, that the event has not been processed and needs to be delivered to the widget'sevent
override.
I don't know exactly what is meant bythen the window becomes click-through
If you mean that the widget reacts to the mouse events, you can avoid it by returning
true
if your event filter has processed theQEvent::HoverMove
. -
@HOSHI
No matter how often I count, your 10th line is number 7 for me. But that's my fault, I am sure :-)This line is making the widget unresponsive to mouse events. I am not sure, if this is what you want. I am also a bit confused about "cursor events". I guess you mean mouse events.
What also comes to mind, is that the event filter always returns
false
.
That tells the event handler, that the event has not been processed and needs to be delivered to the widget'sevent
override.
I don't know exactly what is meant bythen the window becomes click-through
If you mean that the widget reacts to the mouse events, you can avoid it by returning
true
if your event filter has processed theQEvent::HoverMove
.wrote on 22 Dec 2023, 07:36 last edited by HOSHI@Axel-Spoerl
Yeah, it would be line 7 in the post, and 10th line in the IDE.
To clarify, this is what I am trying to make for now:- If I hover my mouse around, I expect the coordinate to be printed in the application output.
- If I click on any part of the screen, it works as if the window is not there. This is what I mean by click-through. For example, if I click on this while the program is running:
I expect the screen to turn into this.
Note that this is just an example; I expect this "click-through" to work with any program.
Currently, when
this->setAttribute(Qt::WA_TransparentForMouseEvents);
is commented out, number 1 works, but number 2 doesn't. The coordinates are printed into the output, but clicks have no effect. -
@Axel-Spoerl
Yeah, it would be line 7 in the post, and 10th line in the IDE.
To clarify, this is what I am trying to make for now:- If I hover my mouse around, I expect the coordinate to be printed in the application output.
- If I click on any part of the screen, it works as if the window is not there. This is what I mean by click-through. For example, if I click on this while the program is running:
I expect the screen to turn into this.
Note that this is just an example; I expect this "click-through" to work with any program.
Currently, when
this->setAttribute(Qt::WA_TransparentForMouseEvents);
is commented out, number 1 works, but number 2 doesn't. The coordinates are printed into the output, but clicks have no effect.@HOSHI
If the intention is to print the mouse coordinates no matter what, I would not rely on hover events or specific widgets here.
I'd install an event filter onqApp
(the application) and interceptQEvent::MouseMove
. You can print the coordinates in your window and make it transparent for mouse events. -
@HOSHI
If the intention is to print the mouse coordinates no matter what, I would not rely on hover events or specific widgets here.
I'd install an event filter onqApp
(the application) and interceptQEvent::MouseMove
. You can print the coordinates in your window and make it transparent for mouse events.wrote on 23 Dec 2023, 12:45 last edited by HOSHI@Axel-Spoerl Do you mean something like this?
#include "widget.h" #include "./ui_widget.h" #include <QLabel> Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget){ ui->setupUi(this); this->setMouseTracking(true); qApp->installEventFilter(this); QLabel *label = new QLabel(this); label->setText("Window is open!"); this->setAttribute(Qt::WA_TranslucentBackground); this->setAttribute(Qt::WA_TransparentForMouseEvents); //Line 12 this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); this->showFullScreen(); } bool Widget::eventFilter(QObject *obj, QEvent *event) { if(!event->isPointerEvent()) return false; if (event->type() == QEvent::MouseMove) { auto cur = this->cursor(); auto str = QString("%1, %2 (%3)").arg(cur.pos().x()).arg(cur.pos().y()).arg(cur.shape()); qInfo() << str; } return true; } Widget::~Widget() { delete ui; }
When line 12 is commented out, the application output correctly prints the coordinate of the cursor as well as its current shape, but I cannot interact with anything underneath the invisible overlay. If it is not commented out, I can interact with anything underneath the overlay, but the
eventFilter
is not triggered. I do expectQt::WA_TransparentForMouseEvents
to makeeventFilter
to not trigger, but I would like to know if there is a way around that, and read the current mouse location and shape whilst allowing me to interact with anything underneath the overlay. -
@Axel-Spoerl Do you mean something like this?
#include "widget.h" #include "./ui_widget.h" #include <QLabel> Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget){ ui->setupUi(this); this->setMouseTracking(true); qApp->installEventFilter(this); QLabel *label = new QLabel(this); label->setText("Window is open!"); this->setAttribute(Qt::WA_TranslucentBackground); this->setAttribute(Qt::WA_TransparentForMouseEvents); //Line 12 this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); this->showFullScreen(); } bool Widget::eventFilter(QObject *obj, QEvent *event) { if(!event->isPointerEvent()) return false; if (event->type() == QEvent::MouseMove) { auto cur = this->cursor(); auto str = QString("%1, %2 (%3)").arg(cur.pos().x()).arg(cur.pos().y()).arg(cur.shape()); qInfo() << str; } return true; } Widget::~Widget() { delete ui; }
When line 12 is commented out, the application output correctly prints the coordinate of the cursor as well as its current shape, but I cannot interact with anything underneath the invisible overlay. If it is not commented out, I can interact with anything underneath the overlay, but the
eventFilter
is not triggered. I do expectQt::WA_TransparentForMouseEvents
to makeeventFilter
to not trigger, but I would like to know if there is a way around that, and read the current mouse location and shape whilst allowing me to interact with anything underneath the overlay.@HOSHI
Can you please specify, what exactly is your expected behavior?
I am a bit confused about what is the use case here.
Do you only want to write the mouse position to the application output, or print it somewhere on the screen?Event filter is installed correctly. Making the widget transparent for mouse events or not, has no influence on the event filter installed on application level. Returning
true
after printing the coordinates tells the event handler, that the event is consumed. It will not be delivered elsewhere. -
@HOSHI
Can you please specify, what exactly is your expected behavior?
I am a bit confused about what is the use case here.
Do you only want to write the mouse position to the application output, or print it somewhere on the screen?Event filter is installed correctly. Making the widget transparent for mouse events or not, has no influence on the event filter installed on application level. Returning
true
after printing the coordinates tells the event handler, that the event is consumed. It will not be delivered elsewhere.wrote on 23 Dec 2023, 19:44 last edited by HOSHI@Axel-Spoerl
Okay, I originally mentioned cursor trail in OP, but I should simplify the use case for the sake of communication. Say, I need a cursor highlighter. All it does is highlight where my cursor is at, but with one extra feature (number 3). I expect the program to do the following:- A circle is drawn around the hotspot of the cursor. It follows it around. If I move my cursor sideways, the circle underneath it follows it. It's a cursor highlighter, after all. This must happen on any part of the screen. The reason I need the coordinates of the mouse is to draw the circle.
- The overlay itself must not interfere with the normal use. In other words, the overlay must not block clicking, dragging, scroll, as if the overlay is not there.
- The highlight disappears when the cursor turns into precision cursor for any reason. Again, this must happen on any part of the screen.
You've probably seen those crappy Bandicam recorded videos, where the cursor is highlighted with yellow, and when the user clicks on any part of the screen, the yellow circle does not interfere with any interaction. If you haven't, here's an example: https://www.youtube.com/watch?v=wpg1YmuxNv8
The reason I'm keep mentioning requirement number 2 is because I am having trouble achieving that one in particular. If I remove the line
this->setAttribute(Qt::WA_TransparentForMouseEvents);
, then I can achieve number 2 because the overlay no longer interferes with the normal use. However, the overlay stops receiving any mouse events, and I cannot achieve number 1 and 3. If I add the linethis->setAttribute(Qt::WA_TransparentForMouseEvents);
back into the code, the program receives mouse events and I can achieve 1 and 3, but the overlay blocks all clicking, and the requirement number 2 is no longer met. Making the event filter to return false did not help either.
I need the overlay to be both always on top and full screen so that it can receive all cursor events and draw highlight regardless of the mouse location.I hope this is clear enough.
- A circle is drawn around the hotspot of the cursor. It follows it around. If I move my cursor sideways, the circle underneath it follows it. It's a cursor highlighter, after all. This must happen on any part of the screen. The reason I need the coordinates of the mouse is to draw the circle.
-
@Axel-Spoerl
Okay, I originally mentioned cursor trail in OP, but I should simplify the use case for the sake of communication. Say, I need a cursor highlighter. All it does is highlight where my cursor is at, but with one extra feature (number 3). I expect the program to do the following:- A circle is drawn around the hotspot of the cursor. It follows it around. If I move my cursor sideways, the circle underneath it follows it. It's a cursor highlighter, after all. This must happen on any part of the screen. The reason I need the coordinates of the mouse is to draw the circle.
- The overlay itself must not interfere with the normal use. In other words, the overlay must not block clicking, dragging, scroll, as if the overlay is not there.
- The highlight disappears when the cursor turns into precision cursor for any reason. Again, this must happen on any part of the screen.
You've probably seen those crappy Bandicam recorded videos, where the cursor is highlighted with yellow, and when the user clicks on any part of the screen, the yellow circle does not interfere with any interaction. If you haven't, here's an example: https://www.youtube.com/watch?v=wpg1YmuxNv8
The reason I'm keep mentioning requirement number 2 is because I am having trouble achieving that one in particular. If I remove the line
this->setAttribute(Qt::WA_TransparentForMouseEvents);
, then I can achieve number 2 because the overlay no longer interferes with the normal use. However, the overlay stops receiving any mouse events, and I cannot achieve number 1 and 3. If I add the linethis->setAttribute(Qt::WA_TransparentForMouseEvents);
back into the code, the program receives mouse events and I can achieve 1 and 3, but the overlay blocks all clicking, and the requirement number 2 is no longer met. Making the event filter to return false did not help either.
I need the overlay to be both always on top and full screen so that it can receive all cursor events and draw highlight regardless of the mouse location.I hope this is clear enough.
@HOSHI
Okay...communication is key... I may be slow in understanding things, but I don't see how this is connected to "Track Cursor Events With Window Click Through".
Let me think about this use case a bit, I'll come back on it. - A circle is drawn around the hotspot of the cursor. It follows it around. If I move my cursor sideways, the circle underneath it follows it. It's a cursor highlighter, after all. This must happen on any part of the screen. The reason I need the coordinates of the mouse is to draw the circle.
-
@HOSHI
Okay...communication is key... I may be slow in understanding things, but I don't see how this is connected to "Track Cursor Events With Window Click Through".
Let me think about this use case a bit, I'll come back on it.wrote on 23 Dec 2023, 20:01 last edited by HOSHI@Axel-Spoerl
The event is primarily for the 3rd requirement where the highlight disappears when the cursor shape changes to precision shape (known asQt::CrossCursor
). I am planning on getting it usingshape()
to get something from this list: https://doc.qt.io/qt-6/qt.html#CursorShape-enum
Additionally, I aim to add a number of features, including but not limited to:- Ripple on click
- Different colour ripple for right click, middle click, etc
I did not mention them in OP because I originally did not expect getting the event to be complicated.
I would love to know if there are any other ways to get them though. -
@Axel-Spoerl
The event is primarily for the 3rd requirement where the highlight disappears when the cursor shape changes to precision shape (known asQt::CrossCursor
). I am planning on getting it usingshape()
to get something from this list: https://doc.qt.io/qt-6/qt.html#CursorShape-enum
Additionally, I aim to add a number of features, including but not limited to:- Ripple on click
- Different colour ripple for right click, middle click, etc
I did not mention them in OP because I originally did not expect getting the event to be complicated.
I would love to know if there are any other ways to get them though.@HOSHI
The architecture seems off and overly complicated to me:- You implement a class inheriting from
QWidget
, which looks like an application's main window. - This class' mouse events are intercepted to eventually move a highlighter with the cursor.
- We don't know anything about this highlighter, so I assume it's just a normal cursor, surrounded by a halo.
=> Why don't you write your own cursor(s), inheriting from
QCursor
and usesetOverrideCursor()
?
1/10