I have a Qt app with several window shortcuts defined in a Qt Designer form action. The shortcuts works well until they are pressed while the focus is on a widget that handles the same combination (overriding my window shortcut).
I would like to have the opposite behavior: window shortcuts overriding focused widget shortcuts.
I tried using eventFilter
and I can catch the desired events, but I'm not able to resend them in a way that the global shortcuts are called. I could use a big switch and call the actions myself, but of course, I would like to avoid that.
I used postEvent
and sendEvent
inside the eventFilter
using the MainWindow
as the receiver, but the events are ignored:
bool MainWindow::eventFilter(QObject*, QEvent* event) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Z
&& keyEvent->modifiers() == Qt::ControlModifier) {
//Calling the triggers directly works
ui->actionUndo->trigger();
return true;
} else if (keyEvent->modifiers().testFlag(
Qt::KeypadModifier)) {
QKeyEvent* ev2
= new QKeyEvent(keyEvent->type(), keyEvent->key(), 0);
qDebug() << keyEvent << ev2;
//This sendEvent doesn't work
QApplication::sendEvent(ui->widget, ev2);
event->accept();
return true;
} else {
return false;
}
}
return false;
}
As one of the solutions you can install QEvent::ShortcutOverride event filters:
For QEvent::ShortcutOverride the receiver needs to explicitly accept the event to trigger the override. Calling ignore() on a key event will propagate it to the parent widget. The event is propagated up the parent widget chain until a widget accepts it or an event filter consumes it.
That event would be called when some widget tries to override a shortcut event, e.g. just a simple example:
I have just a new Qt app with one lineEdit and window menu with Ctrl+V shortcut (overrides the paste shortcut in lineEdit).
Here how it works:
1.Create the filtering method that would ignore (return true
) the shortcut overrides (I've used MainWindow::eventFilter
in sample app, however you can use any filtering object you need or want). It might be better to follow Qt docs and use the accept()/ignore() as stated above, however on my app it worked just fine without them just returning true/false.
2.Install the event filter from p.1 to widget that should ignore shortcut action if overriding.
3.I've added the menu action with Ctrl+V shortcut in designer. In code below you would see the "Hello from window shortcut!"
- the menu action result, when trying to Paste (Ctrl+V) instead of actual lineEdit paste operation.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->lineEdit->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::ShortcutOverride) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
// Ignore only the Ctrl + V shortcut override, you can customize check for your needs
if (keyEvent->modifiers().testFlag(Qt::ControlModifier) && keyEvent->key() == 'V') {
qDebug() << "Ignoring" << keyEvent->modifiers() << "+" << (char)keyEvent->key() << "for" << watched;
event->ignore();
return true;
}
}
return QMainWindow::eventFilter(watched, event);
}
void MainWindow::on_action1_triggered()
{
qDebug() << "Hello from window shortcut!";
}
Sample debug output:
Ignoring QFlags(ControlModifier) + V for QLineEdit(0x575b10, name = "lineEdit")
Hello from window shortcut!
Note: Unfortunatelly you should install such filters for all widgets you want not to override the shortcuts manually.
UPDATE: Shortly - you are ignoring the underlying widget shortcut event and propagating it to the parent widget.
Below is comparison for Ctrl-Z (triggers Undo in edit) and Ctrl-V (ignored in edit instead of Paste, and triggers menu Action):
Block I - events in beginning are the same for both Ctrl-Z and ignored Ctrl-V:
Block II - where the difference happens...
For Ctrl-Z - lineEdit receives the Ctrl+Z KeyPress event, triggering the Undo operation:
QLineEdit recieves QKeyEvent(KeyPress, Key_Z, ControlModifier)
Here the MainWindow recieves no events, not depending of has it Ctrl+Z action shortcuts or not, it's just swallowed by QLineEdit
For Ctrl-V - MainWindow receives the Ctrl+V ShortcutOverride event propagated from QLineEdit:
filterEvent
"Hello from window shortcut!" code from menu Action triggered slot executed.
Here the QLineEdit receives no events after filter tells it to ignore ShortcutOverride, the MainWindow shortcut executed instead
Block III - events in ending are also the same for both Ctrl-Z and ignored Ctrl-V - just key release events:
P.S. I really don't know why it happens the exactly so - but this is just how it works :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With