software/raspi: kivy code dump
This commit is contained in:
parent
011c0b3a58
commit
ba3e9a19b4
12
software/raspi/config.yml
Normal file
12
software/raspi/config.yml
Normal file
@ -0,0 +1,12 @@
|
||||
tasks:
|
||||
- module: key # klucz
|
||||
- module: code # potencjometr
|
||||
- module: key # kulka
|
||||
- module: key # korba
|
||||
- module: code # szuflada mała
|
||||
- module: barcode # kody paskowe
|
||||
- module: snake
|
||||
speed: 6
|
||||
- module: microphone # ??
|
||||
- module: nfc # ??
|
||||
- module: final_microphone
|
13
software/raspi/sejf.kv
Normal file
13
software/raspi/sejf.kv
Normal file
@ -0,0 +1,13 @@
|
||||
<Sejf>:
|
||||
orientation: "vertical"
|
||||
BoxLayout:
|
||||
id: progress
|
||||
size_hint_y: 0.1
|
||||
Label:
|
||||
text: "hello"
|
||||
ScreenManager:
|
||||
id: sm
|
||||
Screen:
|
||||
name: "splashscreen"
|
||||
Label:
|
||||
text: "Splash screen"
|
89
software/raspi/sejf.py
Normal file
89
software/raspi/sejf.py
Normal file
@ -0,0 +1,89 @@
|
||||
import os
|
||||
import logging
|
||||
|
||||
os.environ["KIVY_NO_CONSOLELOG"] = "1"
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
import kivy
|
||||
|
||||
kivy.require("1.0.7")
|
||||
|
||||
from kivy.app import App
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.label import Label
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
|
||||
logging.getLogger("kivy").setLevel(level=logging.WARN)
|
||||
|
||||
logger = logging.getLogger("core")
|
||||
|
||||
from sejf.modules.base import BaseModule
|
||||
from sejf.modules.snake import SnakeModule
|
||||
from sejf.modules.barcode import BarcodeModule
|
||||
|
||||
modules = {
|
||||
"snake": SnakeModule,
|
||||
"barcode": BarcodeModule,
|
||||
"input": InputModule,
|
||||
}
|
||||
|
||||
|
||||
class Sejf(BoxLayout):
|
||||
pass
|
||||
|
||||
|
||||
class ProgressReport(Label):
|
||||
task = ObjectProperty(None)
|
||||
|
||||
def on_task(self, obj, v: BaseModule = None):
|
||||
v.bind(finished=lambda o, v: self.update_text())
|
||||
self.update_text()
|
||||
|
||||
def update_text(self):
|
||||
if self.task.finished:
|
||||
self.text = "%s: done" % (self.task.__class__.__name__,)
|
||||
self.color = (0.0, 1.0, 0.0, 1.0)
|
||||
elif self.task.enabled:
|
||||
self.text = "%s: inprogress" % (self.task.__class__.__name__,)
|
||||
self.color = (1.0, 1.0, 0.0, 1.0)
|
||||
else:
|
||||
self.text = "%s: nope" % (self.task.__class__.__name__,)
|
||||
self.text.color = (0.3, 0.3, 0.3, 1.0)
|
||||
|
||||
|
||||
class SejfApp(App):
|
||||
def __init__(self, config, *args, **kwargs):
|
||||
logger.info("Loading modules...")
|
||||
logger.info("%r", config)
|
||||
|
||||
self.tasks = []
|
||||
|
||||
for i, task in enumerate(config["tasks"]):
|
||||
if task["module"] in modules:
|
||||
logger.info("%d: %r", i, task)
|
||||
conf = task.copy()
|
||||
conf.pop("module")
|
||||
self.tasks.append(modules[task["module"]](self, conf))
|
||||
else:
|
||||
logger.warn("%d: %r - unsupported module", i, task)
|
||||
|
||||
super(SejfApp, self).__init__(*args, **kwargs)
|
||||
|
||||
def build(self):
|
||||
main = Sejf()
|
||||
|
||||
for task in self.tasks:
|
||||
task.build(main)
|
||||
w = main.ids["progress"]
|
||||
w.add_widget(ProgressReport(task=task))
|
||||
|
||||
return main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import yaml
|
||||
|
||||
with open("config.yml") as fd:
|
||||
config = yaml.load(fd)
|
||||
SejfApp(config).run()
|
0
software/raspi/sejf/__init__.py
Normal file
0
software/raspi/sejf/__init__.py
Normal file
0
software/raspi/sejf/modules/__init__.py
Normal file
0
software/raspi/sejf/modules/__init__.py
Normal file
25
software/raspi/sejf/modules/barcode.py
Normal file
25
software/raspi/sejf/modules/barcode.py
Normal file
@ -0,0 +1,25 @@
|
||||
from sejf.modules.base import BaseModule, Schema, fields
|
||||
from kivy.core.window import Window
|
||||
|
||||
|
||||
class BarcodeModule(BaseModule):
|
||||
text_buffer: str = ""
|
||||
|
||||
class ConfigSchema(Schema):
|
||||
code = fields.String(missing="jp2gmd")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BarcodeModule, self).__init__(*args, **kwargs)
|
||||
|
||||
self._keyboard = Window.request_keyboard(None, self, "text")
|
||||
self._keyboard.bind(on_textinput=self._on_keyboard_down)
|
||||
|
||||
def build(self, parent):
|
||||
return None
|
||||
|
||||
def _on_keyboard_down(self, widget, text):
|
||||
self.text_buffer += text
|
||||
self.text_buffer = self.text_buffer[-len(self.config["code"]) :]
|
||||
|
||||
if self.text_buffer == self.config["code"]:
|
||||
self.finished = True
|
20
software/raspi/sejf/modules/base.py
Normal file
20
software/raspi/sejf/modules/base.py
Normal file
@ -0,0 +1,20 @@
|
||||
from marshmallow import Schema, fields
|
||||
from kivy.event import EventDispatcher
|
||||
from kivy.properties import BooleanProperty
|
||||
|
||||
|
||||
class BaseModule(EventDispatcher):
|
||||
ConfigSchema = None
|
||||
enabled = BooleanProperty(True)
|
||||
finished = BooleanProperty(False)
|
||||
|
||||
def __init__(self, parent, config):
|
||||
self.parent = parent
|
||||
if self.ConfigSchema:
|
||||
self.config = self.ConfigSchema().load(config)
|
||||
print(self.config)
|
||||
else:
|
||||
self.config = config
|
||||
|
||||
def build(self, parent):
|
||||
pass
|
214
software/raspi/sejf/modules/snake.py
Normal file
214
software/raspi/sejf/modules/snake.py
Normal file
@ -0,0 +1,214 @@
|
||||
import logging
|
||||
import random
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.uix.label import Label
|
||||
from kivy.core.window import Window
|
||||
from kivy.graphics import Ellipse, Rectangle, Color
|
||||
from kivy.uix.screenmanager import Screen
|
||||
|
||||
from kivy.properties import (
|
||||
NumericProperty,
|
||||
ListProperty,
|
||||
StringProperty,
|
||||
ObjectProperty,
|
||||
BooleanProperty,
|
||||
)
|
||||
from .base import BaseModule, Schema, fields
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SnakeCell(Label):
|
||||
type = StringProperty("head")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SnakeCell, self).__init__(*args, **kwargs)
|
||||
|
||||
with self.canvas:
|
||||
self.c = Color()
|
||||
self.rect = Rectangle()
|
||||
|
||||
self.bind(
|
||||
x=self.update_rect,
|
||||
y=self.update_rect,
|
||||
size=self.update_rect,
|
||||
pos=self.update_rect,
|
||||
)
|
||||
|
||||
def update_rect(self, *args):
|
||||
if self.type == "head":
|
||||
self.c.rgba = (1.0, 0, 0, 1.0)
|
||||
elif self.type == "fruit":
|
||||
self.c.rgba = (1.0, 1.0, 0, 1.0)
|
||||
else:
|
||||
self.c.rgba = (1.0, 1.0, 1.0, 1.0)
|
||||
|
||||
self.rect.size = sz = [15, 15]
|
||||
self.rect.pos = [
|
||||
(self.x + 0.5) * self.parent.width / self.parent.col_width - sz[0] / 2,
|
||||
((self.parent.col_height - self.y - 1) + 0.5)
|
||||
* self.parent.height
|
||||
/ self.parent.col_height
|
||||
- sz[0] / 2,
|
||||
]
|
||||
|
||||
def clone(self, **kwargs):
|
||||
return SnakeCell(x=self.x, y=self.y, **kwargs)
|
||||
|
||||
def collide(self, other):
|
||||
return self.x == other.x and self.y == other.y
|
||||
|
||||
def __repr__(self):
|
||||
return "<SnakeCell(%d,%d)>" % (self.x, self.y)
|
||||
|
||||
|
||||
class SnakeWidget(Label):
|
||||
interval = None
|
||||
direction = ObjectProperty([1, 0])
|
||||
|
||||
head = ObjectProperty(None)
|
||||
tail = ObjectProperty([])
|
||||
fruits = ObjectProperty([])
|
||||
|
||||
tail_length = NumericProperty(5)
|
||||
|
||||
col_width = NumericProperty(16)
|
||||
col_height = NumericProperty(10)
|
||||
speed = NumericProperty(3)
|
||||
|
||||
running = BooleanProperty(False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SnakeWidget, self).__init__(*args, **kwargs)
|
||||
|
||||
self._keyboard = Window.request_keyboard(None, self, "text")
|
||||
self._keyboard.bind(on_key_down=self._on_keyboard_down)
|
||||
self.bind(
|
||||
pos=self.update_rects, size=self.update_rects,
|
||||
)
|
||||
|
||||
def reset(self):
|
||||
self.remove_widget(self.head)
|
||||
for t in self.tail:
|
||||
self.remove_widget(t)
|
||||
for f in self.fruits:
|
||||
self.remove_widget(f)
|
||||
|
||||
self.tail = []
|
||||
self.fruits = []
|
||||
|
||||
self.head = SnakeCell(x=8, y=5)
|
||||
self.add_widget(self.head, index=0)
|
||||
|
||||
self.add_fruit()
|
||||
|
||||
def on_parent(self, *args):
|
||||
self.reset()
|
||||
|
||||
def on_running(self, *args):
|
||||
print("on_running", self.running, self.speed)
|
||||
if self.running and not self.interval:
|
||||
self.interval = Clock.schedule_interval(self.tick, 1 / self.speed)
|
||||
elif not self.running and self.interval:
|
||||
self.interval.cancel()
|
||||
self.interval = None
|
||||
|
||||
def update_rects(self, *args):
|
||||
for c in self.fruits + self.tail + [self.head]:
|
||||
c.update_rect()
|
||||
|
||||
def tick(self, dt):
|
||||
n = self.head.clone(type="tail")
|
||||
self.tail.insert(0, n)
|
||||
self.add_widget(n, index=1)
|
||||
n.update_rect()
|
||||
|
||||
for to_rem in self.tail[self.tail_length :]:
|
||||
self.remove_widget(to_rem)
|
||||
self.tail = self.tail[: self.tail_length]
|
||||
|
||||
self.head.x = (self.head.x + self.direction[0]) % self.col_width
|
||||
self.head.y = (self.head.y + self.direction[1]) % self.col_height
|
||||
if any(self.head.collide(t) for t in self.tail):
|
||||
self.text = "collision!"
|
||||
self.running = False
|
||||
# self.interval.cancel()
|
||||
|
||||
for f in self.fruits[::-1]:
|
||||
if self.head.collide(f):
|
||||
self.fruits.remove(f)
|
||||
self.remove_widget(f)
|
||||
self.tail_length += 1
|
||||
self.add_fruit()
|
||||
|
||||
def _on_keyboard_down(self, widget, event, *args):
|
||||
directions = {
|
||||
"up": (0, -1),
|
||||
"down": (0, 1),
|
||||
"left": (-1, 0),
|
||||
"right": (1, 0),
|
||||
}
|
||||
if event[1] in directions:
|
||||
new_dir = directions[event[1]]
|
||||
old_dir = self.direction
|
||||
if new_dir[0] == -old_dir[0] or old_dir[1] == -new_dir[1]:
|
||||
return
|
||||
self.direction = new_dir
|
||||
|
||||
def add_fruit(self):
|
||||
for n in range(5):
|
||||
c = SnakeCell(
|
||||
x=random.randint(0, self.col_width - 1),
|
||||
y=random.randint(0, self.col_height - 1),
|
||||
type="fruit",
|
||||
)
|
||||
|
||||
if any(c.collide(o) for o in self.tail + self.fruits + [self.head]):
|
||||
continue
|
||||
|
||||
self.fruits.append(c)
|
||||
self.add_widget(c)
|
||||
c.update_rect()
|
||||
break
|
||||
|
||||
|
||||
class SnakeModule(BaseModule):
|
||||
class ConfigSchema(Schema):
|
||||
speed = fields.Integer(missing=5)
|
||||
target_score = fields.Integer(missing=8)
|
||||
start_length = fields.Integer(missing=2)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SnakeModule, self).__init__(*args, **kwargs)
|
||||
logger.info("Initializing...")
|
||||
|
||||
def build(self, parent):
|
||||
self.screen = Screen(name="snek")
|
||||
print(self.config)
|
||||
self.widget = SnakeWidget(tail_length=self.config["start_length"])
|
||||
self.widget.speed = self.config["speed"]
|
||||
# self.widget.running = True
|
||||
self.update_running_state()
|
||||
self.bind(
|
||||
finished=lambda w, v: self.update_running_state(),
|
||||
enabled=lambda w, v: self.update_running_state(),
|
||||
)
|
||||
self.widget.bind(tail_length=self.on_score)
|
||||
self.screen.add_widget(self.widget)
|
||||
parent.ids["sm"].add_widget(self.screen)
|
||||
|
||||
Clock.schedule_interval(self.tick, 0.2)
|
||||
self.sm = parent.ids["sm"]
|
||||
|
||||
def tick(self, dt):
|
||||
if self.enabled:
|
||||
self.sm.switch_to(self.screen)
|
||||
|
||||
def on_score(self, widget, score):
|
||||
print("on_score", score)
|
||||
if score >= self.config["target_score"]:
|
||||
self.finished = True
|
||||
|
||||
def update_running_state(self):
|
||||
self.widget.running = self.enabled and not self.finished
|
Loading…
x
Reference in New Issue
Block a user