hw_wallet/qt: reuse device message dialog across button requests
What changed, and why it matters
This commit is a user-interface performance and polish fix for Electrum's hardware wallet integration. It stops the app from destroying and recreating the same on-screen prompt every time a hardware wallet asks the user to confirm a transaction output. Instead, it keeps the existing dialog open and just updates its text. This removes visible flicker/animation on macOS and reduces delays caused by repeatedly creating windows. There is no security vulnerability being fixed here.
No security action required. Treat as a normal UX/performance improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change modifies electrum/hw_wallet/qt.py so that QtHandlerBase.message_dialog() reuses an already-visible WindowModalDialog and only updates its QLabel text when a new button-request message arrives with the same on_cancel callback. Previously, every button request called clear_dialog() (QDialog.accept) and constructed a new dialog, which on macOS caused sheet animations to replay per output and added GIL contention. The patch stores self.dialog_label and self._dialog_on_cancel to enable in-place updates. No cryptographic, authentication, or trust-boundary logic is altered.
Changed components
electrum/hw_wallet/qt.pyQtHandlerBase.message_dialog()Inspect captured patch +15 / −1
diff --git a/electrum/hw_wallet/qt.py b/electrum/hw_wallet/qt.py
index 5cef3e1..cb562ac 100644
--- a/electrum/hw_wallet/qt.py
+++ b/electrum/hw_wallet/qt.py
@@ -84,6 +84,8 @@ class QtHandlerBase(HardwareHandlerBase, QObject, Logger):
self.win = win
self.device = device
self.dialog = None
+ self.dialog_label = None
+ self._dialog_on_cancel = None
self.done = threading.Event()
def top_level_window(self):
@@ -174,12 +176,22 @@ class QtHandlerBase(HardwareHandlerBase, QObject, Logger):
MESSAGE_DIALOG_TITLE = None # type: Optional[str]
def message_dialog(self, msg, on_cancel=None):
+ # If a dialog is already open, update its text instead of rebuilding it.
+ # A device emits one button request per output, and rebuilding the
+ # window-modal dialog each time is slow and visibly janky on macOS
+ # (the modal "sheet" animates closed/open between outputs). See #10718.
+ if self.dialog is not None and self._dialog_on_cancel == on_cancel:
+ self.dialog_label.setText(msg)
+ if not self.dialog.isVisible(): # e.g. was hidden by a user "cancel"
+ self.dialog.show()
+ return
self.clear_dialog()
title = self.MESSAGE_DIALOG_TITLE
if title is None:
title = _('Please check your {} device').format(self.device)
self.dialog = dialog = WindowModalDialog(self.top_level_window(), title)
- label = QLabel(msg)
+ self._dialog_on_cancel = on_cancel
+ self.dialog_label = label = QLabel(msg)
label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
vbox = QVBoxLayout(dialog)
vbox.addWidget(label)
@@ -197,6 +209,8 @@ class QtHandlerBase(HardwareHandlerBase, QObject, Logger):
if self.dialog:
self.dialog.accept()
self.dialog = None
+ self.dialog_label = None
+ self._dialog_on_cancel = None
def win_query_choice(self, msg: str, choices: Sequence[ChoiceItem]):
try:
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.