diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py
index a963e13f..7505408c 100644
--- a/src/seedsigner/controller.py
+++ b/src/seedsigner/controller.py
@@ -147,6 +147,16 @@ class Controller(Singleton):
else:
# Instantiate the one and only Controller instance
return cls.configure_instance()
+
+
+ @classmethod
+ def reset_instance(cls):
+ """
+ Currently used by the screenshot generator, but could potentially be used to
+ wipe and reset the state of the device.
+ """
+ cls._instance = None
+ cls.configure_instance()
@classmethod
diff --git a/src/seedsigner/gui/toast.py b/src/seedsigner/gui/toast.py
index b9e53fbe..baefa16b 100644
--- a/src/seedsigner/gui/toast.py
+++ b/src/seedsigner/gui/toast.py
@@ -189,12 +189,16 @@ class BaseToastOverlayManagerThread(BaseThread):
class RemoveSDCardToastManagerThread(BaseToastOverlayManagerThread):
- def __init__(self, activation_delay=3):
- # Note: activation_delay is configurable so the screenshot generator can get the
- # toast to immediately render.
+ def __init__(self, activation_delay: int = 3, duration: int = 1e6):
+ """
+ * activation_delay: configurable so the screenshot generator can get the
+ toast to immediately render.
+ * duration: default value is essentially forever. Overrideable for the
+ screenshot generator.
+ """
super().__init__(
- activation_delay=activation_delay, # seconds
- duration=1e6, # seconds ("forever")
+ activation_delay=activation_delay,
+ duration=duration,
)
diff --git a/src/seedsigner/views/seed_views.py b/src/seedsigner/views/seed_views.py
index 0832a3f8..a02e8b4c 100644
--- a/src/seedsigner/views/seed_views.py
+++ b/src/seedsigner/views/seed_views.py
@@ -77,11 +77,8 @@ class SeedSelectSeedView(View):
TYPE_ELECTRUM = ButtonOption("Enter Electrum seed", FontAwesomeIconConstants.KEYBOARD)
- def __init__(self, flow: str = None):
- from seedsigner.controller import Controller
+ def __init__(self, flow: str):
super().__init__()
- if not flow:
- flow = Controller.FLOW__VERIFY_SINGLESIG_ADDR
self.flow = flow
@@ -572,6 +569,7 @@ class SeedOptionsView(View):
button_data = []
if self.controller.unverified_address:
+ # TODO: Verify that an addr verification flow can actually reach this code
addr = self.controller.unverified_address["address"][:7]
self.VERIFY_ADDRESS.button_label += f" {addr}"
button_data.append(self.VERIFY_ADDRESS)
@@ -1701,7 +1699,7 @@ class AddressVerificationStartView(View):
if self.controller.unverified_address["script_type"] == SettingsConstants.LEGACY_P2PKH:
# Legacy P2PKH addresses are always singlesig
sig_type = SettingsConstants.SINGLE_SIG
- destination = Destination(SeedSelectSeedView, skip_current_view=True)
+ destination = Destination(SeedSelectSeedView, view_args=dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR), skip_current_view=True)
if self.controller.unverified_address["script_type"] == SettingsConstants.NESTED_SEGWIT:
# No way to differentiate single sig from multisig
@@ -1721,7 +1719,7 @@ class AddressVerificationStartView(View):
else:
sig_type = SettingsConstants.SINGLE_SIG
- destination = Destination(SeedSelectSeedView, skip_current_view=True)
+ destination = Destination(SeedSelectSeedView, view_args=dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR), skip_current_view=True)
elif self.controller.unverified_address["script_type"] == SettingsConstants.TAPROOT:
# TODO: add Taproot support
@@ -2158,10 +2156,6 @@ class SeedSignMessageConfirmMessageView(View):
super().__init__()
self.page_num = page_num # Note: zero-indexed numbering!
- self.seed_num = self.controller.sign_message_data.get("seed_num")
- if self.seed_num is None:
- raise Exception("Routing error: seed_num hasn't been set")
-
def run(self):
from seedsigner.gui.screens.seed_screens import SeedSignMessageConfirmMessageScreen
diff --git a/tests/screenshot_generator/generator.py b/tests/screenshot_generator/generator.py
index e41551a4..bf71d3df 100644
--- a/tests/screenshot_generator/generator.py
+++ b/tests/screenshot_generator/generator.py
@@ -37,7 +37,7 @@ from seedsigner.views import (MainMenuView, PowerOptionsView, RestartView, NotYe
from seedsigner.views.screensaver import OpeningSplashView
from seedsigner.views.view import ErrorView, NetworkMismatchErrorView, OptionDisabledView, PowerOffView, View
-from .utils import ScreenshotComplete, ScreenshotRenderer
+from .utils import ScreenshotComplete, ScreenshotConfig, ScreenshotRenderer
import warnings; warnings.warn = lambda *args, **kwargs: None
@@ -58,6 +58,51 @@ def test_generate_all(locale, target_locale):
+"""**************************************************************************************
+ Set up global test data that will be re-used across a variety of screenshots and for
+ all locales.
+**************************************************************************************"""
+BASE64_PSBT_1 = """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"""
+mnemonic_12b = ["abandon"] * 11 + ["about"]
+seed_12b = Seed(mnemonic=mnemonic_12b, wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
+
+def add_op_return_to_psbt(psbt: PSBT, raw_payload_data: bytes):
+ data = (compact.to_bytes(OPCODES.OP_RETURN) +
+ compact.to_bytes(OPCODES.OP_PUSHDATA1) +
+ compact.to_bytes(len(raw_payload_data)) +
+ raw_payload_data)
+ script = Script(data)
+ output = OutputScope()
+ output.script_pubkey = script
+ output.value = 0
+ psbt.outputs.append(output)
+ return psbt.to_string()
+
+# Prep a PSBT with a human-readable OP_RETURN
+raw_payload_data = "Chancellor on the brink of third bailout for banks".encode()
+psbt = PSBT.from_base64(BASE64_PSBT_1)
+
+# Simplify the output side
+output = psbt.outputs[-1]
+psbt.outputs.clear()
+psbt.outputs.append(output)
+assert len(psbt.outputs) == 1
+BASE64_PSBT_WITH_OP_RETURN_TEXT = add_op_return_to_psbt(psbt, raw_payload_data)
+
+# Prep a PSBT with a (repeatably) random 80-byte OP_RETURN
+random.seed(6102)
+BASE64_PSBT_WITH_OP_RETURN_RAW_BYTES = add_op_return_to_psbt(PSBT.from_base64(BASE64_PSBT_1), random.randbytes(80))
+
+mnemonic_12 = "forum undo fragile fade shy sign arrest garment culture tube off merit".split()
+mnemonic_24 = "attack pizza motion avocado network gather crop fresh patrol unusual wild holiday candy pony ranch winter theme error hybrid van cereal salon goddess expire".split()
+seed_12 = Seed(mnemonic=mnemonic_12, passphrase="cap*BRACKET3stove", wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
+seed_24 = Seed(mnemonic=mnemonic_24, passphrase="some-PASS*phrase9", wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
+seed_24_w_passphrase = Seed(mnemonic=mnemonic_24, passphrase="some-PASS*phrase9", wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
+
+MULTISIG_WALLET_DESCRIPTOR = """wsh(sortedmulti(1,[22bde1a9/48h/1h/0h/2h]tpubDFfsBrmpj226ZYiRszYi2qK6iGvh2vkkghfGB2YiRUVY4rqqedHCFEgw12FwDkm7rUoVtq9wLTKc6BN2sxswvQeQgp7m8st4FP8WtP8go76/{0,1}/*,[73c5da0a/48h/1h/0h/2h]tpubDFH9dgzveyD8zTbPUFuLrGmCydNvxehyNdUXKJAQN8x4aZ4j6UZqGfnqFrD4NqyaTVGKbvEW54tsvPTK2UoSbCC1PJY8iCNiwTL3RWZEheQ/{0,1}/*))#3jhtf6yx"""
+
+
+
def generate_screenshots(locale):
"""
The `Renderer` class is mocked so that calls in the normal code are ignored
@@ -75,69 +120,21 @@ def generate_screenshots(locale):
Renderer.configure_instance = Mock()
Renderer.get_instance = Mock(return_value=screenshot_renderer)
- controller = Controller.get_instance()
- BASE64_PSBT_1 = """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"""
- mnemonic_12b = ["abandon"] * 11 + ["about"]
- seed_12b = Seed(mnemonic=mnemonic_12b, wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
-
- def add_op_return_to_psbt(psbt: PSBT, raw_payload_data: bytes):
- data = (compact.to_bytes(OPCODES.OP_RETURN) +
- compact.to_bytes(OPCODES.OP_PUSHDATA1) +
- compact.to_bytes(len(raw_payload_data)) +
- raw_payload_data)
- script = Script(data)
- output = OutputScope()
- output.script_pubkey = script
- output.value = 0
- psbt.outputs.append(output)
- return psbt.to_string()
-
- # Prep a PSBT with a human-readable OP_RETURN
- raw_payload_data = "Chancellor on the brink of third bailout for banks".encode()
- psbt = PSBT.from_base64(BASE64_PSBT_1)
-
- # Simplify the output side
- output = psbt.outputs[-1]
- psbt.outputs.clear()
- psbt.outputs.append(output)
- assert len(psbt.outputs) == 1
- BASE64_PSBT_WITH_OP_RETURN_TEXT = add_op_return_to_psbt(psbt, raw_payload_data)
-
- # Prep a PSBT with a (repeatably) random 80-byte OP_RETURN
- random.seed(6102)
- BASE64_PSBT_WITH_OP_RETURN_RAW_BYTES = add_op_return_to_psbt(PSBT.from_base64(BASE64_PSBT_1), random.randbytes(80))
-
-
- def setup_screenshots(locale: str) -> dict:
- # Set up some test data that we'll need in the `Controller` for certain Views
+ def setup_screenshots(locale: str) -> dict[str, list[ScreenshotConfig]]:
+ """ Set up some test data that we'll need in the `Controller` for certain Views """
+ # Must reset the Controller so each locale gets a fresh start
+ Controller.reset_instance()
controller = Controller.get_instance()
controller.settings.set_value(SettingsConstants.SETTING__SIG_TYPES, [attr for attr, name in SettingsConstants.ALL_SIG_TYPES])
controller.settings.set_value(SettingsConstants.SETTING__SCRIPT_TYPES, [attr for attr, name in SettingsConstants.ALL_SCRIPT_TYPES])
- mnemonic_12 = "forum undo fragile fade shy sign arrest garment culture tube off merit".split()
- mnemonic_24 = "attack pizza motion avocado network gather crop fresh patrol unusual wild holiday candy pony ranch winter theme error hybrid van cereal salon goddess expire".split()
- seed_12 = Seed(mnemonic=mnemonic_12, passphrase="cap*BRACKET3stove", wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
- seed_24 = Seed(mnemonic=mnemonic_24, passphrase="some-PASS*phrase9", wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
- seed_24_w_passphrase = Seed(mnemonic=mnemonic_24, passphrase="some-PASS*phrase9", wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
controller.storage.seeds.append(seed_12)
controller.storage.seeds.append(seed_12b)
controller.storage.seeds.append(seed_24)
controller.storage.set_pending_seed(seed_24_w_passphrase)
- # Seed: Verify Address data
- controller.unverified_address = dict(
- # These are all totally fake data
- address="bc1q6p00wazu4nnqac29fvky6vhjnnhku5u2g9njss62rvy7e0yuperq86f5ek",
- network=SettingsConstants.MAINNET,
- sig_type=SettingsConstants.SINGLE_SIG,
- script_type=SettingsConstants.NATIVE_SEGWIT,
- derivation_path = "m/84h/0h/0h",
- verified_index=5,
- verified_index_is_change=False
- )
-
# Pending mnemonic for ToolsCalcFinalWordShowFinalWordView
controller.storage.init_pending_mnemonic(num_words=12)
for i, word in enumerate(mnemonic_12[:11]):
@@ -150,10 +147,6 @@ def generate_screenshots(locale):
controller.psbt = decoder.get_psbt()
controller.psbt_seed = seed_12b
- # Multisig wallet descriptor for the multisig in the above PSBT
- MULTISIG_WALLET_DESCRIPTOR = """wsh(sortedmulti(1,[22bde1a9/48h/1h/0h/2h]tpubDFfsBrmpj226ZYiRszYi2qK6iGvh2vkkghfGB2YiRUVY4rqqedHCFEgw12FwDkm7rUoVtq9wLTKc6BN2sxswvQeQgp7m8st4FP8WtP8go76/{0,1}/*,[73c5da0a/48h/1h/0h/2h]tpubDFH9dgzveyD8zTbPUFuLrGmCydNvxehyNdUXKJAQN8x4aZ4j6UZqGfnqFrD4NqyaTVGKbvEW54tsvPTK2UoSbCC1PJY8iCNiwTL3RWZEheQ/{0,1}/*))#3jhtf6yx"""
- controller.multisig_wallet_descriptor = embit.descriptor.Descriptor.from_string(MULTISIG_WALLET_DESCRIPTOR)
-
# Message signing data
derivation_path = "m/84h/0h/0h/0/0"
controller.sign_message_data = {
@@ -165,17 +158,18 @@ def generate_screenshots(locale):
# Automatically populate all Settings options Views
settings_views_list = []
- settings_views_list.append(settings_views.SettingsMenuView)
- settings_views_list.append((
- settings_views.SettingsMenuView,
- dict(
- visibility=SettingsConstants.VISIBILITY__ADVANCED,
- selected_attr=SettingsConstants.SETTING__ELECTRUM_SEEDS,
- initial_scroll=240, # Just guessing how many pixels to scroll down
- ),
- "SettingsMenuView__Advanced"
- ))
-
+ settings_views_list.append(ScreenshotConfig(settings_views.SettingsMenuView))
+ settings_views_list.append(
+ ScreenshotConfig(
+ settings_views.SettingsMenuView,
+ dict(
+ visibility=SettingsConstants.VISIBILITY__ADVANCED,
+ selected_attr=SettingsConstants.SETTING__ELECTRUM_SEEDS,
+ initial_scroll=240, # Just guessing how many pixels to scroll down
+ ),
+ screenshot_name="SettingsMenuView__Advanced"
+ )
+ )
# so we get a choice for transcribe seed qr format
controller.settings.set_value(
@@ -186,135 +180,183 @@ def generate_screenshots(locale):
if settings_entry.visibility == SettingsConstants.VISIBILITY__HIDDEN:
continue
- settings_views_list.append((settings_views.SettingsEntryUpdateSelectionView, dict(attr_name=settings_entry.attr_name), f"SettingsEntryUpdateSelectionView_{settings_entry.attr_name}"))
+ settings_views_list.append(ScreenshotConfig(settings_views.SettingsEntryUpdateSelectionView, dict(attr_name=settings_entry.attr_name), screenshot_name=f"SettingsEntryUpdateSelectionView_{settings_entry.attr_name}"))
settingsqr_data_persistent = f"settings::v1 name=English_noob_mode persistent=E coords=spa,spd denom=thr network=M qr_density=M xpub_export=E sigs=ss scripts=nat xpub_details=E passphrase=E camera=0 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E locale={locale}"
settingsqr_data_not_persistent = f"settings::v1 name=Mode_Ephemeral persistent=D coords=spa,spd denom=thr network=M qr_density=M xpub_export=E sigs=ss scripts=nat xpub_details=E passphrase=E camera=0 compact_seedqr=E bip85=D priv_warn=E dire_warn=E partners=E locale={locale}"
+
+ # Set up screenshot-specific callbacks to inject data before the View is run and
+ # reset data after the View is run.
+ def load_basic_psbt_cb():
+ decoder = DecodeQR()
+ decoder.add_data(BASE64_PSBT_1)
+ controller.psbt = decoder.get_psbt()
+ controller.psbt_seed = seed_12b
+ controller.multisig_wallet_descriptor = None
+
+
+ def load_multisig_wallet_descriptor_cb():
+ controller.multisig_wallet_descriptor = embit.descriptor.Descriptor.from_string(MULTISIG_WALLET_DESCRIPTOR)
+
+
+ def load_address_verification_data_cb():
+ controller.unverified_address = dict(
+ # These are all totally fake data
+ address="bc1q6p00wazu4nnqac29fvky6vhjnnhku5u2g9njss62rvy7e0yuperq86f5ek",
+ network=SettingsConstants.MAINNET,
+ sig_type=SettingsConstants.SINGLE_SIG,
+ script_type=SettingsConstants.NATIVE_SEGWIT,
+ derivation_path = "m/84h/0h/0h",
+ verified_index=5,
+ verified_index_is_change=False
+ )
+
+
+ def PSBTSelectSeedView_cb_before():
+ # Have to ensure this is cleared out in order to get the seed selection screen
+ controller.psbt_seed = None
+
+
+ def PSBTOverviewView_op_return_cb_before():
+ controller.psbt_seed = seed_12b
+ decoder = DecodeQR()
+ decoder.add_data(BASE64_PSBT_WITH_OP_RETURN_TEXT)
+ controller.psbt = decoder.get_psbt()
+ controller.psbt_parser = PSBTParser(p=controller.psbt, seed=seed_12b)
+
+
+ def PSBTOpReturnView_raw_hex_data_cb_before():
+ decoder.add_data(BASE64_PSBT_WITH_OP_RETURN_RAW_BYTES)
+ controller.psbt = decoder.get_psbt()
+ controller.psbt_parser = PSBTParser(p=controller.psbt, seed=seed_12b)
+
+
screenshot_sections = {
"Main Menu Views": [
- (OpeningSplashView, dict(is_screenshot_renderer=True, force_partner_logos=True)),
- (OpeningSplashView, dict(is_screenshot_renderer=True, force_partner_logos=False), "OpeningSplashView_no_partner_logos"),
- MainMenuView,
- (MainMenuView, {}, 'MainMenuView_SDCardStateChangeToast_removed', SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__REMOVED)),
- (MainMenuView, {}, 'MainMenuView_SDCardStateChangeToast_inserted', SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__INSERTED)),
- (MainMenuView, {}, 'MainMenuView_RemoveSDCardToast', RemoveSDCardToastManagerThread(activation_delay=0)),
- PowerOptionsView,
- RestartView,
- PowerOffView,
+ ScreenshotConfig(OpeningSplashView, dict(is_screenshot_renderer=True, force_partner_logos=True)),
+ ScreenshotConfig(OpeningSplashView, dict(is_screenshot_renderer=True, force_partner_logos=False), screenshot_name="OpeningSplashView_no_partner_logos"),
+ ScreenshotConfig(MainMenuView),
+ ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SDCardStateChangeToast_removed', toast_thread=SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__REMOVED, activation_delay=0, duration=0)),
+ ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_SDCardStateChangeToast_inserted', toast_thread=SDCardStateChangeToastManagerThread(action=MicroSD.ACTION__INSERTED, activation_delay=0, duration=0)),
+ ScreenshotConfig(MainMenuView, screenshot_name='MainMenuView_RemoveSDCardToast', toast_thread=RemoveSDCardToastManagerThread(activation_delay=0, duration=0)),
+ ScreenshotConfig(PowerOptionsView),
+ ScreenshotConfig(RestartView),
+ ScreenshotConfig(PowerOffView),
],
"Seed Views": [
- seed_views.SeedsMenuView,
- seed_views.LoadSeedView,
- seed_views.SeedMnemonicEntryView,
- seed_views.SeedMnemonicInvalidView,
- seed_views.SeedFinalizeView,
- (seed_views.SeedAddPassphraseView, {}, "SeedAddPassphraseView_lowercase"),
- (seed_views.SeedAddPassphraseView, dict(initial_keyboard=SeedAddPassphraseScreen.KEYBOARD__UPPERCASE_BUTTON_TEXT), "SeedAddPassphraseView_uppercase"),
- (seed_views.SeedAddPassphraseView, dict(initial_keyboard=SeedAddPassphraseScreen.KEYBOARD__DIGITS_BUTTON_TEXT), "SeedAddPassphraseView_digits"),
- (seed_views.SeedAddPassphraseView, dict(initial_keyboard=SeedAddPassphraseScreen.KEYBOARD__SYMBOLS_1_BUTTON_TEXT), "SeedAddPassphraseView_symbols_1"),
- (seed_views.SeedAddPassphraseView, dict(initial_keyboard=SeedAddPassphraseScreen.KEYBOARD__SYMBOLS_2_BUTTON_TEXT), "SeedAddPassphraseView_symbols_2"),
- seed_views.SeedAddPassphraseExitDialogView,
- seed_views.SeedReviewPassphraseView,
+ ScreenshotConfig(seed_views.SeedsMenuView),
+ ScreenshotConfig(seed_views.LoadSeedView),
+ ScreenshotConfig(seed_views.SeedMnemonicEntryView),
+ ScreenshotConfig(seed_views.SeedMnemonicInvalidView),
+ ScreenshotConfig(seed_views.SeedFinalizeView),
+ ScreenshotConfig(seed_views.SeedAddPassphraseView, screenshot_name="SeedAddPassphraseView_lowercase"),
+ ScreenshotConfig(seed_views.SeedAddPassphraseView, dict(initial_keyboard=SeedAddPassphraseScreen.KEYBOARD__UPPERCASE_BUTTON_TEXT), screenshot_name="SeedAddPassphraseView_uppercase"),
+ ScreenshotConfig(seed_views.SeedAddPassphraseView, dict(initial_keyboard=SeedAddPassphraseScreen.KEYBOARD__DIGITS_BUTTON_TEXT), screenshot_name="SeedAddPassphraseView_digits"),
+ ScreenshotConfig(seed_views.SeedAddPassphraseView, dict(initial_keyboard=SeedAddPassphraseScreen.KEYBOARD__SYMBOLS_1_BUTTON_TEXT), screenshot_name="SeedAddPassphraseView_symbols_1"),
+ ScreenshotConfig(seed_views.SeedAddPassphraseView, dict(initial_keyboard=SeedAddPassphraseScreen.KEYBOARD__SYMBOLS_2_BUTTON_TEXT), screenshot_name="SeedAddPassphraseView_symbols_2"),
+ ScreenshotConfig(seed_views.SeedAddPassphraseExitDialogView),
+ ScreenshotConfig(seed_views.SeedReviewPassphraseView),
- (seed_views.SeedOptionsView, dict(seed_num=0)),
- (seed_views.SeedBackupView, dict(seed_num=0)),
- (seed_views.SeedExportXpubSigTypeView, dict(seed_num=0)),
- (seed_views.SeedExportXpubScriptTypeView, dict(seed_num=0, sig_type="msig")),
- (seed_views.SeedExportXpubCustomDerivationView, dict(seed_num=0, sig_type="ss", script_type="")),
- (seed_views.SeedExportXpubCoordinatorView, dict(seed_num=0, sig_type="ss", script_type="nat")),
- (seed_views.SeedExportXpubWarningView, dict(seed_num=0, sig_type="msig", script_type="nes", coordinator="spd", custom_derivation="")),
- (seed_views.SeedExportXpubDetailsView, dict(seed_num=0, sig_type="ss", script_type="nat", coordinator="bw", custom_derivation="")),
- #SeedExportXpubQRDisplayView,
- (seed_views.SeedWordsWarningView, dict(seed_num=0)),
- (seed_views.SeedWordsView, dict(seed_num=0)),
- (seed_views.SeedWordsView, dict(seed_num=0, page_index=2), "SeedWordsView_2"),
- (seed_views.SeedBIP85ApplicationModeView, dict(seed_num=0)),
- (seed_views.SeedBIP85SelectChildIndexView, dict(seed_num=0, num_words=24)),
- (seed_views.SeedBIP85InvalidChildIndexView, dict(seed_num=0, num_words=12)),
- (seed_views.SeedWordsBackupTestPromptView, dict(seed_num=0)),
- (seed_views.SeedWordsBackupTestView, dict(seed_num=0, rand_seed=6102)),
- (seed_views.SeedWordsBackupTestMistakeView, dict(seed_num=0, cur_index=7, wrong_word="satoshi")),
- (seed_views.SeedWordsBackupTestSuccessView, dict(seed_num=0)),
- (seed_views.SeedTranscribeSeedQRFormatView, dict(seed_num=0)),
- (seed_views.SeedTranscribeSeedQRWarningView, dict(seed_num=0)),
- (seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=0, seedqr_format=QRType.SEED__COMPACTSEEDQR, num_modules=21), "SeedTranscribeSeedQRWholeQRView_12_Compact"),
- (seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=0, seedqr_format=QRType.SEED__SEEDQR, num_modules=25), "SeedTranscribeSeedQRWholeQRView_12_Standard"),
- (seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=2, seedqr_format=QRType.SEED__COMPACTSEEDQR, num_modules=25), "SeedTranscribeSeedQRWholeQRView_24_Compact"),
- (seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=2, seedqr_format=QRType.SEED__SEEDQR, num_modules=29), "SeedTranscribeSeedQRWholeQRView_24_Standard"),
- (seed_views.SeedTranscribeSeedQRZoomedInView, dict(seed_num=0, seedqr_format=QRType.SEED__COMPACTSEEDQR, initial_block_x=1, initial_block_y=1), "SeedTranscribeSeedQRZoomedInView_12_Compact"),
- (seed_views.SeedTranscribeSeedQRZoomedInView, dict(seed_num=0, seedqr_format=QRType.SEED__SEEDQR, initial_block_x=2, initial_block_y=2), "SeedTranscribeSeedQRZoomedInView_12_Standard"),
+ ScreenshotConfig(seed_views.SeedOptionsView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedBackupView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedExportXpubSigTypeView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedExportXpubScriptTypeView, dict(seed_num=0, sig_type="msig")),
+ ScreenshotConfig(seed_views.SeedExportXpubCustomDerivationView, dict(seed_num=0, sig_type="ss", script_type="")),
+ ScreenshotConfig(seed_views.SeedExportXpubCoordinatorView, dict(seed_num=0, sig_type="ss", script_type="nat")),
+ ScreenshotConfig(seed_views.SeedExportXpubWarningView, dict(seed_num=0, sig_type="msig", script_type="nes", coordinator="spd", custom_derivation="")),
+ ScreenshotConfig(seed_views.SeedExportXpubDetailsView, dict(seed_num=0, sig_type="ss", script_type="nat", coordinator="bw", custom_derivation="")),
+ #ScreenshotConfig(SeedExportXpubQRDisplayView),
+ ScreenshotConfig(seed_views.SeedWordsWarningView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedWordsView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedWordsView, dict(seed_num=0, page_index=2), screenshot_name="SeedWordsView_2"),
+ ScreenshotConfig(seed_views.SeedBIP85ApplicationModeView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedBIP85SelectChildIndexView, dict(seed_num=0, num_words=24)),
+ ScreenshotConfig(seed_views.SeedBIP85InvalidChildIndexView, dict(seed_num=0, num_words=12)),
+ ScreenshotConfig(seed_views.SeedWordsBackupTestPromptView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedWordsBackupTestView, dict(seed_num=0, rand_seed=6102)),
+ ScreenshotConfig(seed_views.SeedWordsBackupTestMistakeView, dict(seed_num=0, cur_index=7, wrong_word="satoshi")),
+ ScreenshotConfig(seed_views.SeedWordsBackupTestSuccessView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedTranscribeSeedQRFormatView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedTranscribeSeedQRWarningView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=0, seedqr_format=QRType.SEED__COMPACTSEEDQR, num_modules=21), screenshot_name="SeedTranscribeSeedQRWholeQRView_12_Compact"),
+ ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=0, seedqr_format=QRType.SEED__SEEDQR, num_modules=25), screenshot_name="SeedTranscribeSeedQRWholeQRView_12_Standard"),
+ ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=2, seedqr_format=QRType.SEED__COMPACTSEEDQR, num_modules=25), screenshot_name="SeedTranscribeSeedQRWholeQRView_24_Compact"),
+ ScreenshotConfig(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=2, seedqr_format=QRType.SEED__SEEDQR, num_modules=29), screenshot_name="SeedTranscribeSeedQRWholeQRView_24_Standard"),
+ ScreenshotConfig(seed_views.SeedTranscribeSeedQRZoomedInView, dict(seed_num=0, seedqr_format=QRType.SEED__COMPACTSEEDQR, initial_block_x=1, initial_block_y=1), screenshot_name="SeedTranscribeSeedQRZoomedInView_12_Compact"),
+ ScreenshotConfig(seed_views.SeedTranscribeSeedQRZoomedInView, dict(seed_num=0, seedqr_format=QRType.SEED__SEEDQR, initial_block_x=2, initial_block_y=2), screenshot_name="SeedTranscribeSeedQRZoomedInView_12_Standard"),
- (seed_views.SeedTranscribeSeedQRConfirmQRPromptView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmQRPromptView, dict(seed_num=0)),
# Screenshot can't render live preview screens
- # (seed_views.SeedTranscribeSeedQRConfirmScanView, dict(seed_num=0)),
+ # ScreenshotConfig(seed_views.SeedTranscribeSeedQRConfirmScanView, dict(seed_num=0)),
- seed_views.SeedSelectSeedView,
- seed_views.AddressVerificationSigTypeView,
- (seed_views.SeedAddressVerificationView, dict(seed_num=0)),
- (seed_views.SeedAddressVerificationSuccessView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.SeedSelectSeedView, dict(flow=Controller.FLOW__VERIFY_SINGLESIG_ADDR), screenshot_name="SeedSelectSeedView_address_verification"),
+ ScreenshotConfig(seed_views.AddressVerificationSigTypeView),
+ ScreenshotConfig(seed_views.SeedAddressVerificationView, dict(seed_num=0), run_before=load_address_verification_data_cb),
+ ScreenshotConfig(seed_views.SeedAddressVerificationSuccessView, dict(seed_num=0)), # Relies on callback above
- seed_views.LoadMultisigWalletDescriptorView,
- seed_views.MultisigWalletDescriptorView,
- (seed_views.SeedDiscardView, dict(seed_num=0)),
+ ScreenshotConfig(seed_views.LoadMultisigWalletDescriptorView),
+ ScreenshotConfig(seed_views.MultisigWalletDescriptorView, run_before=load_multisig_wallet_descriptor_cb),
+ ScreenshotConfig(seed_views.SeedDiscardView, dict(seed_num=0)),
- seed_views.SeedSignMessageConfirmMessageView,
- seed_views.SeedSignMessageConfirmAddressView,
+ ScreenshotConfig(seed_views.SeedSelectSeedView, dict(flow=Controller.FLOW__SIGN_MESSAGE), screenshot_name="SeedSelectSeedView_sign_message"),
+ ScreenshotConfig(seed_views.SeedSignMessageConfirmMessageView),
+ ScreenshotConfig(seed_views.SeedSignMessageConfirmAddressView),
- seed_views.SeedElectrumMnemonicStartView,
+ ScreenshotConfig(seed_views.SeedElectrumMnemonicStartView),
],
"PSBT Views": [
- psbt_views.PSBTSelectSeedView, # this will fail, be rerun below
- psbt_views.PSBTOverviewView,
- psbt_views.PSBTUnsupportedScriptTypeWarningView,
- psbt_views.PSBTNoChangeWarningView,
- psbt_views.PSBTMathView,
- (psbt_views.PSBTAddressDetailsView, dict(address_num=0)),
+ ScreenshotConfig(psbt_views.PSBTSelectSeedView, run_before=PSBTSelectSeedView_cb_before),
+ ScreenshotConfig(psbt_views.PSBTOverviewView, run_before=load_basic_psbt_cb),
+ ScreenshotConfig(psbt_views.PSBTUnsupportedScriptTypeWarningView),
+ ScreenshotConfig(psbt_views.PSBTNoChangeWarningView),
+ ScreenshotConfig(psbt_views.PSBTMathView),
+ ScreenshotConfig(psbt_views.PSBTAddressDetailsView, dict(address_num=0)),
- (NotYetImplementedView, {}, "PSBTChangeDetailsView_multisig_unverified"), # Must manually re-run this below
- (psbt_views.PSBTChangeDetailsView, dict(change_address_num=0), "PSBTChangeDetailsView_multisig_verified"),
- (NotYetImplementedView, {}, "PSBTOverviewView_op_return"), # Placeholder
- (NotYetImplementedView, {}, "PSBTOpReturnView_text"), # Placeholder
- (NotYetImplementedView, {}, "PSBTOpReturnView_raw_hex_data"), # Placeholder
- (psbt_views.PSBTAddressVerificationFailedView, dict(is_change=True, is_multisig=False), "PSBTAddressVerificationFailedView_singlesig_change"),
- (psbt_views.PSBTAddressVerificationFailedView, dict(is_change=False, is_multisig=False), "PSBTAddressVerificationFailedView_singlesig_selftransfer"),
- (psbt_views.PSBTAddressVerificationFailedView, dict(is_change=True, is_multisig=True), "PSBTAddressVerificationFailedView_multisig_change"),
- (psbt_views.PSBTAddressVerificationFailedView, dict(is_change=False, is_multisig=True), "PSBTAddressVerificationFailedView_multisig_selftransfer"),
- psbt_views.PSBTFinalizeView,
- #PSBTSignedQRDisplayView
- psbt_views.PSBTSigningErrorView,
+ ScreenshotConfig(psbt_views.PSBTChangeDetailsView, dict(change_address_num=0), screenshot_name="PSBTChangeDetailsView_multisig_unverified", run_before=load_basic_psbt_cb),
+ ScreenshotConfig(psbt_views.PSBTChangeDetailsView, dict(change_address_num=0), screenshot_name="PSBTChangeDetailsView_multisig_verified", run_before=load_multisig_wallet_descriptor_cb),
+ ScreenshotConfig(psbt_views.PSBTOverviewView, screenshot_name="PSBTOverviewView_op_return", run_before=PSBTOverviewView_op_return_cb_before),
+ ScreenshotConfig(psbt_views.PSBTOpReturnView, screenshot_name="PSBTOpReturnView_text"), # Relies on callback above
+ ScreenshotConfig(psbt_views.PSBTOpReturnView, screenshot_name="PSBTOpReturnView_raw_hex_data", run_before=PSBTOpReturnView_raw_hex_data_cb_before),
+ ScreenshotConfig(psbt_views.PSBTAddressVerificationFailedView, dict(is_change=True, is_multisig=False), screenshot_name="PSBTAddressVerificationFailedView_singlesig_change"),
+ ScreenshotConfig(psbt_views.PSBTAddressVerificationFailedView, dict(is_change=False, is_multisig=False), screenshot_name="PSBTAddressVerificationFailedView_singlesig_selftransfer"),
+ ScreenshotConfig(psbt_views.PSBTAddressVerificationFailedView, dict(is_change=True, is_multisig=True), screenshot_name="PSBTAddressVerificationFailedView_multisig_change"),
+ ScreenshotConfig(psbt_views.PSBTAddressVerificationFailedView, dict(is_change=False, is_multisig=True), screenshot_name="PSBTAddressVerificationFailedView_multisig_selftransfer"),
+ ScreenshotConfig(psbt_views.PSBTFinalizeView),
+ #ScreenshotConfig(PSBTSignedQRDisplayViewScreenshotConfig),
+ ScreenshotConfig(psbt_views.PSBTSigningErrorView),
],
"Tools Views": [
- tools_views.ToolsMenuView,
- #ToolsImageEntropyLivePreviewView
- #ToolsImageEntropyFinalImageView
- tools_views.ToolsImageEntropyMnemonicLengthView,
- tools_views.ToolsDiceEntropyMnemonicLengthView,
- (tools_views.ToolsDiceEntropyEntryView, dict(total_rolls=50)),
- tools_views.ToolsCalcFinalWordNumWordsView,
- tools_views.ToolsCalcFinalWordFinalizePromptView,
- tools_views.ToolsCalcFinalWordCoinFlipsView,
- (tools_views.ToolsCalcFinalWordShowFinalWordView, {}, "ToolsCalcFinalWordShowFinalWordView_pick_word"),
- (tools_views.ToolsCalcFinalWordShowFinalWordView, dict(coin_flips="0010101"), "ToolsCalcFinalWordShowFinalWordView_coin_flips"),
- tools_views.ToolsCalcFinalWordDoneView,
- tools_views.ToolsAddressExplorerSelectSourceView,
- tools_views.ToolsAddressExplorerAddressTypeView,
- tools_views.ToolsAddressExplorerAddressListView,
- # tools_views.ToolsAddressExplorerAddressView,
+ ScreenshotConfig(tools_views.ToolsMenuView),
+ #ScreenshotConfig(ToolsImageEntropyLivePreviewView),
+ #ScreenshotConfig(ToolsImageEntropyFinalImageView),
+ ScreenshotConfig(tools_views.ToolsImageEntropyMnemonicLengthView),
+ ScreenshotConfig(tools_views.ToolsDiceEntropyMnemonicLengthView),
+ ScreenshotConfig(tools_views.ToolsDiceEntropyEntryView, dict(total_rolls=50)),
+ ScreenshotConfig(tools_views.ToolsCalcFinalWordNumWordsView),
+ ScreenshotConfig(tools_views.ToolsCalcFinalWordFinalizePromptView),
+ ScreenshotConfig(tools_views.ToolsCalcFinalWordCoinFlipsView),
+ ScreenshotConfig(tools_views.ToolsCalcFinalWordShowFinalWordView, screenshot_name="ToolsCalcFinalWordShowFinalWordView_pick_word"),
+ ScreenshotConfig(tools_views.ToolsCalcFinalWordShowFinalWordView, dict(coin_flips="0010101"), screenshot_name="ToolsCalcFinalWordShowFinalWordView_coin_flips"),
+ ScreenshotConfig(tools_views.ToolsCalcFinalWordDoneView),
+ ScreenshotConfig(tools_views.ToolsAddressExplorerSelectSourceView),
+ ScreenshotConfig(tools_views.ToolsAddressExplorerAddressTypeView),
+ ScreenshotConfig(tools_views.ToolsAddressExplorerAddressListView),
+ # ScreenshotConfig(tools_views.ToolsAddressExplorerAddressView),
],
"Settings Views": settings_views_list + [
- settings_views.IOTestView,
- settings_views.DonateView,
- (settings_views.SettingsIngestSettingsQRView, dict(data=settingsqr_data_persistent), "SettingsIngestSettingsQRView_persistent"),
- (settings_views.SettingsIngestSettingsQRView, dict(data=settingsqr_data_not_persistent), "SettingsIngestSettingsQRView_not_persistent"),
+ ScreenshotConfig(settings_views.IOTestView),
+ ScreenshotConfig(settings_views.DonateView),
+ ScreenshotConfig(settings_views.SettingsIngestSettingsQRView, dict(data=settingsqr_data_persistent), screenshot_name="SettingsIngestSettingsQRView_persistent"),
+ ScreenshotConfig(settings_views.SettingsIngestSettingsQRView, dict(data=settingsqr_data_not_persistent), screenshot_name="SettingsIngestSettingsQRView_not_persistent"),
],
"Misc Error Views": [
- NotYetImplementedView,
- (UnhandledExceptionView, dict(error=["IndexError", "line 1, in some_buggy_code.py", "list index out of range"])),
- (NetworkMismatchErrorView, dict(derivation_path="m/84'/1'/0'")),
- (OptionDisabledView, dict(settings_attr=SettingsConstants.SETTING__MESSAGE_SIGNING)),
- (ErrorView, dict(
+ ScreenshotConfig(NotYetImplementedView),
+ ScreenshotConfig(UnhandledExceptionView, dict(error=["IndexError", "line 1, in some_buggy_code.py", "list index out of range"])),
+ ScreenshotConfig(NetworkMismatchErrorView, dict(derivation_path="m/84'/1'/0'")),
+ ScreenshotConfig(OptionDisabledView, dict(settings_attr=SettingsConstants.SETTING__MESSAGE_SIGNING)),
+ ScreenshotConfig(ErrorView, dict(
title="Error",
status_headline="Unknown QR Type",
text="QRCode is invalid or is a data format not yet supported.",
@@ -326,15 +368,22 @@ def generate_screenshots(locale):
return screenshot_sections
- def screencap_view(view_cls: View, view_args: dict = {}, view_name: str = None, toast_thread: BaseToastOverlayManagerThread = None):
- if not view_name:
- view_name = view_cls.__name__
- screenshot_renderer.set_screenshot_filename(f"{view_name}.png")
+ def screencap_view(screenshot_config: ScreenshotConfig):
+ # Block until we have exclusive access to the screenshot renderer. Without this
+ # we were occasionally running into confusing race conditions where the next
+ # screenshot would begin rendering over the previous one. Claiming the lock
+ # guarantees that the previous screenshot has been fully rendered and saved.
+ screenshot_renderer.lock.acquire()
+ screenshot_renderer.set_screenshot_filename(f"{screenshot_config.screenshot_name}.png")
+ screenshot_renderer.lock.release()
+
controller = Controller.get_instance()
+ toast_thread = screenshot_config.toast_thread
try:
- print(f"Running {view_name}")
+ print(f"Running {screenshot_config.screenshot_name}")
try:
- view_cls(**view_args).run()
+ screenshot_config.run_callback_before()
+ screenshot_config.View_cls(**screenshot_config.view_kwargs).run()
except ScreenshotComplete:
# The target View has run and its Screen has rendered what it needs to
if toast_thread is not None:
@@ -342,20 +391,29 @@ def generate_screenshots(locale):
controller.activate_toast(toast_thread)
while controller.toast_notification_thread.is_alive():
# Give the Toast a moment to complete its work
+
time.sleep(0.01)
+
+ # TODO: Necessary now that the lock is in place?
+ # Whenever possible, clean up toast thread HERE before killing the
+ # main thread with ScreenshotComplete.
+ toast_thread.stop()
+ toast_thread.join()
raise ScreenshotComplete()
except ScreenshotComplete:
# Slightly hacky way to exit ScreenshotRenderer as expected
- print(f"Completed {view_name}")
+ print(f"Completed {screenshot_config.screenshot_name}")
except Exception as e:
# Something else went wrong
from traceback import print_exc
print_exc()
raise e
finally:
- if toast_thread:
+ if toast_thread and toast_thread.is_alive():
toast_thread.stop()
- time.sleep(0.1) #jdlcdl
+ toast_thread.join()
+
+ screenshot_config.run_callback_after()
# Parse the main `l10n/messages.pot` for overall stats
@@ -370,7 +428,6 @@ def generate_screenshots(locale):
locale, display_name = locale_tuple_list[0]
Settings.get_instance().set_value(SettingsConstants.SETTING__LOCALE, value=locale)
- screenshot_renderer.set_screenshot_path(os.path.join(screenshot_root, locale))
locale_readme = f"""# SeedSigner Screenshots: {display_name}\n"""
@@ -396,57 +453,14 @@ def generate_screenshots(locale):
locale_readme += f"## {section_name}\n\n"
locale_readme += """
"""
locale_readme += f""""""
- for screenshot in screenshot_list:
- if type(screenshot) == tuple:
- toast_thread = None
- if len(screenshot) == 2:
- view_cls, view_args = screenshot
- view_name = view_cls.__name__
- elif len(screenshot) == 3:
- view_cls, view_args, view_name = screenshot
- elif len(screenshot) == 4:
- view_cls, view_args, view_name, toast_thread = screenshot
- else:
- view_cls = screenshot
- view_args = {}
- view_name = view_cls.__name__
- toast_thread = None
-
- screencap_view(view_cls, view_args, view_name, toast_thread=toast_thread)
+ for screenshot_config in screenshot_list:
+ screencap_view(screenshot_config)
locale_readme += """ """
- locale_readme += f"""{view_name}
 | """
+ locale_readme += f"""{screenshot_config.screenshot_name}
 | """
locale_readme += """ \n"""
locale_readme += " |
"
- # many screens don't work, leaving a missing image, re-run here for now
- controller.psbt_seed = None
- screenshot_renderer.set_screenshot_path(os.path.join(screenshot_root, locale, "psbt_views"))
-
- decoder = DecodeQR()
- decoder.add_data(BASE64_PSBT_1)
- controller.psbt = decoder.get_psbt()
- controller.psbt_seed = seed_12b
- controller.multisig_wallet_descriptor = None
- screencap_view(psbt_views.PSBTChangeDetailsView, view_name='PSBTChangeDetailsView_multisig_unverified', view_args=dict(change_address_num=0))
-
- controller.psbt_seed = None
- screencap_view(psbt_views.PSBTSelectSeedView, view_name='PSBTSelectSeedView')
-
- # Render OP_RETURN screens for real
- controller.psbt_seed = seed_12b
- decoder = DecodeQR()
- decoder.add_data(BASE64_PSBT_WITH_OP_RETURN_TEXT)
- controller.psbt = decoder.get_psbt()
- controller.psbt_parser = PSBTParser(p=controller.psbt, seed=seed_12b)
- screencap_view(psbt_views.PSBTOverviewView, view_name='PSBTOverviewView_op_return')
- screencap_view(psbt_views.PSBTOpReturnView, view_name="PSBTOpReturnView_text")
-
- decoder.add_data(BASE64_PSBT_WITH_OP_RETURN_RAW_BYTES)
- controller.psbt = decoder.get_psbt()
- controller.psbt_parser = PSBTParser(p=controller.psbt, seed=seed_12b)
- screencap_view(psbt_views.PSBTOpReturnView, view_name="PSBTOpReturnView_raw_hex_data")
-
with open(os.path.join(screenshot_root, locale, "README.md"), 'w') as readme_file:
readme_file.write(locale_readme)
diff --git a/tests/screenshot_generator/utils.py b/tests/screenshot_generator/utils.py
index 26d232a4..d767bdf2 100644
--- a/tests/screenshot_generator/utils.py
+++ b/tests/screenshot_generator/utils.py
@@ -1,12 +1,19 @@
import os
+
+from dataclasses import dataclass
from PIL import Image, ImageDraw
+
from seedsigner.gui.renderer import Renderer
+from seedsigner.gui.toast import BaseToastOverlayManagerThread
+from seedsigner.views.view import View
+
class ScreenshotComplete(Exception):
pass
+
class ScreenshotRenderer(Renderer):
screenshot_path: str = None
screenshot_filename: str = None
@@ -35,7 +42,7 @@ class ScreenshotRenderer(Renderer):
self.screenshot_path = path
- def show_image(self, image=None, alpha_overlay=None, is_background_thread: bool = False):
+ def show_image(self, image=None, alpha_overlay=None, is_background_thread: bool = False):
if is_background_thread:
return
@@ -51,3 +58,30 @@ class ScreenshotRenderer(Renderer):
self.canvas.save(os.path.join(self.screenshot_path, self.screenshot_filename))
raise ScreenshotComplete()
+
+
+@dataclass
+class ScreenshotConfig:
+ View_cls: View
+ view_kwargs: dict = None
+ screenshot_name: str = None
+ toast_thread: BaseToastOverlayManagerThread = None
+ run_before: callable = None
+ run_after: callable = None
+
+
+ def __post_init__(self):
+ if not self.view_kwargs:
+ self.view_kwargs = {}
+ if not self.screenshot_name:
+ self.screenshot_name = self.View_cls.__name__
+
+
+ def run_callback_before(self):
+ if self.run_before:
+ self.run_before()
+
+
+ def run_callback_after(self):
+ if self.run_after:
+ self.run_after()