mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-06 15:38:25 +00:00
Persistent camera rotation support
This commit is contained in:
@@ -5,6 +5,7 @@ persistent_settings = False
|
||||
|
||||
[display]
|
||||
text_color = ORANGE
|
||||
camera_rotation = 0
|
||||
|
||||
[wallet]
|
||||
network = main
|
||||
|
||||
+12
-27
@@ -10,6 +10,16 @@ from seedsigner.helpers import PiVideoStream, Singleton
|
||||
class Camera(Singleton):
|
||||
_video_stream = None
|
||||
_picamera = None
|
||||
_camera_rotation = None
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls):
|
||||
# This is the only way to access the one and only Controller
|
||||
from seedsigner.models import Settings
|
||||
if cls._instance is None:
|
||||
cls._instance = cls.__new__(cls)
|
||||
cls._instance._camera_rotation = Settings.get_instance().camera_rotation
|
||||
return cls._instance
|
||||
|
||||
|
||||
def start_video_stream_mode(self, resolution=(512, 384), framerate=12, format="bgr"):
|
||||
@@ -28,7 +38,7 @@ class Camera(Singleton):
|
||||
return frame
|
||||
else:
|
||||
if frame is not None:
|
||||
return Image.fromarray(frame.astype('uint8'), 'RGB').rotate(90)
|
||||
return Image.fromarray(frame.astype('uint8'), 'RGB').rotate(90 + self._camera_rotation)
|
||||
return None
|
||||
|
||||
|
||||
@@ -59,37 +69,12 @@ class Camera(Singleton):
|
||||
self._picamera.awb_mode = 'off'
|
||||
self._picamera.awb_gains = g
|
||||
|
||||
"""
|
||||
When outputting to unencoded formats, the camera rounds the requested resolution.
|
||||
The horizontal resolution is rounded up to the nearest multiple of 32 pixels,
|
||||
while the vertical resolution is rounded up to the nearest multiple of 16 pixels.
|
||||
"""
|
||||
# resolution = self._picamera.resolution
|
||||
# if resolution.width % 32 == 0:
|
||||
# raw_width = resolution.width
|
||||
# else:
|
||||
# raw_width = resolution.width - (resolution.width % 32) + 32
|
||||
|
||||
# if resolution.height % 16 == 0:
|
||||
# raw_height = resolution.height
|
||||
# else:
|
||||
# raw_height = resolution.height + 16
|
||||
|
||||
# print(resolution)
|
||||
# print(raw_width, raw_height)
|
||||
# np_array = numpy.empty((raw_width, raw_height, 3), dtype=numpy.uint8)
|
||||
# self._picamera.capture(np_array, format='rgb')
|
||||
|
||||
# np_array.reshape((raw_width, raw_height, 3))
|
||||
|
||||
# return Image.fromarray(np_array[:resolution.width, :resolution.height, :].astype('uint8'), 'RGB')
|
||||
|
||||
stream = io.BytesIO()
|
||||
self._picamera.capture(stream, format='jpeg')
|
||||
|
||||
# "Rewind" the stream to the beginning so we can read its content
|
||||
stream.seek(0)
|
||||
return Image.open(stream)
|
||||
return Image.open(stream).rotate(90 + self._camera_rotation)
|
||||
|
||||
|
||||
def stop_single_frame_mode(self):
|
||||
|
||||
@@ -73,7 +73,8 @@ class Controller(Singleton):
|
||||
controller.settings_tools_view = SettingsToolsView()
|
||||
controller.screensaver = ScreensaverView(controller.buttons)
|
||||
|
||||
controller.screensaver_activation_ms = 60 * 1000
|
||||
controller.screensaver_activation_ms = 120 * 1000
|
||||
|
||||
|
||||
@property
|
||||
def camera(self):
|
||||
@@ -153,6 +154,8 @@ class Controller(Singleton):
|
||||
ret_val = self.show_qr_density_tool()
|
||||
elif ret_val == Path.PERSISTENT_SETTINGS:
|
||||
ret_val = self.show_persistent_settings_tool()
|
||||
elif ret_val == Path.CAMERA_ROTATION:
|
||||
ret_val = self.show_camera_rotation_tool()
|
||||
elif ret_val == Path.DONATE:
|
||||
ret_val = self.show_donate_tool()
|
||||
elif ret_val == Path.RESET:
|
||||
@@ -830,7 +833,13 @@ class Controller(Singleton):
|
||||
|
||||
return Path.SETTINGS_SUB_MENU
|
||||
|
||||
### Show Donate Screen and QR
|
||||
|
||||
def show_camera_rotation_tool(self):
|
||||
r = self.settings_tools_view.display_camera_rotation()
|
||||
if r is not None:
|
||||
self.settings.camera_rotation = r
|
||||
|
||||
return Path.SETTINGS_SUB_MENU ### Show Donate Screen and QR
|
||||
|
||||
def show_donate_tool(self):
|
||||
self.settings_tools_view.display_donate_info_screen()
|
||||
|
||||
@@ -28,6 +28,7 @@ class Path:
|
||||
WALLET = 64
|
||||
QR_DENSITY_SETTING = 65
|
||||
PERSISTENT_SETTINGS = 67
|
||||
CAMERA_ROTATION = 69
|
||||
RESET = 68
|
||||
|
||||
# Seed Slots
|
||||
|
||||
@@ -5,6 +5,8 @@ from .encode_qr_density import EncodeQRDensity
|
||||
import configparser
|
||||
from embit import bip39
|
||||
|
||||
|
||||
|
||||
class Settings(Singleton):
|
||||
|
||||
@classmethod
|
||||
@@ -13,7 +15,7 @@ class Settings(Singleton):
|
||||
if cls._instance:
|
||||
raise Exception("Instance already configured")
|
||||
|
||||
# Instantiate the one and only Controller instance
|
||||
# Instantiate the one and only instance
|
||||
settings = cls.__new__(cls)
|
||||
cls._instance = settings
|
||||
|
||||
@@ -27,6 +29,7 @@ class Settings(Singleton):
|
||||
},
|
||||
'display': {
|
||||
'text_color': "ORANGE",
|
||||
'camera_rotation': 0,
|
||||
},
|
||||
'wallet': {
|
||||
'network': "main",
|
||||
@@ -49,6 +52,7 @@ class Settings(Singleton):
|
||||
self._data["system"]["debug"] = config.getboolean("system", "debug")
|
||||
self._data["system"]["default_language"] = config["system"]["default_language"]
|
||||
self._data["display"]["text_color"] = config["display"]["text_color"]
|
||||
self._data["display"]["camera_rotation"] = int(config["display"]["camera_rotation"])
|
||||
self.network = config["wallet"]["network"]
|
||||
self.software = config["wallet"]["software"]
|
||||
self.qr_density = int(config["wallet"]["qr_density"])
|
||||
@@ -125,6 +129,18 @@ class Settings(Singleton):
|
||||
def text_color(self):
|
||||
return self._data["display"]["text_color"]
|
||||
|
||||
@property
|
||||
def camera_rotation(self):
|
||||
return self._data["display"]["camera_rotation"]
|
||||
|
||||
@camera_rotation.setter
|
||||
def camera_rotation(self, value):
|
||||
if value in [0, 90, 180, 270]:
|
||||
self._data["display"]["camera_rotation"] = value
|
||||
self.__writeConfig()
|
||||
else:
|
||||
raise Exception("Unexpected display.camera_rotation settings.ini value")
|
||||
|
||||
### wallet
|
||||
|
||||
@property
|
||||
|
||||
@@ -124,7 +124,7 @@ class MenuView(View):
|
||||
### Settings Menu
|
||||
|
||||
def display_settings_menu(self) -> int:
|
||||
lines = ["... [ Return to Main ]", "Wallet: <wallet>", "Network: <network>", "QR Density: <density>", "Input / Output Tests", "Persistent Settings: <persistent>", "Version Info", "Donate to SeedSigner", "Reset SeedSigner"]
|
||||
lines = ["... [ Return to Main ]", "Wallet: <wallet>", "Network: <network>", "QR Density: <density>", "Input / Output Tests", "Persistent Settings: <persistent>", "Camera Rotation", "Version Info", "Donate to SeedSigner", "Reset SeedSigner"]
|
||||
input = 0
|
||||
|
||||
lines[1] = lines[1].replace("<wallet>", Settings.get_instance().software)
|
||||
@@ -157,10 +157,12 @@ class MenuView(View):
|
||||
elif self.selected_menu_num == 6:
|
||||
return Path.PERSISTENT_SETTINGS
|
||||
elif self.selected_menu_num == 7:
|
||||
return Path.VERSION_INFO
|
||||
return Path.CAMERA_ROTATION
|
||||
elif self.selected_menu_num == 8:
|
||||
return Path.DONATE
|
||||
return Path.VERSION_INFO
|
||||
elif self.selected_menu_num == 9:
|
||||
return Path.DONATE
|
||||
elif self.selected_menu_num == 10:
|
||||
return Path.RESET
|
||||
raise Exception("Unhandled case")
|
||||
|
||||
|
||||
@@ -1064,10 +1064,12 @@ class SeedToolsView(View):
|
||||
draw.rectangle((mask_width, 0, View.canvas_width - mask_width, pixels_per_block), fill=View.color)
|
||||
draw.rectangle((0, mask_height, pixels_per_block, View.canvas_height - mask_height), fill=View.color)
|
||||
|
||||
label_font = View.COURIERNEW24
|
||||
label_font = View.ASSISTANT26
|
||||
x_label = block_labels_x[cur_block_x]
|
||||
tw, th = draw.textsize(x_label, font=label_font)
|
||||
draw.text(((View.canvas_width - tw) / 2, (pixels_per_block - th) / 2), x_label, fill="BLACK", font=label_font)
|
||||
|
||||
# note: have to nudge the y-coord up (the extra "- 4") for some reason
|
||||
draw.text(((View.canvas_width - tw) / 2, ((pixels_per_block - th) / 2) - 4), x_label, fill="BLACK", font=label_font)
|
||||
|
||||
y_label = block_labels_y[cur_block_y]
|
||||
tw, th = draw.textsize(y_label, font=label_font)
|
||||
@@ -1209,8 +1211,6 @@ class SeedToolsView(View):
|
||||
display_version = autocontrast(
|
||||
seed_entropy_image,
|
||||
cutoff=2
|
||||
).rotate(
|
||||
90
|
||||
).crop(
|
||||
(120, 0, 600, 480)
|
||||
).resize(
|
||||
|
||||
@@ -79,6 +79,28 @@ class SettingsToolsView(View):
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def display_camera_rotation(self) -> int:
|
||||
lines = ["... [ Return to Settings ]"]
|
||||
lines.append("0 (default)")
|
||||
lines.append("90")
|
||||
lines.append("180")
|
||||
lines.append("270")
|
||||
|
||||
r = self.controller.menu_view.display_generic_selection_menu(lines, "Camera Rotation")
|
||||
if r == 1:
|
||||
return None
|
||||
elif r == 2:
|
||||
return 0
|
||||
elif r == 3:
|
||||
return 90
|
||||
elif r == 4:
|
||||
return 180
|
||||
elif r == 5:
|
||||
return 270
|
||||
else:
|
||||
return None
|
||||
|
||||
###
|
||||
### Version Info
|
||||
###
|
||||
|
||||
@@ -158,7 +158,7 @@ class View:
|
||||
text_overlay = Image.new("RGBA", (View.canvas_width, View.canvas_height), (255,255,255,0))
|
||||
text_overlay_draw = ImageDraw.Draw(text_overlay)
|
||||
if not font:
|
||||
font = View.COURIERNEW14
|
||||
font = View.ASSISTANT18
|
||||
tw, th = text_overlay_draw.textsize(text, font=font)
|
||||
if text_background:
|
||||
text_overlay_draw.rectangle(((240 - tw) / 2 - 3, 240 - th, (240 - tw) / 2 + tw + 3, 240), fill=text_background)
|
||||
|
||||
+2
-1
@@ -5,9 +5,10 @@ persistent_settings = False
|
||||
|
||||
[display]
|
||||
text_color = ORANGE
|
||||
camera_rotation = 180
|
||||
|
||||
[wallet]
|
||||
network = main
|
||||
software = Prompt
|
||||
qr_density = 2
|
||||
custom_derivation = m/0
|
||||
custom_derivation = m/0/0
|
||||
Reference in New Issue
Block a user