From d3a1cef9bade02626a1cb56d71d181f761a950cb Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Wed, 9 Aug 2023 00:37:20 +0200 Subject: [PATCH] create ChoiceWidget, refactor ChoicesLayout to ChoiceWidget --- electrum/gui/qt/util.py | 41 ++++++++++++++++ electrum/gui/qt/wizard/wallet.py | 81 ++++++++++++++------------------ 2 files changed, 75 insertions(+), 47 deletions(-) diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py index 52025412c..13c05512c 100644 --- a/electrum/gui/qt/util.py +++ b/electrum/gui/qt/util.py @@ -456,6 +456,47 @@ class ChoicesLayout(object): def selected_index(self): return self.group.checkedId() + +class ChoiceWidget(QWidget): + itemSelected = pyqtSignal([int], arguments=['index']) + + def __init__(self, *, message=None, choices=[], selected=None): + QWidget.__init__(self) + vbox = QVBoxLayout() + self.setLayout(vbox) + + self.selected_index = -1 + self.selected_item = None + self.choices = choices + + if message and len(message) > 50: + vbox.addWidget(WWLabel(message)) + message = "" + gb2 = QGroupBox(message) + vbox.addWidget(gb2) + vbox2 = QVBoxLayout() + gb2.setLayout(vbox2) + self.group = group = QButtonGroup() + assert isinstance(choices, list) + iterator = enumerate(choices) + for i, c in iterator: + button = QRadioButton(gb2) + button.setText(c[1]) + vbox2.addWidget(button) + group.addButton(button) + group.setId(button, i) + if (i == 0 and selected is None) or c[0] == selected: + self.selected_index = i + self.selected_item = c + button.setChecked(True) + group.buttonClicked.connect(self.on_selected) + + def on_selected(self, button): + self.selected_index = self.group.id(button) + self.selected_item = self.choices[self.selected_index] + self.itemSelected.emit(self.selected_index) + + def address_field(addresses): hbox = QHBoxLayout() address_e = QLineEdit() diff --git a/electrum/gui/qt/wizard/wallet.py b/electrum/gui/qt/wizard/wallet.py index ccc241dd2..19da61dc0 100644 --- a/electrum/gui/qt/wizard/wallet.py +++ b/electrum/gui/qt/wizard/wallet.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING from PyQt5.QtCore import Qt, QTimer, QRect, pyqtSignal from PyQt5.QtGui import QPen, QPainter, QPalette -from PyQt5.QtWidgets import (QApplication, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QWidget, +from PyQt5.QtWidgets import (QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QWidget, QFileDialog, QSlider, QGridLayout) from electrum.bip32 import is_bip32_derivation, BIP32Node, normalize_bip32_derivation, xpub_type @@ -21,10 +21,12 @@ from .wizard import QEAbstractWizard, WizardComponent from electrum.logging import get_logger, Logger from electrum import WalletStorage, mnemonic, keystore from electrum.wizard import NewWalletWizard -from ..bip39_recovery_dialog import Bip39RecoveryDialog -from ..password_dialog import PasswordLayout, PW_NEW, MSG_ENTER_PASSWORD, PasswordLayoutForHW -from ..seed_dialog import SeedLayout, MSG_PASSPHRASE_WARN_ISSUE4566, KeysLayout -from ..util import ChoicesLayout, PasswordLineEdit, char_width_in_lineedit, WWLabel, InfoButton, font_height + +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 SeedLayout, MSG_PASSPHRASE_WARN_ISSUE4566, KeysLayout +from electrum.gui.qt.util import (PasswordLineEdit, char_width_in_lineedit, WWLabel, InfoButton, font_height, + ChoiceWidget) if TYPE_CHECKING: from electrum.simple_config import SimpleConfig @@ -276,15 +278,13 @@ class WCWalletType(WizardComponent): ] choices = [pair for pair in wallet_kinds if pair[0] in wallet_types] - self.c_values = [x[0] for x in choices] - c_titles = [x[1] for x in choices] - self.clayout = ChoicesLayout(message, c_titles) - self.layout().addLayout(self.clayout.layout()) + self.choice_w = ChoiceWidget(message=message, choices=choices, selected='standard') + self.layout().addWidget(self.choice_w) self.layout().addStretch(1) self._valid = True def apply(self): - self.wizard_data['wallet_type'] = self.c_values[self.clayout.selected_index()] + self.wizard_data['wallet_type'] = self.choice_w.selected_item[0] class WCKeystoreType(WizardComponent): @@ -298,15 +298,13 @@ class WCKeystoreType(WizardComponent): ('hardware', _('Use a hardware device')) ] - self.c_values = [x[0] for x in choices] - c_titles = [x[1] for x in choices] - self.clayout = ChoicesLayout(message, c_titles) - self.layout().addLayout(self.clayout.layout()) + self.choice_w = ChoiceWidget(message=message, choices=choices) + self.layout().addWidget(self.choice_w) self.layout().addStretch(1) self._valid = True def apply(self): - self.wizard_data['keystore_type'] = self.c_values[self.clayout.selected_index()] + self.wizard_data['keystore_type'] = self.choice_w.selected_item[0] class WCCreateSeed(WizardComponent): @@ -542,7 +540,6 @@ class WCScriptAndDerivation(WizardComponent): WizardComponent.__init__(self, parent, wizard, title=_('Script type and Derivation path')) def on_ready(self): - message1 = _('Choose the type of addresses in your wallet.') message2 = ' '.join([ _('You can override the suggested derivation path.'), @@ -560,15 +557,12 @@ class WCScriptAndDerivation(WizardComponent): if 'multisig_current_cosigner' in self.wizard_data: # get script type of first cosigner ks = self.wizard.keystore_from_data(self.wizard_data['wallet_type'], self.wizard_data) - script_type = xpub_type(ks.get_master_public_key()) - script_types = [*zip(*choices)][0] - chosen_idx = script_types.index(script_type) - default_choice_idx = chosen_idx + default_choice = xpub_type(ks.get_master_public_key()) hide_choices = True else: - default_choice_idx = 2 + default_choice = 'p2wsh' else: - default_choice_idx = 2 + default_choice = 'p2wpkh' choices = [ # TODO: nicer to refactor 'standard' to 'p2pkh', but backend wallet still uses 'standard' ('standard', 'legacy (p2pkh)', bip44_derivation(0, bip43_purpose=44)), @@ -604,24 +598,22 @@ class WCScriptAndDerivation(WizardComponent): self.layout().addWidget(button, alignment=Qt.AlignLeft) self.layout().addWidget(QLabel(_("Or"))) - self.c_values = [x[0] for x in choices] - c_titles = [x[1] for x in choices] - c_default_text = [x[2] for x in choices] + def on_choice_click(index): + self.derivation_path_edit.setText(self.choice_w.selected_item[2]) + self.choice_w = ChoiceWidget(message=message1, choices=choices, selected=default_choice) + self.choice_w.itemSelected.connect(on_choice_click) - def on_choice_click(clayout): - idx = clayout.selected_index() - self.derivation_path_edit.setText(c_default_text[idx]) - self.clayout = ChoicesLayout(message1, c_titles, on_choice_click, - checked_index=default_choice_idx) if not hide_choices: - self.layout().addLayout(self.clayout.layout()) + self.layout().addWidget(self.choice_w) self.layout().addWidget(WWLabel(message2)) self.derivation_path_edit = QLineEdit() self.derivation_path_edit.textChanged.connect(self.validate) - on_choice_click(self.clayout) # set default value for derivation path self.layout().addWidget(self.derivation_path_edit) + + on_choice_click(self.choice_w.selected_index) # set default value for derivation path + self.layout().addStretch(1) def validate(self): @@ -649,7 +641,7 @@ class WCScriptAndDerivation(WizardComponent): def apply(self): cosigner_data = self._current_cosigner(self.wizard_data) - cosigner_data['script_type'] = self.c_values[self.clayout.selected_index()] + cosigner_data['script_type'] = self.choice_w.selected_item[0] cosigner_data['derivation_path'] = str(self.derivation_path_edit.text()) @@ -664,10 +656,8 @@ class WCCosignerKeystore(WizardComponent): ('hardware', _('Cosign with hardware device')) ] - self.c_values = [x[0] for x in choices] - c_titles = [x[1] for x in choices] - self.clayout = ChoicesLayout(message, c_titles) - self.layout().addLayout(self.clayout.layout()) + self.choice_w = ChoiceWidget(message=message, choices=choices) + self.layout().addWidget(self.choice_w) self.cosigner = 0 self.participants = 0 @@ -696,7 +686,7 @@ class WCCosignerKeystore(WizardComponent): self.layout().addStretch(1) def apply(self): - self.wizard_data['cosigner_keystore_type'] = self.c_values[self.clayout.selected_index()] + self.wizard_data['cosigner_keystore_type'] = self.choice_w.selected_item[0] self.wizard_data['multisig_current_cosigner'] = self.cosigner self.wizard_data['multisig_cosigner_data'][str(self.cosigner)] = {} @@ -964,7 +954,7 @@ class WCChooseHWDevice(WizardComponent, Logger): self.device_list = QWidget() self.device_list_layout = QVBoxLayout() self.device_list.setLayout(self.device_list_layout) - self.clayout = None + self.choice_w = None self.rescan_button = QPushButton(_('Rescan devices')) self.rescan_button.clicked.connect(self.on_rescan) @@ -1004,15 +994,13 @@ class WCChooseHWDevice(WizardComponent, Logger): descr = f"{label} [{info.model_name or name}, {state}, {transport_str}]" choices.append(((name, info), descr)) msg = _('Select a device') + ':' - self.c_values = [x[0] for x in choices] - c_titles = [x[1] for x in choices] + # remove old component before adding anew a = self.device_list.layout().itemAt(0) self.device_list.layout().removeItem(a) - self.clayout = ChoicesLayout(msg, c_titles) - self.device_list_layout = self.clayout.layout() - self.device_list.layout().addLayout(self.device_list_layout) + self.choice_w = ChoiceWidget(message=msg, choices=choices) + self.device_list.layout().addWidget(self.choice_w) self.valid = True @@ -1098,10 +1086,9 @@ class WCChooseHWDevice(WizardComponent, Logger): t.start() def apply(self): - if self.clayout: + if self.choice_w: # TODO: data is not (de)serializable yet, wizard_data cannot be persisted - self.wizard_data['hardware_device'] = self.c_values[self.clayout.selected_index()] - self.logger.debug(repr(self.wizard_data['hardware_device'])) + self.wizard_data['hardware_device'] = self.choice_w.selected_item[0] class WCWalletPasswordHardware(WizardComponent):