Solved Keystroke is forwarded unintentionally
-
Hi,
I just use QMessageBox::question( ... )
do you have a sample, that uses standarddialogs with open and slots?
-
In this case you can use the buttonClicked signal to know what happened.
-
Hi,
guess I have to go back to school again - nobody understands my writing :(
@SGaist said in Keystroke is forwarded unintentionally:
... to know what happened.
I already know, what happened.
In the screenshot usecase the delete key with focus at tableview was pressed, which raises this dialog. When the user selects "No" button (cursor right) and presses Enter, then the delete operation aborts.
... but then the edit operation starts (which is bound to Enter key with focus at tableview). When edit starts, the standard dialog has already been removed ...But when enter was pressed, the dialog had the focus and not the tableview. So the event forwarding is faulty, as the action is already processed.
So that's going wrong, and I'm looking for a way to get out of that trap.
If it is possible to clear event-queue programmatically, best point is imho the function that calls
QMessageBox::question
-
@django-Reinhard said in Keystroke is forwarded unintentionally:
So that's going wrong, and I'm looking for a way to get out of that trap.
If your view has QAbstractItemView::AnyKeyPressed as its edit trigger then the Delete you started with probably put the cell into edit mode before your dialog blocked the event loop of the main program. It stays in that mode when your dialog is dismissed.
Hard to say for sure... we cannot see your code or "keyboard handler" or how the plumbing is done. Guessing is what we are left with. -
@ChrisW67 said in Keystroke is forwarded unintentionally:
if your view has QAbstractItemView::AnyKeyPressed as its edit trigger
Well, not intentionally. Don't know, what default is, but I don't use intable editing. Editor is the widget at the lower right.
@ChrisW67 said in Keystroke is forwarded unintentionally:
Hard to say for sure... we cannot see your code or "keyboard handler" or how the plumbing is done. Guessing is what we are left with.
Yes, you're absolutely right! My fault.
My code is available.
excerpts from it ...
Keyboard handler:
void ToolManager::keyReleaseEvent(QKeyEvent *event) { switch (event->key()) { case KeyCodes::Enter: if (tools->hasFocus()) editTool(); break; ... case KeyCodes::Insert: { if (categories->hasFocus()) createCategory(); else if (tools->hasFocus()) createTool(); } break; case KeyCodes::Delete: { if (categories->hasFocus()) deleteCategory(); else if (tools->hasFocus()) deleteTool(); } break; case KeyCodes::F6_Key: { if (categories->hasFocus()) renameCategory(); } break; case KeyCodes::F10_Key: if (tEdit->isEnabled()) saveToolChanges(); break; } event->setAccepted(true); }
... delete-Function:
void ToolManager::deleteTool() { qDebug() << "delete tool: " << toolModel->record(tool2Edit).value("num"); QMessageBox::StandardButton reply; reply = QMessageBox::question(this , tr("QMessageBox::question()") , tr("Should this tool be deleted?") , QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::No) return; if (!toolModel->removeRows(tool2Edit, 1)) qDebug() << toolModel->lastError().text(); if (!toolModel->submitAll()) qDebug() << toolModel->lastError().text(); }
... and edit-Function:
void ToolManager::editTool() { categories->hide(); tools->hide(); tEdit->setEnabled(true); // fill all space inside QScrollarea if (edSize.width() == -1 && edSize.height() == -1) edSize = tEdit->size(); tEdit->resize(size().width() - 20, size().height() - 20); // data record is already loaded into editor, // so just inform about how to finish editing QMessageBox::information(this , tr("QMessageBox::information()") , tr("To finish tool editing press F10 " "to save changes or ESC to abort.")); }
-
Hi,
I did some further tests, but situation does not look any nicer.
I tried to patch Qt like this:
void QPushButton::keyPressEvent(QKeyEvent *e) { Q_D(QPushButton); switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: if (autoDefault() || d->defaultButton) { e->setAccepted(true); // added 4 test click(); break; } Q_FALLTHROUGH(); default: QAbstractButton::keyPressEvent(e); } }
which worked fine in debugging session, but failed in release code.
So I guessed, it is a timing issue, but adding some sleep did not change anything either.so I tried to check timestamp of keyEvent - but against all logic keyEvent that arrives my widget class has newer timestamp, than timestamp when messagebox got closed ...
So I tried to use EventFilter - but - cursor right arrives the filter, but Enter that triggered the button did not arrive the filter.
I also tried to use a modified messagebox, that uses open instead of exec - but on open no dialog window appeared.
Actually I run out of ideas.
debug logs prints this:
- before mb::exec
- Messagebox finished ...
- after mb::exec - myTS: 12197200
- TM: enter ( 16777220 ) has ts: 12197346
- myTS: 12197350
"before mb::exec" is originated in delete-function, just before calling exec of the messagebox
"Messagebox finished" is printed by the callback, connected to messagebox::finished
"after mb::exec" is printed from delete-function, after closing the messagebox. So the Enter-keypress has happened before that timestamp (myTS).
"TM: enter" is printed from keyboard-handler of the main widget (parent of treeview, tableview and editor).
Last message "myTS" is the timestamp printed from keyboard-handler when release of Enter-key has been detected.
So even if event routing might be ok, timestamp refresh is definitely NOT ok. And as QEvent does not contain the widget, that had the focus when the key was pressed, I don't know, how to solve that trap.
Any hint is appreciated!
-
Hi
For test, could you use this function
void MainWindow::deleteTool() { QMessageBox *msgBox = new QMessageBox(this); msgBox->setText("QMessageBox::question()"); msgBox->setInformativeText("Should this tool be deleted?"); msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox->setDefaultButton(QMessageBox::Yes); msgBox->setIcon(QMessageBox::Question); // handle answer connect(msgBox, &QMessageBox::buttonClicked, this, [this, msgBox](QAbstractButton * button) { if ( msgBox->button(QMessageBox::StandardButton::Yes) == button ) qDebug() << "Yes to the dress"; // youcan use this->toolModel->xxxx msgBox->deleteLater(); }); msgBox->open(); }
and see if return still bleeds through as then we know it is indeed a side effect of the exec() / local event loop.
-
Hi,
thank you for your attention!
I changed your code to this:
QMessageBox *msgBox = new QMessageBox(this); msgBox->setText("QMessageBox::question()"); msgBox->setInformativeText("Should this tool be deleted?"); msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox->setDefaultButton(QMessageBox::Yes); msgBox->setIcon(QMessageBox::Question); // handle answer connect(msgBox, &QMessageBox::buttonClicked , this, [this, msgBox](QAbstractButton * button) { if (msgBox->button(QMessageBox::StandardButton::Yes) == button) qDebug() << "Yes to the dress"; // youcan use this->toolModel->xxxx else qDebug() << "No - don't drop this"; qDebug() << "from mb::callback - myTS:" << timeStamp(); msgBox->deleteLater(); }); msgBox->open();
... and the same happens.
Debug log shows:No - don't drop this from mb::callback - myTS: 1068270 TM: enter ( 16777220 ) has ts: 1068408 myTS: 1068410
I updated my repo, in case you want debug.
Just to clarify things: usecase is:
- select a tool (in tableview)
- hit delete key (which opens the messagebox)
- hit cursor right to change focus from "Yes" to "No"
- hit Enter to activate the button
then editor starts with tool detail data, started from keyboard-handler keyRelease and Enter-key.
I thought I had similar code.
Could someone please explain me, why in my case nothing happens, when I call msgBox->open() ?My code looks like:
QMessageBox* MessageBox::showMessage(QWidget *parent , QMessageBox::Icon icon , const QString &title , const QString &text , const QString &button0Text , const QString &button1Text , const QString &button2Text , int , int ) { mb = new QMessageBox(icon, title, text, QMessageBox::NoButton, parent); QString myButton0Text = button0Text; if (myButton0Text.isEmpty()) myButton0Text = QDialogButtonBox::tr("OK"); mb->addButton(myButton0Text, QMessageBox::ActionRole); if (!button1Text.isEmpty()) mb->addButton(button1Text, QMessageBox::ActionRole); if (!button2Text.isEmpty()) mb->addButton(button2Text, QMessageBox::ActionRole); // const QList<QAbstractButton *> &buttonList = messageBox.d_func()->customButtonList; // messageBox.setDefaultButton(static_cast<QPushButton *>(buttonList.value(defaultButtonNumber))); // messageBox.setEscapeButton(buttonList.value(escapeButtonNumber)); connect(mb, &QMessageBox::finished, this, &MessageBox::finished); // messageBox.exec(); return mb; } QMessageBox* MessageBox::question(QWidget* parent, const QString& title, const QString& text) { return showMessage(parent, QMessageBox::Question, title, text, tr("Yes"), tr("No"), QString()); } void MessageBox::finished() { qDebug() << "Messagebox finished ..."; }
In ToolManager::delete I had this code:
MessageBox mb; QMessageBox* qmb = mb.question(this , tr("QMessageBox::question()") , tr("Should this tool be deleted?")); // qmb->installEventFilter(this); qDebug() << "before mb::exec"; qmb->exec(); qDebug() << "after mb::exec - myTS:" << timeStamp(); return;
I tried qmb->open() but nothing happened.
Therefore I got back to qmb->exec(). -
Hmm so it seems its not due to using exec()
If I want to run your code, I have to call/run setupDb first ?
-
Yes, its a separate pro-file, which creates and populates the database.
-
Just to close this:
I added a timecode check and this way I could sort out the unwanted/wrong keyevent.