mirror of
https://github.com/curly60e/pyblock.git
synced 2026-08-13 12:33:15 +02:00
Major improvements across 7 areas: - Add centralized Config singleton (config.py) replacing ~176 config reloads per session with a single cached load - Add logging framework (log.py) with RotatingFileHandler, replacing silent except Exception: pass blocks with structured logging - Refactor menu system (menu.py) with data-driven color selection, eliminating ~1,370 lines of duplicate menu functions - Create shared/ modules extracting 7 utility functions duplicated between PyBlock.py and SPV/spvblock.py - Clean dependencies: pin all versions, remove stdlib packages (asyncio, threading), remove unused imports - Improve Docker: pin ubuntu:24.04, add non-root user, use venv - Improve CI: update to actions v4/v5, add test job before publish - Fix entry point: wrap main loop in def main(), proper module import Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
90 lines
2.7 KiB
Python
90 lines
2.7 KiB
Python
"""
|
|
Data-driven menu system for PyBLOCK.
|
|
|
|
Replaces 80+ duplicate menu functions with a composable Menu class.
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Callable, Optional
|
|
|
|
|
|
COLOR_MAP = {
|
|
"A": "black", "B": "red", "C": "green", "D": "yellow",
|
|
"E": "blue", "F": "magenta", "G": "cyan", "H": "white", "I": "gray",
|
|
}
|
|
|
|
COLOR_DISPLAY = """
|
|
\033[1;30;40mA.\033[0;37;40m Black
|
|
\033[1;31;40mB.\033[0;37;40m Red
|
|
\033[1;32;40mC.\033[0;37;40m Green
|
|
\033[1;33;40mD.\033[0;37;40m Yellow
|
|
\033[1;34;40mE.\033[0;37;40m Blue
|
|
\033[1;35;40mF.\033[0;37;40m Magenta
|
|
\033[1;36;40mG.\033[0;37;40m Cyan
|
|
\033[1;37;40mH.\033[0;37;40m White
|
|
\033[0;37;40mI.\033[0;37;40m Gray
|
|
\033[1;31;40mR.\033[0;37;40m <<< Back
|
|
"""
|
|
|
|
|
|
@dataclass
|
|
class MenuItem:
|
|
key: str
|
|
label: str
|
|
action: Callable
|
|
color: str = "\033[0;37;40m"
|
|
modes: tuple = ("local", "remote", "onchain_only")
|
|
|
|
|
|
@dataclass
|
|
class Menu:
|
|
title: str
|
|
items: list = field(default_factory=list)
|
|
header_fn: Optional[Callable] = None
|
|
show_sysinfo: bool = True
|
|
|
|
def display(self, mode="local", clear_fn=None, logo_fn=None, sysinfo_fn=None):
|
|
if clear_fn:
|
|
clear_fn()
|
|
if logo_fn:
|
|
logo_fn()
|
|
if self.show_sysinfo and sysinfo_fn:
|
|
sysinfo_fn()
|
|
|
|
if self.header_fn:
|
|
self.header_fn()
|
|
|
|
visible = [i for i in self.items if mode in i.modes]
|
|
for item in visible:
|
|
print(f" {item.color}{item.key}.\033[0;37;40m {item.label}")
|
|
print("\n\n\x1b[?25h")
|
|
|
|
def run(self, mode="local", clear_fn=None, logo_fn=None, sysinfo_fn=None):
|
|
self.display(mode, clear_fn, logo_fn, sysinfo_fn)
|
|
choice = input("\033[1;32;40mSelect option: \033[0;37;40m")
|
|
visible = [i for i in self.items if mode in i.modes]
|
|
for item in visible:
|
|
if choice.lower() == item.key.lower():
|
|
item.action()
|
|
return True
|
|
return False
|
|
|
|
|
|
def select_color(settings_dict, key, on_select_fn, back_fn):
|
|
"""Generic color selection that replaces ~20 duplicate color menu functions.
|
|
|
|
Args:
|
|
settings_dict: The settings dictionary to modify (settings or settingsClock)
|
|
key: The key to set ("colorA" or "colorB")
|
|
on_select_fn: Function to call after selecting a color (testlogo/testlogoRB)
|
|
back_fn: Function to call when user presses R (back)
|
|
"""
|
|
print(COLOR_DISPLAY)
|
|
choice = input("\033[1;32;40mSelect color: \033[0;37;40m")
|
|
upper = choice.upper()
|
|
if upper == "R":
|
|
back_fn()
|
|
return
|
|
if upper in COLOR_MAP:
|
|
settings_dict[key] = COLOR_MAP[upper]
|
|
on_select_fn()
|