implemented top level menu
This commit is contained in:
parent
fbac02d6f9
commit
a74caf0f0a
6 changed files with 126 additions and 12 deletions
1
README.md
Normal file
1
README.md
Normal file
|
@ -0,0 +1 @@
|
|||
A menu-driven application launcher for a Raspberry Pi Zero with an Adafruit OLED bonnet.
|
41
cinput.py
Normal file
41
cinput.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
import board
|
||||
from digitalio import DigitalInOut, Direction, Pull
|
||||
from enum import Enum, auto
|
||||
from time import sleep
|
||||
|
||||
class Button(Enum):
|
||||
BTN_A = auto()
|
||||
BTN_B = auto()
|
||||
BTN_C = auto()
|
||||
DIR_U = auto()
|
||||
DIR_R = auto()
|
||||
DIR_D = auto()
|
||||
DIR_L = auto()
|
||||
|
||||
class ControlInput:
|
||||
_buttons = {
|
||||
Button.BTN_A: DigitalInOut(board.D5),
|
||||
Button.BTN_B: DigitalInOut(board.D6),
|
||||
Button.BTN_C: DigitalInOut(board.D4),
|
||||
Button.DIR_U: DigitalInOut(board.D17),
|
||||
Button.DIR_R: DigitalInOut(board.D23),
|
||||
Button.DIR_L: DigitalInOut(board.D27),
|
||||
Button.DIR_D: DigitalInOut(board.D22),
|
||||
}
|
||||
|
||||
_pressed = set()
|
||||
|
||||
def __init__(self):
|
||||
for button in self._buttons:
|
||||
self._buttons[button].direction = Direction.INPUT
|
||||
self._buttons[button].pull = Pull.UP
|
||||
|
||||
def get_one_shot(self):
|
||||
while True:
|
||||
for button in self._buttons:
|
||||
if not self._buttons[button].value and not button in self._pressed:
|
||||
self._pressed.add(button)
|
||||
return button
|
||||
elif self._buttons[button].value:
|
||||
self._pressed.discard(button)
|
||||
|
BIN
font5x8.bin
Normal file
BIN
font5x8.bin
Normal file
Binary file not shown.
24
graphics.py
Normal file
24
graphics.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
from board import SCL, SDA
|
||||
from busio import I2C
|
||||
from adafruit_ssd1306 import SSD1306_I2C
|
||||
|
||||
class Graphics:
|
||||
OLED_WIDTH = 128
|
||||
OLED_HEIGHT = 64
|
||||
OLED_ADDR = 0x3C
|
||||
LINE_HEIGHT = 9
|
||||
MAX_LINES = 7
|
||||
|
||||
def __init__(self):
|
||||
i2c = I2C(SCL, SDA)
|
||||
self._display = SSD1306_I2C(
|
||||
self.OLED_WIDTH, self.OLED_HEIGHT, i2c, addr = self.OLED_ADDR)
|
||||
|
||||
def clear(self):
|
||||
self._display.fill(0)
|
||||
|
||||
def text(self, text, x, y, c):
|
||||
self._display.text(text, x, y, c)
|
||||
|
||||
def show(self):
|
||||
self._display.show()
|
26
main.py
26
main.py
|
@ -1,4 +1,24 @@
|
|||
from menu import Menu
|
||||
from icecream import ic
|
||||
import os
|
||||
from menu import Menu, MenuItem, MenuType
|
||||
from cinput import ControlInput
|
||||
from graphics import Graphics
|
||||
|
||||
ic(Menu)
|
||||
menu_config = [
|
||||
MenuItem("Information",
|
||||
MenuType.EXEC_PLUGIN),
|
||||
MenuItem("Apps",
|
||||
MenuType.SUB_MENU),
|
||||
MenuItem("Reboot",
|
||||
MenuType.EXEC_CMD),
|
||||
MenuItem("Shutdown",
|
||||
MenuType.EXEC_CMD)]
|
||||
|
||||
cinput = ControlInput()
|
||||
graphics = Graphics()
|
||||
menu = Menu(menu_config, cinput, graphics)
|
||||
try:
|
||||
menu.get_selection()
|
||||
except KeyboardInterrupt:
|
||||
graphics.clear()
|
||||
graphics.show()
|
||||
os._exit(0)
|
||||
|
|
46
menu.py
46
menu.py
|
@ -1,5 +1,10 @@
|
|||
from board import SCL, SDA
|
||||
from busio import I2C
|
||||
from adafruit_ssd1306 import SSD1306_I2C
|
||||
from enum import Enum, auto
|
||||
from dataclasses import dataclass
|
||||
from cinput import ControlInput, Button
|
||||
from graphics import Graphics
|
||||
|
||||
class MenuType(Enum):
|
||||
SUB_MENU = auto()
|
||||
|
@ -11,12 +16,35 @@ class MenuItem:
|
|||
display: str
|
||||
menu_type: MenuType
|
||||
|
||||
Menu = [
|
||||
MenuItem("df -h",
|
||||
MenuType.EXEC_CMD),
|
||||
MenuItem("Chess",
|
||||
MenuType.EXEC_PLUGIN),
|
||||
MenuItem("Reboot",
|
||||
MenuType.EXEC_CMD),
|
||||
MenuItem("Shutdown",
|
||||
MenuType.EXEC_CMD)]
|
||||
class Menu:
|
||||
def __init__(
|
||||
self, config: list[MenuItem], cinput: ControlInput, graphics: Graphics):
|
||||
self._selected_index = 0
|
||||
self._menu = config
|
||||
self._input = cinput
|
||||
self._graphics = graphics
|
||||
|
||||
def _print(self, items: list[MenuItem]):
|
||||
self._graphics.clear()
|
||||
top_index = 0 if self._selected_index < Graphics.MAX_LINES else Graphics.MAX_LINES - self._selected_index + 1
|
||||
|
||||
for idx in range(top_index, top_index + Graphics.MAX_LINES):
|
||||
if idx >= len(items):
|
||||
break
|
||||
marker = "> " if idx == self._selected_index else " "
|
||||
self._graphics.text(marker + items[idx].display, 0, (idx - top_index) * Graphics.LINE_HEIGHT, 1)
|
||||
|
||||
self._graphics.show()
|
||||
|
||||
def get_selection(self):
|
||||
while True:
|
||||
self._print(self._menu)
|
||||
pressed = self._input.get_one_shot()
|
||||
if pressed == Button.DIR_U:
|
||||
self._selected_index -= 1
|
||||
if self._selected_index < 0:
|
||||
self._selected_index = len(self._menu) - 1
|
||||
elif pressed == Button.DIR_D:
|
||||
self._selected_index += 1
|
||||
if self._selected_index >= len(self._menu):
|
||||
self._selected_index = 0
|
||||
|
|
Reference in a new issue