From ede790fe7f99a6f2d15c3b7d9ab9f860fddb07a3 Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Thu, 14 May 2026 21:15:02 +0200 Subject: [PATCH] BUG: Fix ctkMessageBox "Don't show again" not working in Qt6 In Qt6, QDialog::finished(int) emits QDialog::Accepted (2) as the result code for custom buttons regardless of their QMessageBox button role. The previous code stored this result code in settings, so on the next launch buttonFromSettings() returned 2, button() found no match, and the dialog was always shown again. Fix by storing the button role instead, which is stable across Qt versions and is what button() uses for lookup. Also add a Qt6-specific code path in setVisible() to make the dialog visible before the queued auto-accept click, which is required for the connected action (e.g. item deletion) to execute in Qt6. --- .../Widgets/ctkDICOMVisualBrowserWidget.cpp | 14 ++------ Libs/Widgets/ctkMessageBox.cpp | 34 ++++++++++++++++--- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp index 22f41a8eab..6bb7cd485f 100644 --- a/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp +++ b/Libs/DICOM/Widgets/ctkDICOMVisualBrowserWidget.cpp @@ -4314,16 +4314,6 @@ bool ctkDICOMVisualBrowserWidget::confirmDeleteSelectedUIDs(const QStringList& u confirmDeleteDialog.addButton(tr("Cancel"), QMessageBox::RejectRole); confirmDeleteDialog.setDontShowAgainSettingsKey("VisualDICOMBrowser/DontConfirmDeleteSelected"); - int result = confirmDeleteDialog.exec(); - - // Check both the result code and the clicked button for robustness - // When auto-accepting via "Don't show again", result will be QDialog::Accepted - if (result == QDialog::Accepted || confirmDeleteDialog.clickedButton() == deleteButton) - { - return true; - } - else - { - return false; - } + confirmDeleteDialog.exec(); + return confirmDeleteDialog.clickedButton() == deleteButton; } diff --git a/Libs/Widgets/ctkMessageBox.cpp b/Libs/Widgets/ctkMessageBox.cpp index 607654518a..1aed03bb50 100644 --- a/Libs/Widgets/ctkMessageBox.cpp +++ b/Libs/Widgets/ctkMessageBox.cpp @@ -85,7 +85,7 @@ void ctkMessageBoxPrivate::init() #if (QT_VERSION >= QT_VERSION_CHECK(5,12,0)) // QMessageBox::done(int) is not called after Qt-5.12 // (see https://bugreports.qt.io/browse/QTBUG-74699), - // but onFinished(int) signal can be used instead. + // but the finished(int) signal can be used to invoke onFinished(int) instead. QObject::connect(q, SIGNAL(finished(int)), q, SLOT(onFinished(int))); #endif } @@ -299,7 +299,22 @@ void ctkMessageBox::onFinished(int resultCode) // Don't save if the button is not an accepting button if (d->DontShowAgainButtonRoles.contains(buttonRole)) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + Q_UNUSED(resultCode); + // Prefer storing the StandardButton value so that buttons sharing the same + // role (e.g. Ok/Open/Save all map to AcceptRole) are correctly identified + // on replay. Fall back to ButtonRole only for truly custom (non-standard) + // buttons, where standardButton() returns NoButton. In Qt6, resultCode for + // custom buttons is QDialog::Accepted (2) rather than the role, so storing + // the role explicitly is still necessary for the fallback path. + QMessageBox::StandardButton stdButton = this->standardButton(this->clickedButton()); + int buttonOrRole = (stdButton != QMessageBox::NoButton) + ? static_cast(stdButton) + : static_cast(buttonRole); + d->writeSettings(buttonOrRole); +#else d->writeSettings(resultCode); +#endif } } @@ -313,11 +328,20 @@ void ctkMessageBox::setVisible(bool visible) QAbstractButton* autoAcceptButton = d->button(dontShowAgainButtonOrRole); if (autoAcceptButton) { - // Show the dialog first, then auto-accept it after the event loop processes +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + // In Qt6, the action (e.g. deletion) triggered by the auto-accepted button + // does not execute unless the dialog has been made visible before click() + // is called. WA_DontShowOnScreen should make the dialog "visible" in Qt's internal + // state without creating a native window, so no window ever appears on screen. + this->setAttribute(Qt::WA_DontShowOnScreen, true); this->Superclass::setVisible(visible); - QTimer::singleShot(0, this, [autoAcceptButton]() { - autoAcceptButton->click(); - }); + QTimer::singleShot(0, autoAcceptButton, SLOT(click())); +#else + // Don't call click now, it would destroy the message box. The calling + // function might expect the message box to be still valid after + // setVisible() return. + QTimer::singleShot(0, autoAcceptButton, SLOT(click())); +#endif return; } }