diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py index 79f5c1bce..52da25554 100644 --- a/electrum/gui/qt/util.py +++ b/electrum/gui/qt/util.py @@ -1259,10 +1259,12 @@ def read_QIcon_from_bytes(b: bytes) -> QIcon: qp = read_QPixmap_from_bytes(b) return QIcon(qp) + class IconLabel(QWidget): HorizontalSpacing = 2 - def __init__(self, *, text='', final_stretch=True): + def __init__(self, *, text='', final_stretch=True, reverse=False, hide_if_empty=False): super(QWidget, self).__init__() + self.hide_if_empty = hide_if_empty size = max(16, font_height()) self.icon_size = QSize(size, size) layout = QHBoxLayout() @@ -1271,13 +1273,18 @@ class IconLabel(QWidget): self.icon = QLabel() self.label = QLabel(text) self.label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) - layout.addWidget(self.label) + layout.addWidget(self.icon if reverse else self.label) layout.addSpacing(self.HorizontalSpacing) - layout.addWidget(self.icon) + layout.addWidget(self.label if reverse else self.icon) if final_stretch: layout.addStretch() + self.setText(text) + def setText(self, text): self.label.setText(text) + if self.hide_if_empty: + self.setVisible(bool(text)) + def setIcon(self, icon): self.icon.setPixmap(icon.pixmap(self.icon_size)) self.icon.repaint() # macOS hack for #6269 diff --git a/electrum/gui/qt/wizard/wallet.py b/electrum/gui/qt/wizard/wallet.py index 4437f5c24..6202f848a 100644 --- a/electrum/gui/qt/wizard/wallet.py +++ b/electrum/gui/qt/wizard/wallet.py @@ -29,7 +29,7 @@ from electrum.gui.qt.bip39_recovery_dialog import Bip39RecoveryDialog from electrum.gui.qt.password_dialog import PasswordLayout, PW_NEW, MSG_ENTER_PASSWORD, PasswordLayoutForHW from electrum.gui.qt.seed_dialog import SeedWidget, MSG_PASSPHRASE_WARN_ISSUE4566, KeysWidget from electrum.gui.qt.util import (PasswordLineEdit, char_width_in_lineedit, WWLabel, InfoButton, font_height, - ChoiceWidget, MessageBoxMixin, icon_path) + ChoiceWidget, MessageBoxMixin, icon_path, IconLabel, read_QIcon) if TYPE_CHECKING: from electrum.simple_config import SimpleConfig @@ -516,6 +516,12 @@ class WCEnterExt(WalletWizardComponent, Logger): self.ext_edit.textEdited.connect(self.on_text_edited) self.layout().addWidget(self.ext_edit) self.layout().addStretch(1) + self.warn_label = IconLabel(reverse=True, hide_if_empty=True) + self.warn_label.setIcon(read_QIcon('warning.png')) + self.layout().addWidget(self.warn_label) + + def on_ready(self): + self.validate() def on_text_edited(self, text): # TODO also for cosigners? @@ -525,30 +531,10 @@ class WCEnterExt(WalletWizardComponent, Logger): def validate(self): self.apply() - text = self.ext_edit.text() - if len(text) == 0: - self.valid = False - return - - cosigner_data = self.wizard.current_cosigner(self.wizard_data) - if self.wizard_data['wallet_type'] == 'multisig': - if 'seed_variant' in cosigner_data and cosigner_data['seed_variant'] in ['bip39', 'slip39']: - # defer validation to when derivation path is known - self.valid = True - else: - if self.wizard.has_duplicate_masterkeys(self.wizard_data): - self.logger.debug('Duplicate master keys!') - # TODO: user feedback - self.valid = False - elif self.wizard.has_heterogeneous_masterkeys(self.wizard_data): - self.logger.debug('Heterogenous master keys!') - # TODO: user feedback - self.valid = False - else: - self.valid = True - else: - self.valid = True + musig_valid, errortext = self.wizard.check_multisig_constraints(self.wizard_data) + self.valid = musig_valid + self.warn_label.setText(errortext) def apply(self): cosigner_data = self.wizard.current_cosigner(self.wizard_data) @@ -567,8 +553,14 @@ class WCConfirmExt(WalletWizardComponent): self.layout().addWidget(self.ext_edit) self.layout().addStretch(1) - def on_text_edited(self, text): - self.valid = text == self.wizard_data['seed_extra_words'] + def on_ready(self): + self.validate() + + def on_text_edited(self, *args): + self.validate() + + def validate(self): + self.valid = self.ext_edit.text() == self.wizard_data['seed_extra_words'] def apply(self): pass @@ -580,6 +572,8 @@ class WCHaveSeed(WalletWizardComponent, Logger): Logger.__init__(self) self.layout().addWidget(WWLabel(_('Please enter your seed phrase in order to restore your wallet.'))) + self.warn_label = IconLabel(reverse=True, hide_if_empty=True) + self.warn_label.setIcon(read_QIcon('warning.png')) self.seed_widget = None self.can_passphrase = True @@ -610,6 +604,8 @@ class WCHaveSeed(WalletWizardComponent, Logger): self.layout().addWidget(self.seed_widget) self.layout().addStretch(1) + self.layout().addWidget(self.warn_label) + def is_seed(self, x): # really only used for electrum seeds. bip39 and slip39 are validated in SeedWidget t = mnemonic.calc_seed_type(x) @@ -635,10 +631,11 @@ class WCHaveSeed(WalletWizardComponent, Logger): return self.apply() - if not self.wizard.check_multisig_constraints(self.wizard_data)[0]: - # TODO: user feedback + musig_valid, errortext = self.wizard.check_multisig_constraints(self.wizard_data) + if not musig_valid: seed_valid = False + self.warn_label.setText(errortext) self.valid = seed_valid def apply(self): @@ -662,6 +659,9 @@ class WCScriptAndDerivation(WalletWizardComponent, Logger): self.choice_w = None self.derivation_path_edit = None + self.warn_label = IconLabel(reverse=True, hide_if_empty=True) + self.warn_label.setIcon(read_QIcon('warning.png')) + def on_ready(self): message1 = _('Choose the type of addresses in your wallet.') message2 = ' '.join([ @@ -736,6 +736,7 @@ class WCScriptAndDerivation(WalletWizardComponent, Logger): on_choice_click(self.choice_w.selected_index) # set default value for derivation path self.layout().addStretch(1) + self.layout().addWidget(self.warn_label) def validate(self): self.apply() @@ -744,10 +745,12 @@ class WCScriptAndDerivation(WalletWizardComponent, Logger): valid = is_bip32_derivation(cosigner_data['derivation_path']) if valid: - valid, error = self.wizard.check_multisig_constraints(self.wizard_data) + valid, errortext = self.wizard.check_multisig_constraints(self.wizard_data) if not valid: - # TODO: user feedback - self.logger.error(error) + self.logger.error(errortext) + self.warn_label.setText(errortext) + else: + self.warn_label.setText(_('Invalid derivation path')) self.valid = valid @@ -825,6 +828,9 @@ class WCHaveMasterKey(WalletWizardComponent): self.label.setMinimumWidth(400) self.header_layout.addWidget(self.label) + self.warn_label = IconLabel(reverse=True, hide_if_empty=True) + self.warn_label.setIcon(read_QIcon('warning.png')) + def on_ready(self): if self.wizard_data['wallet_type'] == 'standard': self.label.setText(self.message_create) @@ -840,10 +846,12 @@ class WCHaveMasterKey(WalletWizardComponent): def is_valid(x) -> bool: if not keystore.is_bip32_key(x): + self.warn_label.setText(_('Invalid key')) return False self.apply() - if not self.wizard.check_multisig_constraints(self.wizard_data)[0]: - # TODO: user feedback + musig_valid, errortext = self.wizard.check_multisig_constraints(self.wizard_data) + self.warn_label.setText(errortext) + if not musig_valid: return False return True else: @@ -858,6 +866,8 @@ class WCHaveMasterKey(WalletWizardComponent): self.keys_widget.validChanged.connect(key_valid_changed) self.layout().addWidget(self.keys_widget) + self.layout().addStretch() + self.layout().addWidget(self.warn_label) def apply(self): text = self.keys_widget.get_text()