Qt - top level widget with keyboard and mouse event transparency? - qt

I want an app's main window to ignore mouse and keyboard events, passing them to applications underneath it in the window manager Z-order.
I see how to make child widgets ignore keyboard or mouse events, but how about the main window?
I'm trying to make a desktop widget that always sits just over the background and is totally invisible to keyboard and mouse events. (Pass through)
Qt::X11BypassWindowManagerHint gets me keyboard pass through (although sadly X11 specific, but fine for now), so how about mouse events?
Is there a OS-agnostic way to be transparent to keyboard events?
EDIT:
The key word here is transparency.
I don't want to EAT mouse and keyboard events, I want the window manager to know I don't want them at all. Those events should be directed to whatever application is under me in the zorder.
For example, I want to be able to click on desktop icons that are covered by my widget and interact with them as if the widget was not there.

I found the following solution (tested on Linux, also works on Windows according to #TheSHEEEP):
setWindowFlags(windowFlags() | Qt::WindowTransparentForInput);
It has been added in more recent qt release (i did not find when)
see http://doc.qt.io/qt-5/qt.html

On Windows you can set WS_EX_TRANSPARENT
To do this in Qt use the following code:
Include the header,
#if _WIN32
#include <windows.h>
#endif
and put the following code into the constructor.
#if _WIN32
HWND hwnd = (HWND) winId();
LONG styles = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, styles | WS_EX_TRANSPARENT);
#endif

Maybe what you want is
widget->setAttribute(Qt::WA_TransparentForMouseEvents)
? That's what QRubberBand uses to let it's parent handle the mouse events. As for keyboard events, a QWidget doesn't get any keyboard events unless it has set itself a focusPolicy().
setFocusPolicy( Qt::NoFocus );
should therefore take care of the keyboard events.

Use Qt's event filters: they will allow your application to eat whichever events you specify (i.e. keyboard and mouse events) but still process other events such as paint events.
bool FilterObject::eventFilter(QObject* object, QEvent* event)
{
QKeyEvent* pKeyEvent = qobject_cast<QKeyEvent*>(event);
QMouseEvent* pMouseEvent = qobject_cast<QMouseEvent*>(event);
if (pKeyEvent || pMouseEvent)
{
// eat all keyboard and mouse events
return true;
}
return FilterObjectParent::eventFilter(object, event);
}

Maybe I'm missing something here, but have you tried subclassing the QMainWindow class and overriding the QWidget::event() method to always return false? If you need to handle some events, you could add that intelligence here as well.
This technique should allow you to inspect the events coming in to the application and ignore them if desired without having to eat them using an event filter.
If this doesn't work you could attempt to redirect the events to the desktop by calling QCoreApplication::notify() and passing the event to the desktop widget obtained by calling QApplication::desktop(). I have no idea if this would work, but it seemed like it might be worth giving a try.

I think that overriding is supposed to work:
bool YourMainWindow::event( QEvent *event )
{
event ->accept();
return true;
}
that's some of what the QWidget class documentation says about event() member function:
This function returns true if the
event was recognized, otherwise it
returns false. If the recognized event
was accepted (see QEvent::accepted),
any further processing such as event
propagation to the parent widget
stops.

Related

Onscreen Keyboard Touch Screen popup up with or without focus in Qt WASM

I am working on an application to be deployed as a wasm app and a windows application.
we are using a windows 10 OS touch screen tablet and google chrome to access the web app. am using an empty new qt project to demonstrate the problem :
The onscreen Keyboard popups up regardless of focus meaning it will pop up wherever i touch the screen:
if btn is pressed
if lineedit is selected
if empty widget space is touched even though there is no focus object behind it.
i include a link to this Behaviour Video so you can see the problem.
the onscreen Keyboard popup without focus problem occurs only if i compile for webassembly, works fine on the same tablet for MSVC.
what i tried :
catch the events then ignore them using :
ui->centralwidget->installEventFilter(this);
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
//print event to qdebug
static int eventEnumIndex = QEvent::staticMetaObject.indexOfEnumerator("Type");
QString TEXT_Event = QEvent::staticMetaObject.enumerator(eventEnumIndex).valueToKey(event->type());;
qDebug()<<"TEXT EVENT="<<TEXT_Event;
if(TEXT_Event.contains("Paint")){
//dont show print event
}else{
ui->Main_PlainTextEdit->appendPlainText(obj->objectName()+"=>"+TEXT_Event);
}
if( event->type()==QEvent::MouseButtonPress|| event->type()==QEvent::MouseButtonRelease)
{
// handle on-screen keyboard
event->ignore();
event->accept();
}
return true;
}
setAttribute(Qt::WA_TransparentForMouseEvents);
not OK as it deactivates all mouse input => no interaction possible,
setAttribute(Qt::WA_AcceptTouchEvents);
This only changes the event from Mouse event to touch event.
Maybe there is an option that i need to tick in the form editor or touchscreen option that needs to be activated, maybe the way to catch and ignore event i implemented is wrong.
I don't know what i am doing wrong but all my attempts to fix this didn't work, please help guide me ?
Thank you in advance.
Here is how I solved it in Qt 5.15. To prevent the keyboard from popping up, I edited the default index.html. Bu default, there is a line:
<canvas id="qtcanvas" oncontextmenu="event.preventDefault()" contenteditable="true"></canvas>
I added inputmode="none", meaning I changed it to:
<canvas id="qtcanvas" oncontextmenu="event.preventDefault()" contenteditable="true" inputmode="none"></canvas>
What this does, is it prevent the onscreen keyboard from popping up. But note that it will not popup even when a line edit is in focus, so one must use either a physical keyboard or a custom virtual keyboard widget if typing text is required in the UI.
Hopefully it will get better in future Qt versions, I see there is currently some fixes scheduled for Qt 6.4 https://bugreports.qt.io/browse/QTBUG-83064

SwipeView detect on release event QML - QT

I need to detect when the user release the finger from the screen.
I'm doing an application with SwipeView, but when the finger remove from the screen I need to detect the minimum slide also.
There is a method for this ? Or maybe if I detect when the finger leave the screen on the ApplicatioWindow.
Thanks.
In the very beginning of my QML learning I had a similar problem: I wanted to detect mouse events without interfering with the rest of the application.
It might not be the right solution, maybe it is very bad style or hacky but it works, and might help you.
The idea is to build a C++ item that I use somewhere as parent node to all nodes I want to spy on their mouse events. In this Item I hook in the childMouseEventFilter by reimplementing it as follows:
bool MouseEventListener::childMouseEventFilter(QQuickItem *item, QEvent *event)
{
emit mouseEventHappend();
event->ignore(); // Don't know if that is right. I think I should not have it here.
return QQuickItem::childMouseEventFilter(item, event);
}
In this solution I don't check what kind of mouse event I got, but you might, and emit different signals depending on it.
If used on a touch device, there will be two events you might be interested in:
QTouchEvent
QMouseEvent
Check the QEvent.type() to handle them appropriately. The interesting types are:
QEvent::MouseButtonPress
QEvent::MouseButtonRelease
QEvent::MouseMove
QEvent::TouchBegin
QEvent::TouchCancel
QEvent::TouchEnd
QEvent::TouchUpdate
More: http://doc.qt.io/qt-5/qevent.html#Type-enum
Especially the touch events offer nice information about the start of the gesture and the last leg of the finger movement, that might be of interest to you.
I guess that the best method for your application is to use a ListView with horizontal orientation and delegate is a "page" and now you can detect
ListView is inherited from Flickable and you can detect the mouse event now.
http://doc.qt.io/qt-5/qml-qtquick-listview.html

Is a QGraphicsSceneEvent::widget() always a QGraphicsView?

According to the docs, QGraphicsSceneEvent::widget() returns a QWidget. Isn't the widget always going to be a QGraphicsView, though (or null)?
I would assume so, except then I don't understand why the devs wouldn't have just made it QGraphicsSceneEvent::view().
The reason I'm asking is that in my subclassed QGraphicsScene, I have overridden QGraphicsScene::mousePressEvent() and I want to know which view originated the event--I'm wondering if it's safe to static cast widget() to a QGraphicsView, or if it's conceivable that some other widget could have created the event.
As it turns out, QGraphicsSceneEvent::widget() returns the viewport widget, not the QGraphicsView. If you want the QGraphicsView, you would need to use: event->widget()->parent().
Documentation page you refered mentions the QGraphicsView as a target of events:
When a QGraphicsView receives Qt mouse, keyboard, and drag and drop
events (QMouseEvent, QKeyEvent, QDragEvent, etc.), it translates them
into instances of QGraphicsSceneEvent subclasses and forwards them to
the QGraphicsScene it displays. The scene then forwards the events to
the relevant items.
However, if you doubt that QGraphicsSceneEvent::widget() always returns QGraphicsView, or you relay that it should always be a QGraphicsView you can check that by using qobject_cast:
QGraphicsView *view = qobject_cast<QGraphicsView *>(event->widget();
if (view) {
// Handle the event
} else {
// This is something that I do not expect.
// ..
}

Qt : keyboard focus

I am writing a program in Qt that looks like this:
The main window is my class Window : QWidget, it has a QGridLayout containing four other widgets (Input_Menu : QWidget and Output_Menu : QWidget, and then two Canvas : QWidget)
I would like to trigger certain events when the user strikes a key. The problem is, the Window sometimes loses focus (it goes, say to Input_Menu, or maybe a button in Input_Menu...)
I have tried the following solutions, but they seem unsatisfactory (and dirty):
Give Window the focus whenever it loses it.
Tell each widget who could have the focus to trigger Window's KeyPressEvent function (or a clone of it) whenever it receives a keyboard event.
Ideally, I would like that if a widget receives an event (say a keyboard event) and doesn't know what to do with it, it should automatically call its parent's event handler. I would have hoped this to be a default feature of Qt but it doesn't look like it. On the other hand I am really confused about the whole focus thing, I don't really get what's going on. Can someone explain this to me: I have included a std::cout << "key pressed" << std::endl; in my Window::KeyPressEvent function. When I first run my program, it seems the focus is on the top QComboBox in Input_Menu: if I hit the Up/Down keys, I navigate in that box and no "key pressed" is showed in my console. If I hit most letters, nothing happens. But if I hit Left/Right keys, I do get a "key pressed" in my console!?
Thanks a lot in advance for your insights.
You can install an event filter on QApplication to filter the relevant QEvent::KeyPress events globally. From the Qt documentation:
It is also possible to filter all events for the entire application,
by installing an event filter on the QApplication or QCoreApplication
object. Such global event filters are called before the
object-specific filters. This is very powerful, but it also slows down
event delivery of every single event in the entire application; the
other techniques discussed should generally be used instead.
Besides the performance considerations, remember to check if your window currently has the focus before you filter the key event, or you might break popup dialogs or input into other windows.
Actually, I found that for keys that are modifiers (such as Shift, Control), Qt supports finding out whether they are pressed.
Eg : if(QApplication::keyboardModifiers() == Qt::ShiftModifier) ...
This is good enough.

QPainter redraw on window gaining/losing focus

I am learning Qt and am trying to paint a simple display for my program with QPainter.
I draw static elements (frames etc.) once and only update dynamic elements afterwards.
Everything works fine, except for when the window loses focus. As soon as that happens, the whole area is cleared (dynamic elements keeps being painted as before).
Is it possible to prevent this behaviour? If not, how do I determine if the window have lost focus?
When your widget is uncovered, the paintEvent member will be called. The event passed in has a region() member that tells you what part of the widget should be redrawn. You can use that to redraw the static parts if/when necessary.
While I did not find why the screen was repainted, the focus can be triggered by using
eventFilter(QObject *, QEvent *event) {
if (event->type() == QEvent::ActivationChange){}
}
and paint function can be called from here. Although a slight delay must be added, as the trigger usually fires before the window looses focus (hence still clearing the repaint).

Resources