From 711a32508580084fb9b5a29699112b974c02c28f Mon Sep 17 00:00:00 2001 From: SomberNight Date: Tue, 31 Oct 2023 17:25:51 +0000 Subject: [PATCH] main_window: split out "walet info" dlg into separate file - no semantic changes, only moving code - the change in qt/util.py is to avoid GC issues - due to moving code, the group was moving out of scope and getting GC-ed, as we only keep a reference to the vbox (idea from https://github.com/Electron-Cash/Electron-Cash/pull/2731/commits/263fb2ba332622a2f7b847377e9611a19854854b ) --- electrum/gui/qt/main_window.py | 145 +--------------------- electrum/gui/qt/util.py | 2 +- electrum/gui/qt/wallet_info_dialog.py | 170 ++++++++++++++++++++++++++ 3 files changed, 174 insertions(+), 143 deletions(-) create mode 100644 electrum/gui/qt/wallet_info_dialog.py diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index 434d3aa1e..ffc3235da 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -1808,148 +1808,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): self.show_message(_('Lightning keys have been initialized.')) def show_wallet_info(self): - dialog = WindowModalDialog(self, _("Wallet Information")) - dialog.setMinimumSize(800, 100) - vbox = QVBoxLayout() - wallet_type = self.wallet.db.get('wallet_type', '') - if self.wallet.is_watching_only(): - wallet_type += ' [{}]'.format(_('watching-only')) - seed_available = _('False') - if self.wallet.has_seed(): - seed_available = _('True') - ks = self.wallet.keystore - assert isinstance(ks, keystore.Deterministic_KeyStore) - seed_available += f" ({ks.get_seed_type()})" - keystore_types = [k.get_type_text() for k in self.wallet.get_keystores()] - grid = QGridLayout() - basename = os.path.basename(self.wallet.storage.path) - cur_row = 0 - grid.addWidget(WWLabel(_("Wallet name")+ ':'), cur_row, 0) - grid.addWidget(WWLabel(basename), cur_row, 1) - cur_row += 1 - if db_metadata := self.wallet.db.get_db_metadata(): - grid.addWidget(WWLabel(_("File created") + ':'), cur_row, 0) - grid.addWidget(WWLabel(db_metadata.to_str()), cur_row, 1) - cur_row += 1 - grid.addWidget(WWLabel(_("Wallet type")+ ':'), cur_row, 0) - grid.addWidget(WWLabel(wallet_type), cur_row, 1) - cur_row += 1 - grid.addWidget(WWLabel(_("Script type")+ ':'), cur_row, 0) - grid.addWidget(WWLabel(self.wallet.txin_type), cur_row, 1) - cur_row += 1 - grid.addWidget(WWLabel(_("Seed available") + ':'), cur_row, 0) - grid.addWidget(WWLabel(str(seed_available)), cur_row, 1) - cur_row += 1 - if len(keystore_types) <= 1: - grid.addWidget(WWLabel(_("Keystore type") + ':'), cur_row, 0) - ks_type = str(keystore_types[0]) if keystore_types else _('No keystore') - grid.addWidget(WWLabel(ks_type), cur_row, 1) - cur_row += 1 - # lightning - grid.addWidget(WWLabel(_('Lightning') + ':'), cur_row, 0) - from .util import IconLabel - if self.wallet.has_lightning(): - if self.wallet.lnworker.has_deterministic_node_id(): - grid.addWidget(WWLabel(_('Enabled')), cur_row, 1) - else: - label = IconLabel(text='Enabled, non-recoverable channels') - label.setIcon(read_QIcon('cloud_no')) - grid.addWidget(label, cur_row, 1) - if self.wallet.db.get('seed_type') == 'segwit': - msg = _("Your channels cannot be recovered from seed, because they were created with an old version of Electrum. " - "This means that you must save a backup of your wallet everytime you create a new channel.\n\n" - "If you want this wallet to have recoverable channels, you must close your existing channels and restore this wallet from seed") - else: - msg = _("Your channels cannot be recovered from seed. " - "This means that you must save a backup of your wallet everytime you create a new channel.\n\n" - "If you want to have recoverable channels, you must create a new wallet with an Electrum seed") - grid.addWidget(HelpButton(msg), cur_row, 3) - cur_row += 1 - grid.addWidget(WWLabel(_('Lightning Node ID:')), cur_row, 0) - cur_row += 1 - nodeid_text = self.wallet.lnworker.node_keypair.pubkey.hex() - nodeid_e = ShowQRLineEdit(nodeid_text, self.config, title=_("Node ID")) - grid.addWidget(nodeid_e, cur_row, 0, 1, 4) - cur_row += 1 - else: - if self.wallet.can_have_lightning(): - grid.addWidget(WWLabel('Not enabled'), cur_row, 1) - button = QPushButton(_("Enable")) - button.pressed.connect(lambda: self.init_lightning_dialog(dialog)) - grid.addWidget(button, cur_row, 3) - else: - grid.addWidget(WWLabel(_("Not available for this wallet.")), cur_row, 1) - grid.addWidget(HelpButton(_("Lightning is currently restricted to HD wallets with p2wpkh addresses.")), cur_row, 2) - cur_row += 1 - vbox.addLayout(grid) - - labels_clayout = None - - if self.wallet.is_deterministic(): - keystores = self.wallet.get_keystores() - - ks_stack = QStackedWidget() - - def select_ks(index): - ks_stack.setCurrentIndex(index) - - # only show the combobox in case multiple accounts are available - if len(keystores) > 1: - def label(idx, ks): - if isinstance(self.wallet, Multisig_Wallet) and hasattr(ks, 'label'): - return _("cosigner") + f' {idx+1}: {ks.get_type_text()} {ks.label}' - else: - return _("keystore") + f' {idx+1}' - - labels = [label(idx, ks) for idx, ks in enumerate(self.wallet.get_keystores())] - - on_click = lambda clayout: select_ks(clayout.selected_index()) - labels_clayout = ChoicesLayout(_("Select keystore"), labels, on_click) - vbox.addLayout(labels_clayout.layout()) - - for ks in keystores: - ks_w = QWidget() - ks_vbox = QVBoxLayout() - ks_vbox.setContentsMargins(0, 0, 0, 0) - ks_w.setLayout(ks_vbox) - - mpk_text = ShowQRTextEdit(ks.get_master_public_key(), config=self.config) - mpk_text.setMaximumHeight(max(150, 10 * font_height())) - mpk_text.addCopyButton() - run_hook('show_xpub_button', mpk_text, ks) - ks_vbox.addWidget(WWLabel(_("Master Public Key"))) - ks_vbox.addWidget(mpk_text) - - der_path_hbox = QHBoxLayout() - der_path_hbox.setContentsMargins(0, 0, 0, 0) - der_path_hbox.addWidget(WWLabel(_("Derivation path") + ':')) - der_path_text = WWLabel(ks.get_derivation_prefix() or _("unknown")) - der_path_text.setTextInteractionFlags(Qt.TextSelectableByMouse) - der_path_hbox.addWidget(der_path_text) - der_path_hbox.addStretch() - ks_vbox.addLayout(der_path_hbox) - - bip32fp_hbox = QHBoxLayout() - bip32fp_hbox.setContentsMargins(0, 0, 0, 0) - bip32fp_hbox.addWidget(QLabel("BIP32 root fingerprint:")) - bip32fp_text = WWLabel(ks.get_root_fingerprint() or _("unknown")) - bip32fp_text.setTextInteractionFlags(Qt.TextSelectableByMouse) - bip32fp_hbox.addWidget(bip32fp_text) - bip32fp_hbox.addStretch() - ks_vbox.addLayout(bip32fp_hbox) - - ks_stack.addWidget(ks_w) - - select_ks(0) - vbox.addWidget(ks_stack) - - vbox.addStretch(1) - btn_export_info = run_hook('wallet_info_buttons', self, dialog) - btn_close = CloseButton(dialog) - btns = Buttons(btn_export_info, btn_close) - vbox.addLayout(btns) - dialog.setLayout(vbox) - dialog.exec_() + from .wallet_info_dialog import WalletInfoDialog + d = WalletInfoDialog(self, window=self) + d.exec_() def remove_wallet(self): if self.question('\n'.join([ diff --git a/electrum/gui/qt/util.py b/electrum/gui/qt/util.py index 791dba706..3775b1875 100644 --- a/electrum/gui/qt/util.py +++ b/electrum/gui/qt/util.py @@ -434,7 +434,7 @@ class ChoicesLayout(object): vbox.addWidget(gb2) vbox2 = QVBoxLayout() gb2.setLayout(vbox2) - self.group = group = QButtonGroup() + self.group = group = QButtonGroup(gb2) if isinstance(choices, list): iterator = enumerate(choices) else: diff --git a/electrum/gui/qt/wallet_info_dialog.py b/electrum/gui/qt/wallet_info_dialog.py new file mode 100644 index 000000000..ce3901625 --- /dev/null +++ b/electrum/gui/qt/wallet_info_dialog.py @@ -0,0 +1,170 @@ +# Copyright (C) 2023 The Electrum developers +# Distributed under the MIT software license, see the accompanying +# file LICENCE or http://www.opensource.org/licenses/mit-license.php + +import os +from typing import TYPE_CHECKING + +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import (QLabel, QVBoxLayout, QGridLayout, + QHBoxLayout, QPushButton, QWidget, QStackedWidget) + +from electrum import keystore +from electrum.plugin import run_hook +from electrum.i18n import _ +from electrum.wallet import Multisig_Wallet + +from .qrtextedit import ShowQRTextEdit +from .util import (read_QIcon, WindowModalDialog, ChoicesLayout, Buttons, + WWLabel, CloseButton, HelpButton, font_height, ShowQRLineEdit) + +if TYPE_CHECKING: + from .main_window import ElectrumWindow + + +class WalletInfoDialog(WindowModalDialog): + + def __init__(self, parent: QWidget, *, window: 'ElectrumWindow'): + WindowModalDialog.__init__(self, parent, _("Wallet Information")) + self.setMinimumSize(800, 100) + wallet = window.wallet + config = window.config + vbox = QVBoxLayout() + wallet_type = wallet.db.get('wallet_type', '') + if wallet.is_watching_only(): + wallet_type += ' [{}]'.format(_('watching-only')) + seed_available = _('False') + if wallet.has_seed(): + seed_available = _('True') + ks = wallet.keystore + assert isinstance(ks, keystore.Deterministic_KeyStore) + seed_available += f" ({ks.get_seed_type()})" + keystore_types = [k.get_type_text() for k in wallet.get_keystores()] + grid = QGridLayout() + basename = os.path.basename(wallet.storage.path) + cur_row = 0 + grid.addWidget(WWLabel(_("Wallet name")+ ':'), cur_row, 0) + grid.addWidget(WWLabel(basename), cur_row, 1) + cur_row += 1 + if db_metadata := wallet.db.get_db_metadata(): + grid.addWidget(WWLabel(_("File created") + ':'), cur_row, 0) + grid.addWidget(WWLabel(db_metadata.to_str()), cur_row, 1) + cur_row += 1 + grid.addWidget(WWLabel(_("Wallet type")+ ':'), cur_row, 0) + grid.addWidget(WWLabel(wallet_type), cur_row, 1) + cur_row += 1 + grid.addWidget(WWLabel(_("Script type")+ ':'), cur_row, 0) + grid.addWidget(WWLabel(wallet.txin_type), cur_row, 1) + cur_row += 1 + grid.addWidget(WWLabel(_("Seed available") + ':'), cur_row, 0) + grid.addWidget(WWLabel(str(seed_available)), cur_row, 1) + cur_row += 1 + if len(keystore_types) <= 1: + grid.addWidget(WWLabel(_("Keystore type") + ':'), cur_row, 0) + ks_type = str(keystore_types[0]) if keystore_types else _('No keystore') + grid.addWidget(WWLabel(ks_type), cur_row, 1) + cur_row += 1 + # lightning + grid.addWidget(WWLabel(_('Lightning') + ':'), cur_row, 0) + from .util import IconLabel + if wallet.has_lightning(): + if wallet.lnworker.has_deterministic_node_id(): + grid.addWidget(WWLabel(_('Enabled')), cur_row, 1) + else: + label = IconLabel(text='Enabled, non-recoverable channels') + label.setIcon(read_QIcon('cloud_no')) + grid.addWidget(label, cur_row, 1) + if wallet.db.get('seed_type') == 'segwit': + msg = _("Your channels cannot be recovered from seed, because they were created with an old version of Electrum. " + "This means that you must save a backup of your wallet everytime you create a new channel.\n\n" + "If you want this wallet to have recoverable channels, you must close your existing channels and restore this wallet from seed") + else: + msg = _("Your channels cannot be recovered from seed. " + "This means that you must save a backup of your wallet everytime you create a new channel.\n\n" + "If you want to have recoverable channels, you must create a new wallet with an Electrum seed") + grid.addWidget(HelpButton(msg), cur_row, 3) + cur_row += 1 + grid.addWidget(WWLabel(_('Lightning Node ID:')), cur_row, 0) + cur_row += 1 + nodeid_text = wallet.lnworker.node_keypair.pubkey.hex() + nodeid_e = ShowQRLineEdit(nodeid_text, config, title=_("Node ID")) + grid.addWidget(nodeid_e, cur_row, 0, 1, 4) + cur_row += 1 + else: + if wallet.can_have_lightning(): + grid.addWidget(WWLabel('Not enabled'), cur_row, 1) + button = QPushButton(_("Enable")) + button.pressed.connect(lambda: window.init_lightning_dialog(self)) + grid.addWidget(button, cur_row, 3) + else: + grid.addWidget(WWLabel(_("Not available for this wallet.")), cur_row, 1) + grid.addWidget(HelpButton(_("Lightning is currently restricted to HD wallets with p2wpkh addresses.")), cur_row, 2) + cur_row += 1 + vbox.addLayout(grid) + + labels_clayout = None + + if wallet.is_deterministic(): + keystores = wallet.get_keystores() + + ks_stack = QStackedWidget() + + def select_ks(index): + ks_stack.setCurrentIndex(index) + + # only show the combobox in case multiple accounts are available + if len(keystores) > 1: + def label(idx, ks): + if isinstance(wallet, Multisig_Wallet) and hasattr(ks, 'label'): + return _("cosigner") + f' {idx+1}: {ks.get_type_text()} {ks.label}' + else: + return _("keystore") + f' {idx+1}' + + labels = [label(idx, ks) for idx, ks in enumerate(wallet.get_keystores())] + + on_click = lambda clayout: select_ks(clayout.selected_index()) + labels_clayout = ChoicesLayout(_("Select keystore"), labels, on_click) + vbox.addLayout(labels_clayout.layout()) + + for ks in keystores: + ks_w = QWidget() + ks_vbox = QVBoxLayout() + ks_vbox.setContentsMargins(0, 0, 0, 0) + ks_w.setLayout(ks_vbox) + + mpk_text = ShowQRTextEdit(ks.get_master_public_key(), config=config) + mpk_text.setMaximumHeight(max(150, 10 * font_height())) + mpk_text.addCopyButton() + run_hook('show_xpub_button', mpk_text, ks) + ks_vbox.addWidget(WWLabel(_("Master Public Key"))) + ks_vbox.addWidget(mpk_text) + + der_path_hbox = QHBoxLayout() + der_path_hbox.setContentsMargins(0, 0, 0, 0) + der_path_hbox.addWidget(WWLabel(_("Derivation path") + ':')) + der_path_text = WWLabel(ks.get_derivation_prefix() or _("unknown")) + der_path_text.setTextInteractionFlags(Qt.TextSelectableByMouse) + der_path_hbox.addWidget(der_path_text) + der_path_hbox.addStretch() + ks_vbox.addLayout(der_path_hbox) + + bip32fp_hbox = QHBoxLayout() + bip32fp_hbox.setContentsMargins(0, 0, 0, 0) + bip32fp_hbox.addWidget(QLabel("BIP32 root fingerprint:")) + bip32fp_text = WWLabel(ks.get_root_fingerprint() or _("unknown")) + bip32fp_text.setTextInteractionFlags(Qt.TextSelectableByMouse) + bip32fp_hbox.addWidget(bip32fp_text) + bip32fp_hbox.addStretch() + ks_vbox.addLayout(bip32fp_hbox) + + ks_stack.addWidget(ks_w) + + select_ks(0) + vbox.addWidget(ks_stack) + + vbox.addStretch(1) + btn_export_info = run_hook('wallet_info_buttons', self, self) + btn_close = CloseButton(self) + btns = Buttons(btn_export_info, btn_close) + vbox.addLayout(btns) + self.setLayout(vbox)