diff --git a/docs/emulator_setup.md b/docs/emulator_setup.md new file mode 100644 index 00000000..66883dca --- /dev/null +++ b/docs/emulator_setup.md @@ -0,0 +1,10 @@ +# Emulator Setup + +## Create/Enter virtualenv +mkvirtualenv seedsigner-gui + +## Pip installs +pip install -r requirements.txt + +## Install Tkinter +sudo apt install python3-tk diff --git a/docs/touchscreen_setup.md b/docs/touchscreen_setup.md new file mode 100644 index 00000000..d162b7eb --- /dev/null +++ b/docs/touchscreen_setup.md @@ -0,0 +1,295 @@ +# Initial Pi Setup +touch /media/skorn/boot/ssh + +nano /media/skorn/boot/wpa_supplicant.conf +``` +country=us +update_config=1 +ctrl_interface=/var/run/wpa_supplicant + +network={ + scan_ssid=1 + ssid="MyNetworkSSID" + psk="Pa55w0rd1234" +} +``` + +https://wirelessthings.io/index.php/2021/05/01/720x720-lcd-screen-using-dpi-on-raspberry-pi-4/ + + + +Download and extract overlay files: +https://www.waveshare.com/wiki/File:28DPIB_DTBO.zip + +copy files to /media/user/boot/overlays +cp ~/Downloads/28DPI_DTBO/waveshare-28dpi-* /media/skorn/boot/overlays + + + +```bash +nano /media/skorn/boot/config.txt +``` + +``` +# Update existing +#dtoverlay=vc4-kms-v3d +dtparam=i2c_arm=on +dtparam=i2s=on +dtparam=spi=on + +# Add new +gpio=0-9=a2 +gpio=12-17=a2 +gpio=20-25=a2 +dtoverlay=dpi24 +enable_dpi_lcd=1 +display_default_lcd=1 +extra_transpose_buffer=2 +dpi_group=2 +dpi_mode=87 +dpi_output_format=0x7F216 +hdmi_timings=480 0 26 16 10 640 0 25 10 15 0 0 0 60 0 32000000 1 +dtoverlay=waveshare-28dpi-3b-4b +dtoverlay=waveshare-28dpi-3b +dtoverlay=waveshare-28dpi-4b +display_rotate=1 +``` + + +sudo raspi-config + +set to boot to desktop +set screen resolution: DMT 4 480 x 640 +expand filesystem + + +# rotate display +sudo apt install xserver-xorg-input-libinput +sudo mkdir /etc/X11/xorg.conf.d +sudo cp /usr/share/X11/xorg.conf.d/40-libinput.conf /etc/X11/xorg.conf.d/ +sudo nano /etc/X11/xorg.conf.d/40-libinput.conf +``` +# add to touchscreen section +Option "CalibrationMatrix" "0 1 0 -1 0 1 0 0 1" +``` + +# current seedsigner application installs +sudo apt-get update && sudo apt-get install -y \ +python3-pip \ +python-pil \ +libopenjp2-7 \ +git \ +libatlas-base-dev \ +qrencode \ +python3-opencv \ +python3-numpy \ +python3-picamera + +sudo apt-get update && sudo apt-get install -y wiringpi python3-pip \ + python3-numpy python-pil libopenjp2-7 git python3-opencv \ + python3-picamera libatlas-base-dev qrencode + + +apt-get install python-tk + +sudo apt install imagemagick + + +pip install pillow tk pyautogui + + +sudo apt-get install -y python3-pip \ + python3-numpy libopenjp2-7 git python3-opencv \ + python3-picamera libatlas-base-dev qrencode + + +# Tkinter +sudo apt install python3-tk + +# OPENBOX +sudo apt install openbox xinit lightdm + + +To run Openbox from the commandline, setup the .xinitrc file in your home directory and insert the following line: + +exec openbox-session + + +application startup config +/etc/xdg/openbox/autostart +``` +. /home/pi/env/bin/activate +cd /home/pi/seedsigner/src +nohup python3 main.py > /home/pi/seedsigner.log & +``` + + +# Lightdm +sudo apt install lightdm + +https://gist.github.com/kuanyui/2972b15639e78940cd1349844ca0e4fc + +sudo nano /etc/lightdm/lightdm.conf +``` +start-default-seat=true +autologin-user=pi +autologin-user-timeout=0 +autologin-session=openbox +``` + +# ENV Vars +export DISPLAY=:0.0 +export NOTAPI=true + +# TKINTER Examples +https://tkdocs.com/tutorial/eventloop.html +https://python-textbok.readthedocs.io/en/1.0/Introduction_to_GUI_Programming.html +https://www.journaldev.com/48165/tkinter-working-with-classes + +``` +from PIL import Image +i = Image.open("cube.png") +i.show() +``` + +``` +import tkinter as tk +from PIL import Image, ImageTk + +def callback(event): + print("clicked at", event.x, event.y) + +window = tk.Tk() +window.attributes('-fullscreen',True) + + +x = window.winfo_pointerx() +y = window.winfo_pointery() + + + +window.bind("", callback) + + +imagefile2 = "../../cube.png" +imgpil2 = Image.open(imagefile) +img2 = ImageTk.PhotoImage(imgpil) + +imagefile = "seedsigner/resources/logo_black_240.png" +imgpil = Image.open(imagefile) +img = ImageTk.PhotoImage(imgpil) + +#lbl = tk.Label(window, image = img).pack() +frame = tk.Label(window) +frame.configure(image=img) +frame.place(x=333,y=233) + +lbl.configure(image=img2) + +def button_press_move(): + frame.place(x=random.randint(0,640), y=random.randint(0,480)) + +btn_KEY_UP = tk.Button(window, text="KEY1") +btn_KEY_UP.place(x=50, y=50) +btn_KEY_UP.configure(width=10,height=4) +btn_KEY_UP.configure(command=button_press_move) + + +myglobal = None +myglobal1 = None + + +def press(event): + print(event.state) + print(dir(event)) + global myglobal + myglobal1 = event + +def release(event): + print(event.state) + print(dir(event)) + global myglobal1 + myglobal1 = event + +btn_KEY_UP.bind("", press) +btn_KEY_UP.bind("", release) + +window.mainloop() +``` + +``` +w = tk.Canvas(window, width=240, height=240, bg="green").pack() +``` + + +``` +from queue import Queue +from seedsigner.helpers import touchscreen +import tkinter as tk +from PIL import Image, ImageTk +import threading + +a = Queue() +sq = touchscreen.touchscreen(a) +sq.Init() + +import random +from time import sleep + +def showimages(): + while True: + print("looping images") + imagefile2 = "../../cube.png" + imgpil2 = Image.open(imagefile2) + a.put((imgpil2, random.randint(0,400), random.randint(0,400))) + sleep(0.1) + +x = threading.Thread(target=showimages) +x.start() + + + +img2 = ImageTk.PhotoImage(imgpil) +``` + + +``` +import tkinter as tk +import threading +import signal +import sys + + +class MyTkApp(object): + def poll(self): + sys.stderr.write("poll\n") + self.root.after(500, self.poll) + def quit(self, event): + print("you pressed control c") + self.root.quit() + def run(self): + self.root = tk.Tk() + self.root.after(500, self.poll) + self.root.bind('', self.quit) + self.root.mainloop() + + +app = MyTkApp() +app.start() + +import time + +while app.is_alive(): + try: + time.sleep(0.5) + except KeyboardInterrupt: + app.root.destroy() + break +``` + + +``` +from queue import Queue +from seedsigner.helpers import touchscreen + +``` \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f24a8e7e..8fa58fd9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,12 @@ embit==0.4.10 numpy==1.21.1 -picamera==1.13 +# need to not install this on x86... +#picamera==1.13 Pillow==8.2.0 -e git+https://github.com/kdmukai/pyzbar.git@c3c237821c6a20b17953efe59b90df0b514a1c03#egg=pyzbar qrcode==7.3.1 -RPi.GPIO==0.7.0 +#RPi.GPIO==0.7.1 six==1.16.0 spidev==3.5 --e src/vendor/urtypes \ No newline at end of file +urtypes @ git+https://github.com/jreesun/urtypes.git@e0d0db277ec2339650343eaf7b220fffb9233241 +#-e src/vendor/urtypes diff --git a/src/main.py b/src/main.py index deb8df17..d8152fe0 100644 --- a/src/main.py +++ b/src/main.py @@ -2,15 +2,21 @@ import configparser import sys import time -from seedsigner.controller import Controller +import threading +from seedsigner.controller import Controller +from seedsigner.helpers import touchscreen config = configparser.ConfigParser() config.read("settings.ini") # One-time setup to intialize the one and only Controller +# Controller.configure_instance(config, disable_hardware=True) Controller.configure_instance(config) # Get the one and only Controller instance and start our main loop controller = Controller.get_instance() -controller.start() +x = threading.Thread(target=controller.start) +x.start() +controller.disp.mainloop() +# controller.start() diff --git a/src/seedsigner/controller.py b/src/seedsigner/controller.py index ab238d2b..a41f7d0b 100644 --- a/src/seedsigner/controller.py +++ b/src/seedsigner/controller.py @@ -9,6 +9,7 @@ from embit.networks import NETWORKS from embit.descriptor import Descriptor from binascii import hexlify from threading import Thread +from queue import Queue # Internal file class dependencies from .views import (View, MenuView, SeedToolsView,SigningToolsView, @@ -16,6 +17,8 @@ from .views import (View, MenuView, SeedToolsView,SigningToolsView, from .helpers import Buttons, B, Path, Singleton from .models import (EncodeQRDensity, QRType, Seed, SeedStorage, Settings, DecodeQR, DecodeQRStatus, EncodeQR, PSBTParser) +from seedsigner.helpers import touchscreen + class Controller(Singleton): """ The Controller is a globally available singleton that maintains SeedSigner state. @@ -65,11 +68,19 @@ class Controller(Singleton): controller = cls.__new__(cls) cls._instance = controller + controller.q = Queue() + print("init display") + controller.disp = touchscreen.touchscreen(controller.q) + controller.disp.Init() + + + # Input Buttons if disable_hardware: + print("disabling the buttons for now") controller.buttons = None else: - controller.buttons = Buttons() + controller.buttons = Buttons(controller.disp) # models controller.storage = SeedStorage() @@ -81,25 +92,28 @@ class Controller(Singleton): controller.color = controller.settings.text_color controller.current_bg_qr_color = controller.settings.qr_background_color + + # Views - controller.menu_view = MenuView() - controller.seed_tools_view = SeedToolsView() - controller.io_test_view = IOTestView() - controller.signing_tools_view = SigningToolsView(controller.storage) - controller.settings_tools_view = SettingsToolsView() - controller.screensaver = ScreensaverView(controller.buttons) + controller.menu_view = MenuView(controller.disp, controller.q) + controller.seed_tools_view = SeedToolsView(controller.disp, controller.q) + # controller.io_test_view = IOTestView() + # controller.signing_tools_view = SigningToolsView(controller.storage) + controller.settings_tools_view = SettingsToolsView(controller.disp, controller.q) + controller.screensaver = ScreensaverView(controller.buttons, controller.disp, controller.q) controller.screensaver_activation_ms = 120 * 1000 @property def camera(self): - from .camera import Camera - return Camera.get_instance() + if not os.getenv("NOTAPI", False): + from .camera import Camera + return Camera.get_instance() def start(self) -> None: - opening_splash = OpeningSplashView() + opening_splash = OpeningSplashView(self.disp, self.q) opening_splash.start() if self.DEBUG: diff --git a/src/seedsigner/helpers/ST7789.py b/src/seedsigner/helpers/ST7789.py index 8f8de657..af38a9f3 100644 --- a/src/seedsigner/helpers/ST7789.py +++ b/src/seedsigner/helpers/ST7789.py @@ -1,5 +1,7 @@ import spidev -import RPi.GPIO as GPIO +import os +if not os.getenv("NOTAPI", False): + import RPi.GPIO as GPIO import time import numpy as np diff --git a/src/seedsigner/helpers/buttons.py b/src/seedsigner/helpers/buttons.py index 3956a5b0..5b478f3c 100644 --- a/src/seedsigner/helpers/buttons.py +++ b/src/seedsigner/helpers/buttons.py @@ -1,7 +1,9 @@ -import RPi.GPIO as GPIO + import time - +import os +if not os.getenv("NOTAPI", False): + import RPi.GPIO as GPIO class Buttons: KEY_UP_PIN = 6 @@ -14,22 +16,34 @@ class Buttons: KEY2_PIN = 20 KEY3_PIN = 16 - def __init__(self) -> None: + def __init__(self, disp) -> None: #init GPIO - GPIO.setmode(GPIO.BCM) - GPIO.setup(Buttons.KEY_UP_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up - GPIO.setup(Buttons.KEY_DOWN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up - GPIO.setup(Buttons.KEY_LEFT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up - GPIO.setup(Buttons.KEY_RIGHT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up - GPIO.setup(Buttons.KEY_PRESS_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up - GPIO.setup(Buttons.KEY1_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up - GPIO.setup(Buttons.KEY2_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up - GPIO.setup(Buttons.KEY3_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up + # GPIO.setmode(GPIO.BCM) + # GPIO.setup(Buttons.KEY_UP_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up + # GPIO.setup(Buttons.KEY_DOWN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up + # GPIO.setup(Buttons.KEY_LEFT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up + # GPIO.setup(Buttons.KEY_RIGHT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up + # GPIO.setup(Buttons.KEY_PRESS_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up + # GPIO.setup(Buttons.KEY1_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up + # GPIO.setup(Buttons.KEY2_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up + # GPIO.setup(Buttons.KEY3_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up - self.GPIO = GPIO + self.disp = disp + + + # self.GPIO = GPIO self.override_ind = False - self.add_events([B.KEY_UP, B.KEY_DOWN, B.KEY_PRESS, B.KEY_LEFT, B.KEY_RIGHT, B.KEY1, B.KEY2, B.KEY3]) + self.add_events([ + B.KEY_UP, + B.KEY_DOWN, + B.KEY_PRESS, + B.KEY_LEFT, + B.KEY_RIGHT, + B.KEY1, + B.KEY2, + B.KEY3 + ]) # Track state over time so we can apply input delays/ignores as needed self.cur_input = None # Track which direction or button was last pressed @@ -39,7 +53,10 @@ class Buttons: self.next_repeat_threshold = 250 # Amount of time where we no longer consider input a continuous hold + + def wait_for(self, keys=[], check_release=True, release_keys=[]) -> int: + print("wait_for") # TODO: Refactor to keep control in the Controller and not here from seedsigner.controller import Controller controller = Controller.get_instance() @@ -48,27 +65,47 @@ class Buttons: release_keys = keys self.override_ind = False + x = 0 + counter = 0 while True: + counter += 1 cur_time = int(time.time() * 1000) - if cur_time - self.last_input_time > controller.screensaver_activation_ms and not controller.screensaver.is_running: - # Start the screensaver. Will block execution until input detected. - controller.start_screensaver() + # DONT WORRY ABOUT SCREENSAVER FOR NOW + # if cur_time - self.last_input_time > controller.screensaver_activation_ms and not controller.screensaver.is_running: + # # Start the screensaver. Will block execution until input detected. + # controller.start_screensaver() - # We're back. Update last_input_time to now. - self.update_last_input_time() + # # We're back. Update last_input_time to now. + # self.update_last_input_time() - # Freeze any further processing for a moment to avoid having the wakeup - # input register in the resumed UI. - time.sleep(self.next_repeat_threshold / 1000.0) + # # Freeze any further processing for a moment to avoid having the wakeup + # # input register in the resumed UI. + # time.sleep(self.next_repeat_threshold / 1000.0) - # Resume from a fresh loop - continue + # # Resume from a fresh loop + # continue + + # x += 1 + # # if x == 10: + # # self.window.update() + if counter == 1000: + counter = 0 + print(f"lock status: {B.release_lock}") for key in keys: - if not check_release or ((check_release and key in release_keys and B.release_lock) or check_release and key not in release_keys): + # self.window.update() + # print(f"looking for key: {key}") + # if self.disp.button_state[key] == True: + # return key + # time.sleep(0.1) + + if not check_release or \ + ((check_release and key in release_keys and B.release_lock) or check_release and key not in release_keys): # when check release is False or the release lock is released (True) - if self.GPIO.input(key) == GPIO.LOW or self.override_ind: + if self.disp.button_state[key] == False or self.override_ind: B.release_lock = False + print("setting lock") + print if self.override_ind: self.override_ind = False return B.OVERRIDE @@ -77,6 +114,7 @@ class Buttons: self.cur_input = key self.cur_input_started = int(time.time() * 1000) # in milliseconds self.last_input_time = self.cur_input_started + print(f"new key press: {key}") return key else: @@ -86,12 +124,14 @@ class Buttons: # continuous input. Treat as a new separate press. self.cur_input_started = cur_time self.last_input_time = cur_time + print(f"still pressing: {key}") return key elif cur_time - self.cur_input_started > self.first_repeat_threshold: # We're good to relay this immediately as continuous # input. self.last_input_time = cur_time + print(f"continuous press: {key}") return key else: @@ -110,13 +150,17 @@ class Buttons: time.sleep(0.01) # wait 10 ms to give CPU chance to do other things + def lock_input(self, lock): + pass + def update_last_input_time(self): self.last_input_time = int(time.time() * 1000) def add_events(self, keys=[]): - for key in keys: - GPIO.add_event_detect(key, self.GPIO.RISING, callback=Buttons.rising_callback) + pass + # for key in keys: + # GPIO.add_event_detect(key, self.GPIO.RISING, callback=Buttons.rising_callback) def rising_callback(channel): @@ -137,7 +181,7 @@ class Buttons: return True def check_for_low(self, key) -> bool: - if self.GPIO.input(key) == self.GPIO.LOW: + if self.disp.button_state[key] == False: self.update_last_input_time() return True else: @@ -145,7 +189,7 @@ class Buttons: def has_any_input(self) -> bool: for key in B.ALL_KEYS: - if self.GPIO.input(key) == GPIO.LOW: + if self.disp.button_state[key] == False: return True return False @@ -156,14 +200,26 @@ class Buttons: # TODO: Implement `release_lock` functionality as a global somewhere. Mixes up design # patterns to have a static constants class plus a settable global value. class B: - KEY_UP = 6 - KEY_DOWN = 19 - KEY_LEFT = 5 - KEY_RIGHT = 26 - KEY_PRESS = 13 - KEY1 = 21 - KEY2 = 20 - KEY3 = 16 + # KEY_UP = 6 + # KEY_DOWN = 19 + # KEY_LEFT = 5 + # KEY_RIGHT = 26 + # KEY_PRESS = 13 + # KEY1 = 21 + # KEY2 = 20 + # KEY3 = 16 + + KEY_UP = "UP" + KEY_DOWN = "DOWN" + KEY_LEFT = "LL" + KEY_RIGHT = "RR" + KEY_PRESS = "PRESS" + KEY1 = "KEY1" + KEY2 = "KEY2" + KEY3 = "KEY3" + # KEY1 = "K1" + # KEY2 = "K2" + # KEY3 = "K3" OVERRIDE = 1000 ALL_KEYS = [ diff --git a/src/seedsigner/helpers/pivideostream.py b/src/seedsigner/helpers/pivideostream.py index cb3031b3..486dc848 100644 --- a/src/seedsigner/helpers/pivideostream.py +++ b/src/seedsigner/helpers/pivideostream.py @@ -1,6 +1,8 @@ -# import the necessary packages -from picamera.array import PiRGBArray -from picamera import PiCamera +import os +if not os.getenv("NOTAPI", False): + # import the necessary packages + from picamera.array import PiRGBArray + from picamera import PiCamera from threading import Thread import time diff --git a/src/seedsigner/helpers/touchscreen.py b/src/seedsigner/helpers/touchscreen.py new file mode 100644 index 00000000..5ca12dff --- /dev/null +++ b/src/seedsigner/helpers/touchscreen.py @@ -0,0 +1,218 @@ +from pathlib import Path +from time import sleep +from threading import Thread +import os + +import tkinter as tk +from tkinter import ttk +from PIL import Image, ImageTk +import tkinter.font as font +# myFont = font.Font(family='Helvetica') + +from pprint import pprint + +from .buttons import B + + +class touchscreen(tk.Tk): + def __init__(self, q): + super().__init__() + # Thread.__init__(self) + print("inside touchscreen _init_") + # self.window = tk.Tk() + # self.window.attributes('-fullscreen', True) + self.config(cursor="circle") + self.latest_img = None + self.q = q + sleep(1) + self.title('SeedSigner') + if not os.getenv("NOTAPI", False): + self.attributes("-fullscreen", True) + else: + self.geometry("640x480") + + self.myFont = font.Font(family='Helvetica', size=35, weight='bold') + self.myFont2 = font.Font(family='Helvetica', size=20, weight='bold') + + # self.img_path = Path("/home/pi/seedsigner/src/seedsigner/resources") + + self.img_path = Path("seedsigner/resources") + + + def Init(self): + """Initialize display""" + print("inside Init") + + self.button_state = { + "UP": True, + "DOWN": True, + "LL": True, + "RR": True, + "PRESS": True, + "KEY1": True, + "KEY2": True, + "KEY3": True + } + + self.style = ttk.Style() + self.style.theme_use("clam") + self.style.configure('TButton', font=('Helvetica', 20)) + + self.btn_right = Image.open(self.img_path.joinpath("buttons/RR.png")) + self.btn_up = Image.open(self.img_path.joinpath("buttons/UP.png")) + self.btn_left = Image.open(self.img_path.joinpath("buttons/LL.png")) + self.btn_down = Image.open(self.img_path.joinpath("buttons/DOWN.png")) + self.btn_select = Image.open(self.img_path.joinpath("buttons/SELECT.png")) + self.btn_k1 = Image.open(self.img_path.joinpath("buttons/K1.png")) + self.btn_k2 = Image.open(self.img_path.joinpath("buttons/K2.png")) + self.btn_k3 = Image.open(self.img_path.joinpath("buttons/K3.png")) + + self.btn_right_tk = ImageTk.PhotoImage(self.btn_right) + self.btn_up_tk = ImageTk.PhotoImage(self.btn_up) + self.btn_left_tk = ImageTk.PhotoImage(self.btn_left) + self.btn_down_tk = ImageTk.PhotoImage(self.btn_down) + self.btn_select_tk = ImageTk.PhotoImage(self.btn_select) + self.btn_k1_tk = ImageTk.PhotoImage(self.btn_k1) + self.btn_k2_tk = ImageTk.PhotoImage(self.btn_k2) + self.btn_k3_tk = ImageTk.PhotoImage(self.btn_k3) + + + self.label = tk.Label(self) + self.label.place(x=160, y=0) + + self.btn_KEY_PRESS = tk.Button(self, text="PRESS", image=self.btn_select_tk) + self.btn_KEY_PRESS.place(x=160,y=400) + # self.btn_KEY_PRESS.configure(ipady=10, ipadx=10) + # self.btn_KEY_PRESS.configure(width=30, height=5) + + self.btn_KEY_UP = tk.Button(self, text="UP", image=self.btn_up_tk) + self.btn_KEY_UP.place(x=0,y=0) + # self.btn_KEY_UP.configure(width=5, height=5) + + self.btn_KEY_DOWN = tk.Button(self, text="DOWN", image=self.btn_down_tk) + self.btn_KEY_DOWN.place(x=0,y=320) + # self.btn_KEY_DOWN.configure(width=5, height=5) + + self.btn_KEY_LEFT = tk.Button(self, text="LL", image=self.btn_left_tk) + self.btn_KEY_LEFT.place(x=0,y=160) + # self.btn_KEY_LEFT.configure(width=5, height=5) + + self.btn_KEY_RIGHT = tk.Button(self, text="RR", image=self.btn_right_tk) + self.btn_KEY_RIGHT.place(x=80,y=160) + # self.btn_KEY_RIGHT.configure(width=5, height=5) + + self.btn_KEY_1 = tk.Button(self, text="KEY1", image=self.btn_k1_tk) + self.btn_KEY_1.place(x=560, y=0) + # self.btn_KEY_1.configure(width=5, height=5) + + self.btn_KEY_2 = tk.Button(self, text="KEY2", image=self.btn_k2_tk) + self.btn_KEY_2.place(x=560, y=160) + # self.btn_KEY_2.configure(width=5, height=5) + + self.btn_KEY_3 = tk.Button(self, text="KEY3", image=self.btn_k3_tk) + self.btn_KEY_3.place(x=560, y=320) + # self.btn_KEY_3.configure(width=5, height=5) + + self.btn_KEY_UP.bind("", self.update_button_state) + self.btn_KEY_DOWN.bind("", self.update_button_state) + self.btn_KEY_PRESS.bind("", self.update_button_state) + self.btn_KEY_LEFT.bind("", self.update_button_state) + self.btn_KEY_RIGHT.bind("", self.update_button_state) + self.btn_KEY_1.bind("", self.update_button_state) + self.btn_KEY_2.bind("", self.update_button_state) + self.btn_KEY_3.bind("", self.update_button_state) + + + # self.btn_KEY_UP.bind("", self.update_button_state) + # self.btn_KEY_DOWN.bind("", self.update_button_state) + # self.btn_KEY_PRESS.bind("", self.update_button_state) + # self.btn_KEY_LEFT.bind("", self.update_button_state) + # self.btn_KEY_RIGHT.bind("", self.update_button_state) + # self.btn_KEY_1.bind("", self.update_button_state) + # self.btn_KEY_2.bind("", self.update_button_state) + # self.btn_KEY_3.bind("", self.update_button_state) + + + self.refresh() + # self.mainloop() + + def run(self): + self.window = tk.Tk() + self.window.attributes('-fullscreen', True) + + + + # self.btn_KEY_LEFT = tk.Button(self.window, text="LL") + # self.btn_KEY_RIGHT = tk.Button(self.window, text="RR") + + # self.btn_KEY_1 = tk.Button(self.window, text="KEY1") + # self.btn_KEY_2 = tk.Button(self.window, text="KEY2") + # self.btn_KEY_3 = tk.Button(self.window, text="KEY3") + + + # while True: + # r = self.q.get() + # print(r) + # self.ShowImage(r[0],r[1],r[2]) + # self.window.update() + # sleep(0.05) + + self.refresh() + print("after refresh") + self.mainloop() + + def refresh(self): + self.draw() + self.after(2, self.refresh) + + def draw(self): + try: + data = self.q.get(timeout=0.25) + print("got data") + self.ShowImage(*data) + except Exception as e: + pass + + def update_button_state(self, event): + button_name = event.widget.cget("text") + event_type = event.type.value + set_value = None + # 4 is press, 5 is release + # Active HIGH + # set_value = True if event_type == "4" else False + # Active LOW + set_value = False if event_type == "4" else True + + # if set_value == True: + # print("releasing lock") + # B.release_lock = True + + print(f"Setting button {button_name} to {set_value}\n") + self.button_state[button_name] = set_value + pprint(self.button_state) + self.after(5, self.reset_press, button_name, True) + + def reset_press(self, button_name, button_value): + self.button_state[button_name] = button_value + print("releasing lock") + B.release_lock = True + + def ShowImage(self, Image, Xstart, Ystart): + """Set buffer to value of Python Imaging Library image.""" + """Write display buffer to physical display""" + print("inside ShowImage") + + Image = Image.resize(size=(400,400)) + img = ImageTk.PhotoImage(Image) + self.latest_img = img + + + self.label["image"] = self.latest_img + # self.label.place(x=Xstart, y=Ystart) + # sleep(1) + print("updated") + + + def reset(self): + """Reset the display""" + print("DO SOMETHING!") \ No newline at end of file diff --git a/src/seedsigner/resources/arrow.png b/src/seedsigner/resources/arrow.png new file mode 100644 index 00000000..62c5fba3 Binary files /dev/null and b/src/seedsigner/resources/arrow.png differ diff --git a/src/seedsigner/resources/buttons/DOWN.png b/src/seedsigner/resources/buttons/DOWN.png new file mode 100644 index 00000000..7b79a67c Binary files /dev/null and b/src/seedsigner/resources/buttons/DOWN.png differ diff --git a/src/seedsigner/resources/buttons/K1.png b/src/seedsigner/resources/buttons/K1.png new file mode 100644 index 00000000..cb52271f Binary files /dev/null and b/src/seedsigner/resources/buttons/K1.png differ diff --git a/src/seedsigner/resources/buttons/K2.png b/src/seedsigner/resources/buttons/K2.png new file mode 100644 index 00000000..63d238d8 Binary files /dev/null and b/src/seedsigner/resources/buttons/K2.png differ diff --git a/src/seedsigner/resources/buttons/K3.png b/src/seedsigner/resources/buttons/K3.png new file mode 100644 index 00000000..3edbe4f9 Binary files /dev/null and b/src/seedsigner/resources/buttons/K3.png differ diff --git a/src/seedsigner/resources/buttons/LL.png b/src/seedsigner/resources/buttons/LL.png new file mode 100644 index 00000000..4dc41baf Binary files /dev/null and b/src/seedsigner/resources/buttons/LL.png differ diff --git a/src/seedsigner/resources/buttons/RR.png b/src/seedsigner/resources/buttons/RR.png new file mode 100644 index 00000000..6a1cb14e Binary files /dev/null and b/src/seedsigner/resources/buttons/RR.png differ diff --git a/src/seedsigner/resources/buttons/SELECT.png b/src/seedsigner/resources/buttons/SELECT.png new file mode 100644 index 00000000..c240bc67 Binary files /dev/null and b/src/seedsigner/resources/buttons/SELECT.png differ diff --git a/src/seedsigner/resources/buttons/UP.png b/src/seedsigner/resources/buttons/UP.png new file mode 100644 index 00000000..dbd1c5ab Binary files /dev/null and b/src/seedsigner/resources/buttons/UP.png differ diff --git a/src/seedsigner/views/menu_view.py b/src/seedsigner/views/menu_view.py index 8756312a..cf86c71e 100644 --- a/src/seedsigner/views/menu_view.py +++ b/src/seedsigner/views/menu_view.py @@ -10,8 +10,8 @@ import re class MenuView(View): - def __init__(self) -> None: - View.__init__(self) + def __init__(self, disp, q) -> None: + View.__init__(self, disp, q) self.menu_lines = [] self.selected_menu_num = 1 @@ -38,6 +38,7 @@ class MenuView(View): # Wait for Button Input (specifically menu selection/press) while True: + print("wait for button press MAIN MENU") if ret_val == 0: input = self.buttons.wait_for([B.KEY_UP, B.KEY_DOWN, B.KEY_PRESS], check_release=True, release_keys=[B.KEY_PRESS]) else: @@ -77,6 +78,7 @@ class MenuView(View): # Wait for Button Input (specifically menu selection/press) while True: + print("wait for button press SEED TOOLS MENU") input = self.buttons.wait_for([B.KEY_UP, B.KEY_DOWN, B.KEY_PRESS], check_release=True, release_keys=[B.KEY_PRESS]) if input == B.KEY_UP: self.menu_up() @@ -107,6 +109,7 @@ class MenuView(View): # Wait for Button Input (specifically menu selection/press) while True: + print("wait for button press SIGNING TOOLS MENU") input = self.buttons.wait_for([B.KEY_UP, B.KEY_DOWN, B.KEY_PRESS], check_release=True, release_keys=[B.KEY_PRESS]) if input == B.KEY_UP: self.menu_up() @@ -145,6 +148,7 @@ class MenuView(View): # Wait for Button Input (specifically menu selection/press) while True: + print("wait for button press SETTINGS MENU") input = self.buttons.wait_for([B.KEY_UP, B.KEY_DOWN, B.KEY_PRESS], check_release=True, release_keys=[B.KEY_PRESS]) if input == B.KEY_UP: self.menu_up() @@ -198,6 +202,7 @@ class MenuView(View): # Wait for Button Input (specifically menu selection/press) while True: + print("wait for button press 12/24 WORDS MENU") input = self.buttons.wait_for([B.KEY_UP, B.KEY_DOWN, B.KEY_PRESS], check_release=True, release_keys=[B.KEY_PRESS]) if input == B.KEY_UP: self.menu_up() diff --git a/src/seedsigner/views/screensaver.py b/src/seedsigner/views/screensaver.py index 3f4afa0d..c28d4c62 100644 --- a/src/seedsigner/views/screensaver.py +++ b/src/seedsigner/views/screensaver.py @@ -6,14 +6,23 @@ from PIL import Image, ImageDraw from . import View -from seedsigner.helpers import B +from seedsigner.helpers import B, touchscreen class LogoView(View): - def __init__(self): + def __init__(self, disp, q): + print("inside logo") + # View.disp = touchscreen.touchscreen() + # View.disp.Init() + View.disp = disp dirname = os.path.dirname(__file__) logo_url = os.path.join(dirname, "../../", "seedsigner", "resources", "logo_black_240.png") self.logo = Image.open(logo_url) + View.canvas_width = View.WIDTH + View.canvas_height = View.HEIGHT + View.canvas = Image.new('RGB', (View.canvas_width, View.canvas_height)) + View.draw = ImageDraw.Draw(View.canvas) + @@ -26,7 +35,8 @@ class OpeningSplashView(LogoView): for i in range(250, -1, -25): self.logo.putalpha(255 - i) background = Image.new("RGBA", self.logo.size, (0,0,0)) - View.disp.ShowImage(Image.alpha_composite(background, self.logo), 0, 0) + # View.disp.ShowImage(Image.alpha_composite(background, self.logo), 0, 0) + View.q.put((Image.alpha_composite(background, self.logo), 0, 0)) # Display version num and hold for a few seconds font = View.ROBOTOCONDENSED_REGULAR_22 @@ -43,8 +53,8 @@ class OpeningSplashView(LogoView): class ScreensaverView(LogoView): - def __init__(self, buttons): - super().__init__() + def __init__(self, buttons, disp, q): + super().__init__(disp, q) self.buttons = buttons diff --git a/src/seedsigner/views/seed_tools_view.py b/src/seedsigner/views/seed_tools_view.py index ec4b3fb6..c599ef4b 100644 --- a/src/seedsigner/views/seed_tools_view.py +++ b/src/seedsigner/views/seed_tools_view.py @@ -18,8 +18,8 @@ class SeedToolsView(View): ALPHABET = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] - def __init__(self) -> None: - View.__init__(self) + def __init__(self, disp, q) -> None: + View.__init__(self, disp, q) # Gather words and seed display information self.words = [] diff --git a/src/seedsigner/views/settings_tools_view.py b/src/seedsigner/views/settings_tools_view.py index b850e926..d9e12733 100644 --- a/src/seedsigner/views/settings_tools_view.py +++ b/src/seedsigner/views/settings_tools_view.py @@ -5,8 +5,8 @@ from seedsigner.models import EncodeQRDensity class SettingsToolsView(View): - def __init__(self) -> None: - View.__init__(self) + def __init__(self, disp, q) -> None: + View.__init__(self, disp, q) self.qr = QR() self.donate_image = None diff --git a/src/seedsigner/views/view.py b/src/seedsigner/views/view.py index d1ffb509..64b9c84f 100644 --- a/src/seedsigner/views/view.py +++ b/src/seedsigner/views/view.py @@ -5,7 +5,8 @@ import pathlib import spidev as SPI import time from multiprocessing import Queue -from seedsigner.helpers import B, ST7789 +from seedsigner.helpers import B, ST7789, touchscreen + ### Generic View Class to Instatiate Display @@ -78,7 +79,7 @@ class View: previous_button_width = None - def __init__(self) -> None: + def __init__(self, screen, q) -> None: # Import here to avoid circular imports from seedsigner.controller import Controller self.controller = Controller.get_instance() @@ -91,14 +92,19 @@ class View: View.canvas = Image.new('RGB', (View.canvas_width, View.canvas_height)) View.draw = ImageDraw.Draw(View.canvas) + View.q = q # 240x240 display with hardware SPI: View.bus = 0 View.device = 0 # TODO: Add `disable_hardware` option for test suite here, too? - View.disp = ST7789(SPI.SpiDev(View.bus, View.device),View.RST, View.DC, View.BL) - View.disp.Init() - + # View.disp = ST7789(SPI.SpiDev(View.bus, View.device),View.RST, View.DC, View.BL) + print("init display") + # View.disp = touchscreen.touchscreen() + # View.disp.Init() + View.disp = screen + # import code + # code.interact(local=dict(globals(), **locals())) self.queue = Queue() @@ -115,7 +121,8 @@ class View: # Always keep a copy of the current display in the canvas View.canvas.paste(image) - View.disp.ShowImage(image, 0, 0) + View.q.put((image, 0, 0)) + # View.disp.ShowImage(image, 0, 0) def disp_show_image_pan(image, start_x, start_y, end_x, end_y, rate, alpha_overlay=None):