From 36b04b6dbc7ba6b16206e10095133f8a000f0479 Mon Sep 17 00:00:00 2001 From: Kaustubh Patange Date: Sat, 14 Feb 2026 15:38:59 +0530 Subject: [PATCH 01/10] Add simulator for local development (#1) * Simulator that works on macos * fork simulator from https://github.com/ltcmweb/seedsigner * Fix camera exposure and screen size * resize and crop + revert rbga in read_capture * remove padding * move gpio to different module * restore modules for dev mode * merge camera * extract local display --------- Co-authored-by: Hector Chu Co-authored-by: Nick Klockenga --- .gitignore | 3 +- README.md | 1 + requirements.txt | 10 +- src/gui.py | 149 +++++++++++++++ src/seedsigner/controller.py | 13 +- src/seedsigner/extras/dev.py | 4 + src/seedsigner/extras/gpio.py | 49 +++++ src/seedsigner/extras/local_camera.py | 164 ++++++++++++++++ src/seedsigner/extras/local_display.py | 8 + src/seedsigner/gui/components.py | 2 +- src/seedsigner/gui/renderer.py | 1 - src/seedsigner/hardware/buttons.py | 10 +- src/seedsigner/hardware/camera.py | 177 +++++++++--------- .../hardware/displays/display_driver.py | 10 +- src/seedsigner/models/decode_qr.py | 3 + src/seedsigner/models/encode_qr.py | 5 +- src/seedsigner/models/psbt_parser.py | 2 +- 17 files changed, 505 insertions(+), 106 deletions(-) create mode 100644 src/gui.py create mode 100644 src/seedsigner/extras/dev.py create mode 100644 src/seedsigner/extras/gpio.py create mode 100644 src/seedsigner/extras/local_camera.py create mode 100644 src/seedsigner/extras/local_display.py diff --git a/.gitignore b/.gitignore index 9c70709f3..5239a8763 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ src/seedsigner/models/settings_definition.json .coverage* *.po -*.mo \ No newline at end of file +*.mo +*.bmp diff --git a/README.md b/README.md index 171bd6713..05a730e41 100644 --- a/README.md +++ b/README.md @@ -358,3 +358,4 @@ See the [SeedSigner OS repo](https://github.com/SeedSigner/seedsigner-os/) for i # Developer Local Build Instructions Raspberry Pi OS is commonly used for development. See the [Raspberry Pi OS Build Instructions](docs/raspberry_pi_os_build_instructions.md) +Desktop build for development on macos. See the [TODO] diff --git a/requirements.txt b/requirements.txt index d7a20d593..c0b75601e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,7 @@ -embit==0.8.0 -Pillow==10.0.1 +embit +Pillow pyzbar @ git+https://github.com/seedsigner/pyzbar.git@c3c237821c6a20b17953efe59b90df0b514a1c03 -qrcode==7.3.1 -urtypes==1.0.1 +qrcode +urtypes +numpy +opencv-python diff --git a/src/gui.py b/src/gui.py new file mode 100644 index 000000000..d10c0d01f --- /dev/null +++ b/src/gui.py @@ -0,0 +1,149 @@ +import os +import numpy as np +import socket +import threading +import time + +from tkinter import * +from PIL import Image, ImageTk + +from seedsigner.hardware.buttons import HardwareButtons + + +class Display: + def __init__(self, width: int = 240, height: int = 240): + self.width = width + self.height = height + + self.setup_sock() + self.run() + + def run(self): + self.root = Tk() + self.root.title("SeedSigner") + + self.root.geometry(f"{self.width*2}x{self.height}+240+240") + self.root.resizable(False, False) + self.root.configure(bg='orange') + self.root.attributes("-topmost", True) + + self.label=Label(self.root) + self.label.pack() + + self.joystick=Frame(self.root) + self.joystick.pack() + self.joystick.place(x=20, y=85) + self.joystick.configure(bg='orange') + + pixel = PhotoImage(width=1, height=1) + + self.btnL = Button(self.joystick, image=pixel, width=20, height=20, command=HardwareButtons.KEY_LEFT_PIN) + self.btnL.grid(row=1, column=0) + self.bindButtonClick(self.btnL) + + self.btnR = Button(self.joystick, image=pixel, width=20, height=20, command=HardwareButtons.KEY_RIGHT_PIN) + self.btnR.grid(row=1, column=2) + self.bindButtonClick(self.btnR) + + self.btnC = Button(self.joystick, image=pixel, width=20, height=20, command=HardwareButtons.KEY_PRESS_PIN) + self.btnC.grid(row=1, column=1) + self.bindButtonClick(self.btnC) + + self.btnU = Button(self.joystick, image=pixel, width=20, height=20, command=HardwareButtons.KEY_UP_PIN) + self.btnU.grid(row=0, column=1) + self.bindButtonClick(self.btnU) + + self.btnD = Button(self.joystick, image=pixel, width=20, height=20, command=HardwareButtons.KEY_DOWN_PIN) + self.btnD.grid(row=2, column=1) + self.bindButtonClick(self.btnD) + + self.btn1 = Button(self.root, image=pixel, width=40, height=20, command=HardwareButtons.KEY1_PIN) + self.btn1.place(x=self.width+160, y=60) + self.bindButtonClick(self.btn1) + + self.btn2 = Button(self.root, image=pixel, width=40, height=20, command=HardwareButtons.KEY2_PIN) + self.btn2.place(x=self.width+160, y=116) + self.bindButtonClick(self.btn2) + + self.btn3 = Button(self.root, image=pixel, width=40, height=20, command=HardwareButtons.KEY3_PIN) + self.btn3.place(x=self.width+160, y=172) + self.bindButtonClick(self.btn3) + + def key_handler(event): + if event.keysym == "Up": self.key_press(HardwareButtons.KEY_UP_PIN) + if event.keysym == "Down": self.key_press(HardwareButtons.KEY_DOWN_PIN) + if event.keysym == "Left": self.key_press(HardwareButtons.KEY_LEFT_PIN) + if event.keysym == "Right": self.key_press(HardwareButtons.KEY_RIGHT_PIN) + + if event.keysym in ("1", "KP_1"): self.key_press(HardwareButtons.KEY1_PIN) + if event.keysym in ("2", "KP_2"): self.key_press(HardwareButtons.KEY2_PIN) + if event.keysym in ("3", "KP_3"): self.key_press(HardwareButtons.KEY3_PIN) + + if event.keysym == "Return": self.key_press(HardwareButtons.KEY_PRESS_PIN) + + self.root.bind("", key_handler) + + self.periodic_update() + self.root.mainloop() + + def key_press(self, key): + self.set_input(key, 0) + time.sleep(0.1) + self.set_input(key, 1) + + def bindButtonClick(self, btn): + btn.bind("