touchscreen!
@@ -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
|
||||
@@ -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("<Button-1>", 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("<ButtonPress>", press)
|
||||
btn_KEY_UP.bind("<ButtonRelease>", 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('<Control-c>', 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
|
||||
|
||||
```
|
||||
@@ -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
|
||||
urtypes @ git+https://github.com/jreesun/urtypes.git@e0d0db277ec2339650343eaf7b220fffb9233241
|
||||
#-e src/vendor/urtypes
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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("<ButtonPress>", self.update_button_state)
|
||||
self.btn_KEY_DOWN.bind("<ButtonPress>", self.update_button_state)
|
||||
self.btn_KEY_PRESS.bind("<ButtonPress>", self.update_button_state)
|
||||
self.btn_KEY_LEFT.bind("<ButtonPress>", self.update_button_state)
|
||||
self.btn_KEY_RIGHT.bind("<ButtonPress>", self.update_button_state)
|
||||
self.btn_KEY_1.bind("<ButtonPress>", self.update_button_state)
|
||||
self.btn_KEY_2.bind("<ButtonPress>", self.update_button_state)
|
||||
self.btn_KEY_3.bind("<ButtonPress>", self.update_button_state)
|
||||
|
||||
|
||||
# self.btn_KEY_UP.bind("<ButtonRelease>", self.update_button_state)
|
||||
# self.btn_KEY_DOWN.bind("<ButtonRelease>", self.update_button_state)
|
||||
# self.btn_KEY_PRESS.bind("<ButtonRelease>", self.update_button_state)
|
||||
# self.btn_KEY_LEFT.bind("<ButtonRelease>", self.update_button_state)
|
||||
# self.btn_KEY_RIGHT.bind("<ButtonRelease>", self.update_button_state)
|
||||
# self.btn_KEY_1.bind("<ButtonRelease>", self.update_button_state)
|
||||
# self.btn_KEY_2.bind("<ButtonRelease>", self.update_button_state)
|
||||
# self.btn_KEY_3.bind("<ButtonRelease>", 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!")
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 847 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||