Catching drag cancel event in Qt - c++

I'm experiencing some difficulties with catching application-scope events in Qt.
My purpose is to catch every user action in some GUI application. Of course I've overrided QApplication::notify handler and processing different types of application-scope events there. Something like that:
bool CoolApplication::notify(QObject *obj, QEvent *e)
{
switch (e->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
// Handle mouse
break;
case QEvent::Wheel:
// Handle wheel
break;
case QEvent::KeyPress:
case QEvent::KeyRelease:
// Handle keyboard and so on
break;
default:
break;
}
return QApplication::notify(obj, e);
}
That works pretty well until user starts drag-and-drop operation. Mouse press event is handled well, it is catched in my handler, life is beautiful there. The problems start when I try to catch mouse release event on Windows.
When entering drag operation mouse and keyboard events are no longer handled by QApplication::notify. It is possible to catch drop event when it was successfull (I can add case QEvent::Drop branch into my handler), but how to catch ignored drop or canceling drop with Esc? It seems impossible to be done directly, maybe I can catch drag event loop termination event or something like that? Or even maybe someone knows a direct way to do it?
Edit:
Once more turning the attention that I need to catch application-scope event, not the widget one

Please, try this
bool QtCoreApplication::notify(QObject *obj, QEvent *e)
{
switch (e->type()) {
case QEvent::QDropEvent:
QDropEvent* dropEvent = (QDropEvent*)e;
dropEvent->keyboardModifiers(); // keys pressed
dropEvent->dropAction(); // Drop result
break;
return QApplication::notify(obj, e);
}

As this remained an open item to us, we checked the usage of the destroyed signal.
This is fired at the moment the element is discarded, which seems to happen a the moment the QDrag is killed by the Escape key pressure
void dragDestroyed()
{
qDebug("drag and drop cancled by Escape key");
}
QDrag *drag = new QDrag(this);
connect( drag, SIGNAL(destroyed()), this, SLOT(dragDestroyed()) );
drag->exec(Qt::MoveAction);
probably this is relevant for anyone facing the issue.

Related

QObjectPicker not receiving hovering QMouseEvent in Qt3DWidget

I've implemented a Qt3DWidget which works fairly well by letting Qt3D draw to an offscreen texture and using the texture's ID to draw on a quad in a QOpenGLWidget. The input source on the QInputSettings is set to this, i.e. the widget itself.
One issue that remains is that mouse hovering events (without clicking) are not properly processed and a QObjectPicker never fires its moved event when only hovering over an object. Clicking and dragging works on the other hand. For a couple of hours now I tried to track down where the event gets eaten - I'm sure this happens somewhere because clicking and moving emits the moved event. The latter indicates (in my view) that the event filter (a PickEventFilter - private Qt3D class) has been successfully installed. One failure case would have been that the event filter doesn't get installed.
Now I'm kind of stuck because it just seems impossible trying to figure out where the event dies. I've got the Qt debugging symbols and stepped through the code (which is sometimes a bit buggy because maybe due to code optimization). I figured out that the PickEventFilter gets the leaving event when moving the mouse by setting a breakpoint in this method:
bool PickEventFilter::eventFilter(QObject *obj, QEvent *e)
{
Q_UNUSED(obj);
switch (e->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove: {
QMutexLocker locker(&m_mutex);
m_pendingMouseEvents.push_back({obj, QMouseEvent(*static_cast<QMouseEvent *>(e))});
} break;
case QEvent::HoverMove: {
QMutexLocker locker(&m_mutex);
QHoverEvent *he = static_cast<QHoverEvent *>(e);
m_pendingMouseEvents.push_back({obj, QMouseEvent(QEvent::MouseMove,
he->pos(), Qt::NoButton, Qt::NoButton,
he->modifiers())});
} break;
case QEvent::KeyPress:
case QEvent::KeyRelease: {
QMutexLocker locker(&m_mutex);
m_pendingKeyEvents.push_back(QKeyEvent(*static_cast<QKeyEvent *>(e)));
}
default:
break;
}
return false;
}
And also on this line in QWidgetWindow:
QApplicationPrivate::sendMouseEvent(receiver, &translated, widget, m_widget,
&qt_button_down, qt_last_mouse_receiver);
When sendMouseEvent sends the moving event it never reaches the event filter. The only thing that arrives is a leave event. When you use createWindowContainer and Qt3DWindow the mouse events work. I really don't know what the difference is.
I don't think it's feasible to post code related to this issue but I'd hope that some of you can provide some ideas what to try out.
Thanks to Scheff in the comments I was able to make it work by setting setMouseTracking in the QWidget class to true. This was blocking the hovering events.

How to imitate mouse events with full functionality in Qt

I am trying to remote control a Qt application over UDP messages from my host computer.
Currently, the remote controlled computer runs a Qt application that receives messages and creates Mouse Events. Host computer sends type, button, modifier and position (between 0 and 1) whenever there is a new Mouse Event. I am trying to create events with these values in remote controlled computer and imitate them just like they are created by a user.
void MainWindow::slotNewMouseEvent(MouseMsg msg)
{
auto point = scalePoint(msg.x, msg.y); // basic conversion for coordinates
switch(msg.type)
{
case QEvent::MouseButtonPress:
QTest::mousePress(this->childAt(point), (Qt::MouseButton) msg.button,
(Qt::KeyboardModifiers) msg.modifier);
break;
case QEvent::MouseButtonRelease:
QTest::mouseRelease(this->childAt(point), (Qt::MouseButton) msg.button,
(Qt::KeyboardModifiers) msg.modifier);
break;
case QEvent::MouseButtonDblClick:
QTest::mouseDClick(this->childAt(point), (Qt::MouseButton) msg.button,
(Qt::KeyboardModifiers) msg.modifier);
break;
case QEvent::MouseMove:
QTest::mouseMove(this->childAt(point), point);
break;
default:
break;
}
}
void MainWindow::slotNewMouseEvent(MouseMsg msg)
{
auto point = scalePoint(msg.x, msg.y); // basic conversion for coordinates
QMouseEvent *evt = new QMouseEvent((QEvent::Type) msg.type,
point,
(Qt::MouseButton) msg.button,
(Qt::MouseButtons) msg.buttons,
(Qt::KeyboardModifiers) msg.modifier);
qApp->postEvent(this->childAt(point), evt);
// I was expecting funcionalities like this one to be done automatically
if(evt->type() == QEvent::MouseButtonDblClick || evt->type() == QEvent::MouseButtonPress)
{
this->childAt(point)->setFocus();
}
qApp->processEvents();
}
Both of these functions gives almost same results which is not desired. I was expecting sending newly created events to the top widget on screen would work. However, some functionalities like setting focus on clicked element, opening context menu by right click or selecting elements of a combobox (elements window is not a widget) does not work.
Is there a solution with keeping those events in remote controlled computer's Qt application? I would prefer not using system libraries like "windows.h" to prevent harms to the system and making my application cross-platform. Do I have to use them?

QKeyEvent isAutoRepeat not working?

So, I have an application where if a particular button is kept pressed it plays an audio device, when the button is released it stops the audio device. I use keyPressEvent and KeyReleaseEvent to implement this which is similar to the code below:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if(event->isAutoRepeat())
{
event->ignore();
}
else
{
if(event->key() == Qt::Key_0)
{
qDebug()<<"key_0 pressed"<<endl;
}
else
{
QWidget::keyPressEvent(event);
}
}
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
if(event->isAutoRepeat())
{
event->ignore();
}
else
{
if(event->key() == Qt::Key_0)
{
qDebug()<<"key_0 released"<<endl;
}
else
{
QWidget::keyReleaseEvent(event);
}
}
}
But apparently isAutoRepeat function isn't working as I can see continuous print out of key_0 pressed and key_0 released despite the fact I haven't released the 0 key after I have pressed it. Is my code wrong or something else is wrong?
Thanks.
EDIT
I think this is happening because the MainWindow loses the keyboard focus. How can I actually find out which widget has the focus? I'm actually using some widgets when Qt::Key_0 pressed, but I thought I set all those possible widgets to Qt::NoFocus, I guess it's not working.
I'm trying to know which widget has the focus by doing the following:
QWidget * wigdet = QApplication::activeWindow();
qDebug()<<wigdet->accessibleName()<<endl;
but it always prints an empty string. How can I make it print the name of the widget which has the keyboard focus?
So as I also stumbled over this issue (and grabKeyboard didn't really help), I begun digging in qtbase. It is connected to X11 via xcb, and by default, in case of repeated keys, X11 sends for each repeated key a release-event immediately followed by a key-press-event. So holding down a key results in a sequence of XCB_BUTTON_RELEASE/XCB_BUTTON_PRESS-events beeing sent to the client (try it out with xev or the source at the end of this page).
Then, qt (qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp) tries to figure out from these events whether its an autorepeat case: when a release is received, it uses a lookahead feature to figure if its followed by a press (with timestamps close enough), and if so it assumes autorepeat.
This does not always work, at least not on all platforms. For my case (old and outworn slow laptop (Intel® Celeron(R) CPU N2830 # 2.16GHz × 2) running ubuntu 16.04), it helped to just put a usleep (500) before that check, allowing the press event following the release event to arrive... it's around line 1525 of qxcbkeyboard.cpp:
// look ahead for auto-repeat
KeyChecker checker(source->xcb_window(), code, time, state);
usleep(500); // Added, 100 is to small, 200 is ok (for me)
xcb_generic_event_t *event = connection()->checkEvent(checker);
if (event) {
...
Filed this as QTBUG-57335.
Nb: The behaviour of X can be changed by using
Display *dpy=...;
Bool result;
XkbSetDetectableAutoRepeat (dpy, true, &result);
Then it wont send this release-press-sequences in case of a hold down key, but using it would require more changes to the autorepeat-detection-logic.
Anyway solved it.
The problem was that I have a widget which is a subclass of QGLWidget which I use to show some augmented reality images from Kinect. This widget takes over the keyboard focus whenever a keyboard button is pressed.
To solve this problem, I needed to call grabKeyboard function from the MainWindow class (MainWindow is a subclass of QMainWindow), so this->grabKeyboard() is the line I needed to add when key_0 button is pressed so that MainWindow doesn't lose the keyboard focus, and then when the key is released I needed to add the line this->releaseKeyboard() to resume normal behaviour, that is, other widgets can have the keyboard focus.

QMoveEvent continuously emitted

I am using the same Qt program (v5.2.1) on both windows 7 and Ubuntu. In both I have a QMainWindow that overrides the event function like so:
bool MyMainWindow::event(QEvent *event) {
switch(event->type()) {
case QEvent::Move:
// Do stuff
break;
}
return QWidget::event(event);
}
In there I look for the QEvent::Move to do some processing. On windows, if I click and drag the window around the event seems to happen continuously. However, on Ubuntu it only happens after the mouse is released. I can't seem to find this in any documentation and would appreciate some clarification.

Why does QEvent::ShortcutOverride event occur?

I have a QMainWindow with an event filter installed.
After I open and close a QDialog the keyboard arrow keys don't respond since QMainWindow receives only ShortcutOverride events instead of KeyPress events.
When I changed the QMainWindow's event filter to handle ShortcutOverride events I got a strange behavior since in my program each key press event is preceded by two shortcut override events (why??).
This doesn't work - events are handled more than once:
bool eventFilter(QObject *, QEvent *event) {
if(type == QEvent::KeyPress || type == QEvent::ShortcutOverride) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
switch(keyEvent->key()) {
case Qt::Key_Up:
case Qt::Key_Down:
case Qt::Key_Left:
case Qt::Key_Right:
// Do something here
break;
default:
break;
}
return true;
}
return false;
}
Why is this happening? Where do those ShortcutOverride events come from?
Edit:
As it turns out, QMainwindow loses focus when the QDialog is opened and never gains it back when the QDialog is closed.
I used setFocus() in the eventFilter of QMainwindow and it seems to do the trick.
Lost focus is exactly the reason for not getting any keyboard events after a dialog opens up. The solution you suggested is probably the best fix for this issue.
As for ShortcutOverride events, they come for every keypress because any key or combination of keys can be registered as a shortcut. This event gives you an option to cancel shortcut handling, if needed. It has to be processed at the earliest possible moment when the engine doesn't know yet if the pressed key is going to be used as a shortcut or not. Thus the event is generated for all key presses "just in case". You may read more here: https://wiki.qt.io/ShortcutOverride
It is happens when a key press in child is made. It is used to for overriding shortcut key handling (QKeyEvent).
refer http://qt-project.org/doc/qt-4.8/qevent.html#Type-enum