diff --git a/l10n/messages.pot b/l10n/messages.pot index 9bba17ec..5f147efe 100644 --- a/l10n/messages.pot +++ b/l10n/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: seedsigner 0.8.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-11-01 16:48-0500\n" +"POT-Creation-Date: 2024-11-02 12:38-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1012,7 +1012,7 @@ msgid "Scan a SeedQR" msgstr "" #: src/seedsigner/views/seed_views.py -msgid "Create a seed" +msgid " Create a seed" msgstr "" #: src/seedsigner/views/seed_views.py diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index 693afaef..3ff20e2f 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -1,8 +1,6 @@ import logging import time import traceback -import gettext -import os from embit.descriptor import Descriptor from embit.psbt import PSBT @@ -140,9 +138,6 @@ class Controller(Singleton): screensaver: ScreensaverScreen = None toast_notification_thread: BaseToastOverlayManagerThread = None - # Multilanguage / Localization (l10n) initialization - gettext.install('messages', localedir='seedsigner/resources/seedsigner-translations/l10n') - @classmethod def get_instance(cls): @@ -155,17 +150,7 @@ class Controller(Singleton): @classmethod - def configure_instance(cls, disable_hardware=False): - """ - - `disable_hardware` is only meant to be used by the test suite so that it - can keep re-initializing a Controller in however many tests it needs to. But - this is only possible if the hardware isn't already being reserved. Without - this you get: - - RuntimeError: Conflicting edge detection already enabled for this GPIO channel - - each time you try to re-initialize a Controller. - """ + def configure_instance(cls): from seedsigner.gui.renderer import Renderer from seedsigner.hardware.microsd import MicroSD diff --git a/src/seedsigner/gui/screens/psbt_screens.py b/src/seedsigner/gui/screens/psbt_screens.py index 42326759..7546e619 100644 --- a/src/seedsigner/gui/screens/psbt_screens.py +++ b/src/seedsigner/gui/screens/psbt_screens.py @@ -5,14 +5,13 @@ from dataclasses import dataclass from gettext import gettext as _ from gettext import ngettext from PIL import Image, ImageDraw, ImageFilter -from typing import List +from seedsigner.gui.components import (BtcAmount, Icon, FontAwesomeIconConstants, IconTextLine, FormattedAddress, GUIConstants, Fonts, SeedSignerIconConstants, TextArea, + calc_bezier_curve, linear_interp) from seedsigner.gui.renderer import Renderer from seedsigner.models.threads import BaseThread -from .screen import ButtonListScreen, WarningScreen -from ..components import (BtcAmount, Button, Icon, FontAwesomeIconConstants, IconTextLine, FormattedAddress, GUIConstants, Fonts, SeedSignerIconConstants, TextArea, - calc_bezier_curve, linear_interp) +from .screen import ButtonListScreen @@ -24,7 +23,7 @@ class PSBTOverviewScreen(ButtonListScreen): num_inputs: int = 0 num_self_transfer_outputs: int = 0 num_change_outputs: int = 0 - destination_addresses: List[str] = None + destination_addresses: list[str] = None has_op_return: bool = False diff --git a/src/seedsigner/gui/screens/scan_screens.py b/src/seedsigner/gui/screens/scan_screens.py index a7832848..ae74ef7f 100644 --- a/src/seedsigner/gui/screens/scan_screens.py +++ b/src/seedsigner/gui/screens/scan_screens.py @@ -5,13 +5,11 @@ from gettext import gettext as _ from PIL import Image, ImageDraw from seedsigner.gui import renderer -from seedsigner.hardware.buttons import HardwareButtonsConstants -from seedsigner.hardware.camera import Camera -from seedsigner.models.decode_qr import DecodeQR, DecodeQRStatus +from seedsigner.gui.components import GUIConstants, Fonts +from seedsigner.models.decode_qr import DecodeQR from seedsigner.models.threads import BaseThread, ThreadsafeCounter from .screen import BaseScreen -from ..components import GUIConstants, Fonts, SeedSignerIconConstants @@ -78,8 +76,10 @@ class ScanScreen(BaseScreen): class LivePreviewThread(BaseThread): - def __init__(self, camera: Camera, decoder: DecodeQR, renderer: renderer.Renderer, instructions_text: str, render_rect: tuple[int,int,int,int], frame_decode_status: ThreadsafeCounter, frames_decoded_counter: ThreadsafeCounter): - self.camera = camera + def __init__(self, decoder: DecodeQR, renderer: renderer.Renderer, instructions_text: str, render_rect: tuple[int,int,int,int], frame_decode_status: ThreadsafeCounter, frames_decoded_counter: ThreadsafeCounter): + from seedsigner.hardware.camera import Camera + + self.camera = Camera.get_instance() self.decoder = decoder self.renderer = renderer self.instructions_text = instructions_text @@ -252,6 +252,9 @@ class ScanScreen(BaseScreen): Screen. Once interaction starts, the display updates have to be managed in _run(). The live preview is an extra-complex case. """ + from seedsigner.hardware.buttons import HardwareButtonsConstants + from seedsigner.models.decode_qr import DecodeQRStatus + num_frames = 0 start_time = time.time() while True: diff --git a/src/seedsigner/gui/screens/screen.py b/src/seedsigner/gui/screens/screen.py index f190bda9..74f99b5b 100644 --- a/src/seedsigner/gui/screens/screen.py +++ b/src/seedsigner/gui/screens/screen.py @@ -9,7 +9,6 @@ from seedsigner.gui.components import (GUIConstants, BaseComponent, Button, Icon, IconButton, LargeIconButton, SeedSignerIconConstants, TopNav, TextArea, load_image) from seedsigner.gui.keyboard import Keyboard, TextEntryDisplay -from seedsigner.gui.renderer import Renderer from seedsigner.hardware.buttons import HardwareButtonsConstants, HardwareButtons from seedsigner.models.encode_qr import BaseQrEncoder from seedsigner.models.settings import SettingsConstants @@ -125,6 +124,7 @@ class LoadingScreenThread(BaseThread): def run(self): + from seedsigner.gui.renderer import Renderer renderer: Renderer = Renderer.get_instance() center_image = load_image("btc_logo_60x60.png") @@ -181,7 +181,7 @@ class LoadingScreenThread(BaseThread): width=GUIConstants.COMPONENT_PADDING ) - renderer.show_image(is_background_thread=True) + renderer.show_image() position += arc_sweep @@ -675,12 +675,12 @@ class QRDisplayScreen(BaseScreen): qr_encoder: BaseQrEncoder = None class QRDisplayThread(BaseThread): - def __init__(self, qr_encoder: BaseQrEncoder, qr_brightness: ThreadsafeCounter, renderer: Renderer, - tips_start_time: ThreadsafeCounter): + def __init__(self, qr_encoder: BaseQrEncoder, qr_brightness: ThreadsafeCounter, tips_start_time: ThreadsafeCounter): + from seedsigner.gui.renderer import Renderer super().__init__() self.qr_encoder = qr_encoder self.qr_brightness = qr_brightness - self.renderer = renderer + self.renderer = Renderer.get_instance() self.tips_start_time = tips_start_time @@ -807,7 +807,6 @@ class QRDisplayScreen(BaseScreen): self.threads.append(QRDisplayScreen.QRDisplayThread( qr_encoder=self.qr_encoder, qr_brightness=self.qr_brightness, - renderer=self.renderer, tips_start_time=self.tips_start_time )) diff --git a/src/seedsigner/views/psbt_views.py b/src/seedsigner/views/psbt_views.py index 5b64c474..700bb2bd 100644 --- a/src/seedsigner/views/psbt_views.py +++ b/src/seedsigner/views/psbt_views.py @@ -1,14 +1,8 @@ from gettext import gettext as _ -from embit.psbt import PSBT -from embit import script -from embit.networks import NETWORKS -from seedsigner.controller import Controller -from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconConstants -from seedsigner.models.encode_qr import UrPsbtQrEncoder from seedsigner.models.psbt_parser import PSBTParser from seedsigner.models.settings import SettingsConstants -from seedsigner.gui.screens.psbt_screens import PSBTOpReturnScreen, PSBTOverviewScreen, PSBTMathScreen, PSBTAddressDetailsScreen, PSBTChangeDetailsScreen, PSBTFinalizeScreen +from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconConstants from seedsigner.gui.screens.screen import (RET_CODE__BACK_BUTTON, ButtonListScreen, WarningScreen, DireWarningScreen, QRDisplayScreen) from seedsigner.views.view import BackStackView, MainMenuView, NotYetImplementedView, View, Destination @@ -22,6 +16,8 @@ class PSBTSelectSeedView(View): def run(self): + from seedsigner.controller import Controller + # Note: we can't just autoroute to the PSBT Overview because we might have a # multisig where we want to sign with more than one key on this device. if not self.controller.psbt: @@ -111,6 +107,7 @@ class PSBTOverviewView(View): def run(self): + from seedsigner.gui.screens.psbt_screens import PSBTOverviewScreen psbt_parser = self.controller.psbt_parser change_data = psbt_parser.change_data @@ -217,6 +214,7 @@ class PSBTMathView(View): + change value """ def run(self): + from seedsigner.gui.screens.psbt_screens import PSBTMathScreen psbt_parser: PSBTParser = self.controller.psbt_parser if not psbt_parser: # Should not be able to get here @@ -253,6 +251,7 @@ class PSBTAddressDetailsView(View): def run(self): + from seedsigner.gui.screens.psbt_screens import PSBTAddressDetailsScreen psbt_parser: PSBTParser = self.controller.psbt_parser if not psbt_parser: @@ -311,6 +310,7 @@ class PSBTChangeDetailsView(View): def run(self): + from seedsigner.gui.screens.psbt_screens import PSBTChangeDetailsScreen psbt_parser: PSBTParser = self.controller.psbt_parser if not psbt_parser: @@ -367,6 +367,9 @@ class PSBTChangeDetailsView(View): else: # Single sig try: + from embit import script + from embit.networks import NETWORKS + if is_change_derivation_path: loading_screen_text = _("Verifying Change...") else: @@ -442,6 +445,7 @@ class PSBTChangeDetailsView(View): return Destination(PSBTFinalizeView) elif button_data[selected_menu_num] == self.VERIFY_MULTISIG: + from seedsigner.controller import Controller from seedsigner.views.seed_views import LoadMultisigWalletDescriptorView self.controller.resume_main_flow = Controller.FLOW__PSBT return Destination(LoadMultisigWalletDescriptorView) @@ -484,6 +488,7 @@ class PSBTOpReturnView(View): Shows the OP_RETURN data """ def run(self): + from seedsigner.gui.screens.psbt_screens import PSBTOpReturnScreen psbt_parser: PSBTParser = self.controller.psbt_parser if not psbt_parser: @@ -514,6 +519,9 @@ class PSBTFinalizeView(View): def run(self): + from embit.psbt import PSBT + from seedsigner.gui.screens.psbt_screens import PSBTFinalizeScreen + psbt_parser: PSBTParser = self.controller.psbt_parser psbt: PSBT = self.controller.psbt self.APPROVE_PSBT = _("Approve PSBT") @@ -550,6 +558,8 @@ class PSBTFinalizeView(View): class PSBTSignedQRDisplayView(View): def run(self): + from seedsigner.models.encode_qr import UrPsbtQrEncoder + qr_encoder = UrPsbtQrEncoder( psbt=self.controller.psbt, qr_density=self.settings.get_value(SettingsConstants.SETTING__QR_DENSITY), diff --git a/src/seedsigner/views/scan_views.py b/src/seedsigner/views/scan_views.py index 067297f6..bf14a3cb 100644 --- a/src/seedsigner/views/scan_views.py +++ b/src/seedsigner/views/scan_views.py @@ -3,18 +3,13 @@ import re from gettext import gettext as _ -from embit.descriptor import Descriptor - -from seedsigner.gui.screens.screen import RET_CODE__BACK_BUTTON -from seedsigner.models.decode_qr import DecodeQR -from seedsigner.models.seed import Seed from seedsigner.models.settings import SettingsConstants -from seedsigner.views.settings_views import SettingsIngestSettingsQRView -from seedsigner.views.view import BackStackView, ErrorView, MainMenuView, NotYetImplementedView, OptionDisabledView, View, Destination +from seedsigner.views.view import BackStackView, ErrorView, MainMenuView, NotYetImplementedView, View, Destination logger = logging.getLogger(__name__) + class ScanView(View): """ The catch-all generic scanning View that will accept any of our supported QR @@ -29,6 +24,8 @@ class ScanView(View): def __init__(self): + from seedsigner.models.decode_qr import DecodeQR + super().__init__() # Define the decoder here to make it available to child classes' is_valid_qr_type # checks and so we can inject data into it in the test suite's `before_run()`. @@ -80,6 +77,7 @@ class ScanView(View): else: # Found a valid mnemonic seed! All new seeds should be considered # pending (might set a passphrase, SeedXOR, etc) until finalized. + from seedsigner.models.seed import Seed from .seed_views import SeedFinalizeView self.controller.storage.set_pending_seed( Seed(mnemonic=seed_mnemonic, wordlist_language_code=self.wordlist_language_code) @@ -98,10 +96,12 @@ class ScanView(View): return Destination(PSBTSelectSeedView, skip_current_view=True) elif self.decoder.is_settings: + from seedsigner.views.settings_views import SettingsIngestSettingsQRView data = self.decoder.get_settings_data() return Destination(SettingsIngestSettingsQRView, view_args=dict(data=data)) elif self.decoder.is_wallet_descriptor: + from embit.descriptor import Descriptor from seedsigner.views.seed_views import MultisigWalletDescriptorView descriptor_str = self.decoder.get_wallet_descriptor() diff --git a/src/seedsigner/views/screensaver.py b/src/seedsigner/views/screensaver.py index e75eca53..55c5cde0 100644 --- a/src/seedsigner/views/screensaver.py +++ b/src/seedsigner/views/screensaver.py @@ -4,7 +4,6 @@ import random import time from gettext import gettext as _ -from PIL import Image from seedsigner.gui.components import Fonts, GUIConstants, load_image from seedsigner.gui.screens.screen import BaseScreen @@ -37,6 +36,7 @@ class LogoScreen(BaseScreen): class OpeningSplashScreen(LogoScreen): def start(self): + from PIL import Image from seedsigner.controller import Controller controller = Controller.get_instance() @@ -95,6 +95,7 @@ class OpeningSplashScreen(LogoScreen): class ScreensaverScreen(LogoScreen): def __init__(self, buttons): + from PIL import Image super().__init__() self.buttons = buttons diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py index aab06ce1..014dd138 100644 --- a/src/seedsigner/views/seed_views.py +++ b/src/seedsigner/views/seed_views.py @@ -1,27 +1,18 @@ import logging -import embit import random import time from binascii import hexlify -from embit import bip39 -from embit.descriptor import Descriptor -from embit.networks import NETWORKS from gettext import gettext as _ -from typing import List -from seedsigner.controller import Controller +from embit.descriptor import Descriptor + from seedsigner.gui.components import FontAwesomeIconConstants, SeedSignerIconConstants -from seedsigner.helpers import embit_utils from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen, WarningScreen, DireWarningScreen, seed_screens) -from seedsigner.gui.screens.screen import LargeIconStatusScreen, QRDisplayScreen -from seedsigner.helpers import embit_utils -from seedsigner.models.decode_qr import DecodeQR from seedsigner.models.encode_qr import CompactSeedQrEncoder, GenericStaticQrEncoder, SeedQrEncoder, SpecterXPubQrEncoder, StaticXpubQrEncoder, UrXpubQrEncoder -from seedsigner.models.psbt_parser import PSBTParser from seedsigner.models.qr_type import QRType -from seedsigner.models.seed import InvalidSeedException, Seed +from seedsigner.models.seed import Seed from seedsigner.models.settings import Settings, SettingsConstants from seedsigner.models.settings_definition import SettingsDefinition from seedsigner.models.threads import BaseThread, ThreadsafeCounter @@ -30,6 +21,7 @@ from seedsigner.views.view import NotYetImplementedView, OptionDisabledView, Vie logger = logging.getLogger(__name__) + class SeedsMenuView(View): LOAD = "Load a seed" @@ -84,12 +76,16 @@ class SeedSelectSeedView(View): TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD) - def __init__(self, flow: str = Controller.FLOW__VERIFY_SINGLESIG_ADDR): + def __init__(self, flow: str = None): + from seedsigner.controller import Controller super().__init__() + if not flow: + flow = Controller.FLOW__VERIFY_SINGLESIG_ADDR self.flow = flow def run(self): + from seedsigner.controller import Controller seeds = self.controller.storage.seeds if self.flow == Controller.FLOW__VERIFY_SINGLESIG_ADDR: @@ -267,6 +263,7 @@ class SeedMnemonicEntryView(View): ) else: # Attempt to finalize the mnemonic + from seedsigner.models.seed import InvalidSeedException try: self.controller.storage.convert_pending_mnemonic_to_pending_seed() except InvalidSeedException: @@ -282,7 +279,7 @@ class SeedMnemonicInvalidView(View): def __init__(self): super().__init__() - self.mnemonic: List[str] = self.controller.storage.pending_mnemonic + self.mnemonic: list[str] = self.controller.storage.pending_mnemonic def run(self): @@ -538,6 +535,7 @@ class SeedOptionsView(View): def run(self): + from seedsigner.controller import Controller from seedsigner.views.psbt_views import PSBTOverviewView if self.controller.unverified_address: @@ -557,6 +555,7 @@ class SeedOptionsView(View): return Destination(SeedSignMessageConfirmMessageView, skip_current_view=True) if self.controller.psbt: + from seedsigner.models.psbt_parser import PSBTParser if PSBTParser.has_matching_input_fingerprint(self.controller.psbt, self.seed, network=self.settings.get_value(SettingsConstants.SETTING__NETWORK)): if self.controller.resume_main_flow and self.controller.resume_main_flow == Controller.FLOW__PSBT: # Re-route us directly back to the start of the PSBT flow @@ -708,6 +707,7 @@ class SeedExportXpubScriptTypeView(View): def run(self): + from seedsigner.controller import Controller from .tools_views import ToolsAddressExplorerAddressTypeView args = {"seed_num": self.seed_num, "sig_type": self.sig_type} @@ -777,6 +777,7 @@ class SeedExportXpubCustomDerivationView(View): def run(self): + from seedsigner.controller import Controller ret = self.run_screen( seed_screens.SeedExportXpubCustomDerivationScreen, initial_value=self.custom_derivation_path, @@ -911,6 +912,7 @@ class SeedExportXpubDetailsView(View): elif seed_derivation_override: derivation_path = seed_derivation_override else: + from seedsigner.helpers import embit_utils derivation_path = embit_utils.get_standard_derivation_path( network=self.settings.get_value(SettingsConstants.SETTING__NETWORK), wallet_type=self.sig_type, @@ -928,13 +930,15 @@ class SeedExportXpubDetailsView(View): self.loading_screen.start() try: + from embit.bip32 import HDKey + from embit.networks import NETWORKS embit_network = NETWORKS[SettingsConstants.map_network_to_embit(self.settings.get_value(SettingsConstants.SETTING__NETWORK))] version = self.seed.detect_version( derivation_path, self.settings.get_value(SettingsConstants.SETTING__NETWORK), self.sig_type ) - root = embit.bip32.HDKey.from_seed( + root = HDKey.from_seed( self.seed.seed_bytes, version=embit_network["xprv"] ) @@ -995,6 +999,7 @@ class SeedExportXpubQRDisplayView(View): def run(self): + from seedsigner.gui.screens.screen import QRDisplayScreen self.run_screen( QRDisplayScreen, qr_encoder=self.qr_encoder @@ -1249,7 +1254,7 @@ class SeedWordsBackupTestPromptView(View): class SeedWordsBackupTestView(View): - def __init__(self, seed_num: int, bip85_data: dict = None, confirmed_list: List[bool] = None, cur_index: int = None): + def __init__(self, seed_num: int, bip85_data: dict = None, confirmed_list: list[bool] = None, cur_index: int = None): super().__init__() self.seed_num = seed_num if self.seed_num is None: @@ -1271,6 +1276,8 @@ class SeedWordsBackupTestView(View): def run(self): + from embit import bip39 + if self.cur_index is None: self.cur_index = int(random.random() * len(self.mnemonic_list)) while self.cur_index in self.confirmed_list: @@ -1328,7 +1335,7 @@ class SeedWordsBackupTestMistakeView(View): REVIEW = _("Review Seed Words") RETRY = _("Try Again") - def __init__(self, seed_num: int, bip85_data: dict = None, cur_index: int = None, wrong_word: str = None, confirmed_list: List[bool] = None): + def __init__(self, seed_num: int, bip85_data: dict = None, cur_index: int = None, wrong_word: str = None, confirmed_list: list[bool] = None): super().__init__() self.seed_num = seed_num self.bip85_data = bip85_data @@ -1379,6 +1386,7 @@ class SeedWordsBackupTestSuccessView(View): self.seed_num = seed_num def run(self): + from seedsigner.gui.screens.screen import LargeIconStatusScreen LargeIconStatusScreen( title=_("Backup Verified"), show_back_button=False, @@ -1608,6 +1616,7 @@ class SeedTranscribeSeedQRConfirmScanView(View): def run(self): from seedsigner.gui.screens.scan_screens import ScanScreen + from seedsigner.models.decode_qr import DecodeQR # Run the live preview and QR code capture process # TODO: Does this belong in its own BaseThread? @@ -1634,6 +1643,7 @@ class SeedTranscribeSeedQRConfirmScanView(View): return Destination(BackStackView, skip_current_view=True) else: + from seedsigner.gui.screens.screen import LargeIconStatusScreen LargeIconStatusScreen( title=_("Confirm SeedQR"), status_headline=_("Success!"), @@ -1672,6 +1682,7 @@ class AddressVerificationStartView(View): def run(self): + from seedsigner.helpers import embit_utils if self.controller.unverified_address["script_type"] == SettingsConstants.LEGACY_P2PKH: # Legacy P2PKH addresses are always singlesig sig_type = SettingsConstants.SINGLE_SIG @@ -1689,6 +1700,7 @@ class AddressVerificationStartView(View): # Can jump straight to the brute-force verification View destination = Destination(SeedAddressVerificationView, skip_current_view=True) else: + from seedsigner.controller import Controller self.controller.resume_main_flow = Controller.FLOW__VERIFY_MULTISIG_ADDR destination = Destination(LoadMultisigWalletDescriptorView, skip_current_view=True) @@ -1718,6 +1730,8 @@ class AddressVerificationSigTypeView(View): MULTISIG = _("Multisig") def run(self): + from seedsigner.helpers import embit_utils + from seedsigner.controller import Controller button_data = [self.SINGLE_SIG, self.MULTISIG] selected_menu_num = self.run_screen( seed_screens.AddressVerificationSigTypeScreen, @@ -1909,6 +1923,7 @@ class SeedAddressVerificationView(View): def run(self): + from seedsigner.helpers import embit_utils while self.keep_running: if self.threadsafe_counter.cur_count % 10 == 0: logger.info(f"Incremented to {self.threadsafe_counter.cur_count}") @@ -1949,6 +1964,7 @@ class AddressVerificationSuccessView(View): def run(self): + from seedsigner.gui.screens.screen import LargeIconStatusScreen address = self.controller.unverified_address["address"] sig_type = self.controller.unverified_address["sig_type"] verified_index = self.controller.unverified_address["verified_index"] @@ -2000,6 +2016,7 @@ class LoadMultisigWalletDescriptorView(View): return Destination(ScanWalletDescriptorView) elif button_data[selected_menu_num] == self.CANCEL: + from seedsigner.controller import Controller if self.controller.resume_main_flow == Controller.FLOW__PSBT: return Destination(BackStackView) else: @@ -2026,6 +2043,7 @@ class MultisigWalletDescriptorView(View): button_data = [self.OK] if self.controller.resume_main_flow: + from seedsigner.controller import Controller if self.controller.resume_main_flow == Controller.FLOW__PSBT: button_data = [self.RETURN] elif self.controller.resume_main_flow == Controller.FLOW__VERIFY_MULTISIG_ADDR and self.controller.unverified_address: @@ -2074,6 +2092,7 @@ class SeedSignMessageStartView(View): load a seed first. """ def __init__(self, derivation_path: str, message: str): + from seedsigner.helpers import embit_utils super().__init__() self.derivation_path = derivation_path self.message = message @@ -2119,6 +2138,7 @@ class SeedSignMessageStartView(View): # We already know which seed we're signing with self.set_redirect(Destination(SeedSignMessageConfirmMessageView, skip_current_view=True)) else: + from seedsigner.controller import Controller self.set_redirect(Destination(SeedSelectSeedView, view_args=dict(flow=Controller.FLOW__SIGN_MESSAGE), skip_current_view=True)) @@ -2159,6 +2179,7 @@ class SeedSignMessageConfirmMessageView(View): class SeedSignMessageConfirmAddressView(View): def __init__(self): + from seedsigner.helpers import embit_utils super().__init__() data = self.controller.sign_message_data seed_num = data.get("seed_num") @@ -2220,6 +2241,7 @@ class SeedSignMessageSignedMessageQRView(View): Displays the signed message as a QR code. """ def __init__(self): + from seedsigner.helpers import embit_utils super().__init__() data = self.controller.sign_message_data @@ -2232,6 +2254,7 @@ class SeedSignMessageSignedMessageQRView(View): def run(self): + from seedsigner.gui.screens.screen import QRDisplayScreen qr_encoder = GenericStaticQrEncoder(data=self.signed_message) self.run_screen( diff --git a/src/seedsigner/views/settings_views.py b/src/seedsigner/views/settings_views.py index 5284fac6..a48314cf 100644 --- a/src/seedsigner/views/settings_views.py +++ b/src/seedsigner/views/settings_views.py @@ -1,17 +1,13 @@ -from gettext import gettext as _ import logging -from seedsigner.gui.components import SeedSignerIconConstants -from seedsigner.hardware.microsd import MicroSD - -from .view import View, Destination, MainMenuView +from gettext import gettext as _ from seedsigner.gui.components import SeedSignerIconConstants from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen, settings_screens) from seedsigner.models.settings import Settings, SettingsConstants, SettingsDefinition -logger = logging.getLogger(__name__) +from .view import View, Destination, MainMenuView -from .view import View, Destination, BackStackView, MainMenuView +logger = logging.getLogger(__name__) @@ -194,6 +190,7 @@ class SettingsEntryUpdateSelectionView(View): class SettingsIngestSettingsQRView(View): def __init__(self, data: str): + from seedsigner.hardware.microsd import MicroSD super().__init__() # May raise an Exception which will bubble up to the Controller to display to the diff --git a/src/seedsigner/views/tools_views.py b/src/seedsigner/views/tools_views.py index 7679dd91..fd94dfc2 100644 --- a/src/seedsigner/views/tools_views.py +++ b/src/seedsigner/views/tools_views.py @@ -1,22 +1,13 @@ -from dataclasses import dataclass import hashlib import logging import os import time from gettext import gettext as _ -from embit.descriptor import Descriptor -from PIL import Image -from PIL.ImageOps import autocontrast -from seedsigner.controller import Controller from seedsigner.gui.components import FontAwesomeIconConstants, GUIConstants, SeedSignerIconConstants -from seedsigner.gui.screens import (RET_CODE__BACK_BUTTON, ButtonListScreen, WarningScreen) -from seedsigner.gui.screens.tools_screens import (ToolsCalcFinalWordDoneScreen, ToolsCalcFinalWordFinalizePromptScreen, - ToolsCalcFinalWordScreen, ToolsCoinFlipEntryScreen, ToolsDiceEntropyEntryScreen, ToolsImageEntropyFinalImageScreen, - ToolsImageEntropyLivePreviewScreen, ToolsAddressExplorerAddressTypeScreen) -from seedsigner.helpers import embit_utils, mnemonic_generation -from seedsigner.models.encode_qr import GenericStaticQrEncoder +from seedsigner.gui.screens import RET_CODE__BACK_BUTTON, ButtonListScreen +from seedsigner.helpers import mnemonic_generation from seedsigner.models.seed import Seed from seedsigner.models.settings_definition import SettingsConstants from seedsigner.views.seed_views import SeedDiscardView, SeedFinalizeView, SeedMnemonicEntryView, SeedOptionsView, SeedWordsWarningView, SeedExportXpubScriptTypeView @@ -26,6 +17,7 @@ from .view import View, Destination, BackStackView logger = logging.getLogger(__name__) + class ToolsMenuView(View): IMAGE = (_("New seed"), FontAwesomeIconConstants.CAMERA) DICE = (_("New seed"), FontAwesomeIconConstants.DICE) @@ -69,6 +61,7 @@ class ToolsMenuView(View): ****************************************************************************""" class ToolsImageEntropyLivePreviewView(View): def run(self): + from seedsigner.gui.screens.tools_screens import ToolsImageEntropyLivePreviewScreen self.controller.image_entropy_preview_frames = None ret = ToolsImageEntropyLivePreviewScreen().display() @@ -82,6 +75,9 @@ class ToolsImageEntropyLivePreviewView(View): class ToolsImageEntropyFinalImageView(View): def run(self): + from PIL import Image + from PIL.ImageOps import autocontrast + from seedsigner.gui.screens.tools_screens import ToolsImageEntropyFinalImageScreen if not self.controller.image_entropy_final_image: from seedsigner.hardware.camera import Camera # Take the final full-res image @@ -224,6 +220,7 @@ class ToolsDiceEntropyEntryView(View): def run(self): + from seedsigner.gui.screens.tools_screens import ToolsDiceEntropyEntryScreen ret = ToolsDiceEntropyEntryScreen( return_after_n_chars=self.total_rolls, ).display() @@ -288,6 +285,7 @@ class ToolsCalcFinalWordFinalizePromptView(View): ZEROS = _("Finalize with zeros") def run(self): + from seedsigner.gui.screens.tools_screens import ToolsCalcFinalWordFinalizePromptScreen mnemonic = self.controller.storage.pending_mnemonic mnemonic_length = len(mnemonic) if mnemonic_length == 12: @@ -324,6 +322,7 @@ class ToolsCalcFinalWordFinalizePromptView(View): class ToolsCalcFinalWordCoinFlipsView(View): def run(self): + from seedsigner.gui.screens.tools_screens import ToolsCoinFlipEntryScreen mnemonic_length = len(self.controller.storage.pending_mnemonic) if mnemonic_length == 12: @@ -398,6 +397,7 @@ class ToolsCalcFinalWordShowFinalWordView(View): def run(self): + from seedsigner.gui.screens.tools_screens import ToolsCalcFinalWordScreen button_data = [self.NEXT] selected_menu_num = self.run_screen( ToolsCalcFinalWordScreen, @@ -423,6 +423,7 @@ class ToolsCalcFinalWordDoneView(View): DISCARD = (_("Discard"), None, None, "red") def run(self): + from seedsigner.gui.screens.tools_screens import ToolsCalcFinalWordDoneScreen mnemonic = self.controller.storage.pending_mnemonic mnemonic_word_length = len(mnemonic) final_word = mnemonic[-1] @@ -460,6 +461,8 @@ class ToolsAddressExplorerSelectSourceView(View): TYPE_ELECTRUM = (_("Enter Electrum seed"), FontAwesomeIconConstants.KEYBOARD) def run(self): + from seedsigner.controller import Controller + seeds = self.controller.storage.seeds button_data = [] for seed in seeds: @@ -558,6 +561,7 @@ class ToolsAddressExplorerAddressTypeView(View): elif seed_derivation_override: derivation_path = seed_derivation_override else: + from seedsigner.helpers import embit_utils derivation_path = embit_utils.get_standard_derivation_path( network=self.settings.get_value(SettingsConstants.SETTING__NETWORK), wallet_type=SettingsConstants.SINGLE_SIG, @@ -574,6 +578,7 @@ class ToolsAddressExplorerAddressTypeView(View): def run(self): + from seedsigner.gui.screens.tools_screens import ToolsAddressExplorerAddressTypeScreen data = self.controller.address_explorer_data wallet_descriptor_display_name = None @@ -638,6 +643,7 @@ class ToolsAddressExplorerAddressListView(View): else: try: from seedsigner.gui.screens.screen import LoadingScreenThread + from seedsigner.helpers import embit_utils # TRANSLATOR_NOTE: a status message that our payment addresses are being calculated self.loading_screen = LoadingScreenThread(text=_("Calculating addrs...")) self.loading_screen.start() @@ -658,6 +664,7 @@ class ToolsAddressExplorerAddressListView(View): raise Exception(_("Custom Derivation address explorer not yet implemented")) elif "wallet_descriptor" in data: + from embit.descriptor import Descriptor descriptor: Descriptor = data["wallet_descriptor"] if descriptor.is_basic_multisig: for i in range(self.start_index, self.start_index + addrs_per_screen): @@ -726,6 +733,8 @@ class ToolsAddressExplorerAddressView(View): def run(self): from seedsigner.gui.screens.screen import QRDisplayScreen + from seedsigner.models.encode_qr import GenericStaticQrEncoder + qr_encoder = GenericStaticQrEncoder(data=self.address) self.run_screen( QRDisplayScreen,