--- /dev/null
+from datetime import datetime
+from math import cos, sin, tau
+
+import pygame
+
+from ui import Child
+
+
+class ClockWidget(Child):
+ CLOCK_RADIUS = 3 / 8
+ MARKINGS = (3 / 4, 15 / 16)
+ HANDS = (
+ (-1 / 16, 3 / 16), # fullday
+ (-1 / 16, 7 / 16), # hour
+ (-1 / 16, 3 / 4), # minute
+ (-1 / 8, 13 / 16), # second
+ )
+ WIDTHS = (
+ 0.0325, # fullday
+ 0.02625, # hour
+ 0.0175, # minute
+ 0.005, # second
+ )
+
+ def __init__(self, parent, rect):
+ super().__init__(parent)
+ self.rect = rect
+ min_rect_size = min(rect.size)
+ self.radius = min_rect_size * self.CLOCK_RADIUS
+ self.widths = tuple(w * min_rect_size for w in self.WIDTHS)
+ self.state = (0, 0, 0, 0, 0, 0)
+
+ def update(self):
+ now = datetime.now()
+ state = (
+ now.strftime("%b"),
+ now.day,
+ now.hour,
+ now.minute,
+ now.second,
+ now.microsecond,
+ )
+ if state != self.state:
+ self.state = state
+ self.dirty = True
+
+ def draw(self):
+ month, day, hour, minute, second, microsecond = self.state
+ second = second + microsecond / 1000000
+ fractional_minute = minute + second / 60
+ fractional_hour = hour + fractional_minute / 60
+ self.draw_markings()
+ self.draw_date(month, "gold")
+ self.draw_date(str(day), "gold", True)
+ self.draw_hand_polygon(
+ fractional_hour * tau / 24, self.HANDS[0], self.widths[0], "purple"
+ )
+ if fractional_hour >= 13:
+ fractional_hour -= 12
+ self.draw_hand_polygon(
+ fractional_hour * tau / 12, self.HANDS[1], self.widths[1], "lightgreen"
+ )
+ self.draw_hand_polygon(
+ fractional_minute * tau / 60, self.HANDS[2], self.widths[2], "darkcyan"
+ )
+ self.draw_hand_polygon(second * tau / 60, self.HANDS[3], self.widths[3], "blue")
+
+ def draw_markings(self):
+ center = self.rect.center
+ radius = self.radius
+ markings = tuple(m * radius for m in self.MARKINGS)
+ for i in range(12):
+ angle = tau * i / 12
+ fractions = cos(angle), -sin(angle)
+ pygame.draw.line(
+ self.surf,
+ "green",
+ tuple(c + (f * markings[0]) for c, f in zip(center, fractions)),
+ tuple(c + (f * radius) for c, f in zip(center, fractions)),
+ )
+ for j in range(i * 5 + 1, (i + 1) * 5):
+ angle = tau * j / 60
+ fractions = cos(angle), -sin(angle)
+ pygame.draw.line(
+ self.surf,
+ "yellow",
+ tuple(c + (f * markings[1]) for c, f in zip(center, fractions)),
+ tuple(c + (f * radius) for c, f in zip(center, fractions)),
+ )
+ pygame.draw.circle(self.surf, "green", center, radius, 1)
+
+ def draw_date(self, string, color, right=False):
+ fs = self.font.render(string, True, color)
+ if right:
+ x_offset = self.MARKINGS[0] * self.radius - fs.get_width()
+ else:
+ x_offset = -self.MARKINGS[0] * self.radius
+ self.surf.blit(
+ fs,
+ (
+ self.rect.centerx + x_offset,
+ self.rect.centery - fs.get_height() / 2,
+ ),
+ )
+
+ def draw_hand_polygon(self, angle, interval, width, color):
+ # fix origin and direction
+ angle = tau / 4 - angle
+ center = self.rect.center
+ radius = self.radius
+ fractions = cos(angle), -sin(angle)
+ points = (
+ tuple(c + (f * interval[0] * radius) for c, f in zip(center, fractions)),
+ tuple(c + (f * interval[1] * radius) for c, f in zip(center, fractions)),
+ )
+ offset = (cos(angle + tau / 4) * width / 2, -sin(angle + tau / 4) * width / 2)
+ pygame.draw.polygon(
+ self.surf,
+ color,
+ [
+ (points[0][0] - offset[0], points[0][1] - offset[1]),
+ (points[0][0] + offset[0], points[0][1] + offset[1]),
+ (points[1][0] + offset[0], points[1][1] + offset[1]),
+ (points[1][0] - offset[0], points[1][1] - offset[1]),
+ ]
+ )
--- /dev/null
+import pygame
+
+from ui import Child
+from ui.multitouch import MultitouchHandler
+
+
+class DraggableButton(Child):
+ def __init__(self, parent, rect, surf, dests, callback, highlight=False):
+ super().__init__(parent)
+ assert rect.size == surf.get_size()
+ self.rect = rect
+ self.surface = surf
+ self.dests = dests
+ self.callback = callback
+ self.highlight = highlight
+ self.pushed = None
+ self.drag_pos = None
+ self.drag_rel = None
+
+ def draw(self):
+ if not self.pushed:
+ value_color = "lime" if self.highlight else "gray"
+ colors = ("black", value_color, value_color)
+ else:
+ colors = ("darkgray", "lightgray", "black")
+ pygame.draw.rect(self.surf, colors[0], self.rect)
+ pygame.draw.rect(self.surf, colors[1], self.rect, 8)
+ self.surf.blit(self.surface, self.rect.topleft)
+ if self.drag_pos is not None:
+ for dest in self.dests:
+ width = 6
+ if dest.collidepoint(self.drag_pos):
+ dest = dest.inflate((128, 128))
+ width *= 2
+ pygame.draw.rect(self.surf, "red", dest, width)
+ self.surf.blit(
+ self.surface,
+ (
+ self.drag_pos[0] - self.drag_rel[0],
+ self.drag_pos[1] - self.drag_rel[1],
+ ),
+ )
+
+ def toggle(self, pos=None, pushed=None):
+ if pos is None:
+ self.drag_pos = None
+ self.drag_rel = None
+ else:
+ if self.drag_rel is None:
+ self.drag_rel = (pos[0] - self.rect.left, pos[1] - self.rect.top)
+ self.drag_pos = pos
+ self.pushed = pushed
+ self.dirty = True
+
+ def handle_mousebuttondown(self, ev):
+ if ev.button == 1 and self.rect.collidepoint(ev.pos):
+ self.toggle(ev.pos, True)
+
+ def handle_mousemotion(self, ev):
+ if self.pushed is not True:
+ return
+ if not ev.buttons[0]:
+ self.toggle()
+ else:
+ self.drag_pos = ev.pos
+
+ def handle_mousebuttonup(self, ev):
+ if ev.button != 1 or self.pushed is not True:
+ return
+ self.drop(ev.pos)
+ self.toggle()
+
+ def drop(self, pos):
+ for i, dest in enumerate(self.dests):
+ if dest.collidepoint(pos):
+ self.rect, self.dests[i] = dest, self.rect
+ self.callback()
+ break
+
+ def handle_fingerdown(self, event):
+ pos = MultitouchHandler.map_coords((event.x, event.y), self.surf.get_size())
+ if self.pushed is None and self.rect.collidepoint(pos):
+ self.toggle(pos, (event.touch_id, event.finger_id))
+
+ def handle_fingermotion(self, event):
+ if self.pushed != (event.touch_id, event.finger_id):
+ return
+ self.drag_pos = MultitouchHandler.map_coords(
+ (event.x, event.y), self.surf.get_size()
+ )
+
+ def handle_fingerup(self, event):
+ if self.pushed != (event.touch_id, event.finger_id):
+ return
+ self.drop(
+ MultitouchHandler.map_coords((event.x, event.y), self.surf.get_size())
+ )
+ self.toggle()
self.press()
def handle_mousemotion(self, event):
- if event.buttons[0] and not self.rect.collidepoint(event.pos):
+ if not event.buttons[0] or not self.rect.collidepoint(event.pos):
self.release()
def handle_mousebuttonup(self, event):
return (size[0] / x_sum, size[1] / x_sums_len)
-def get_keyboard_keys(rect, parent, callback, keys):
+def get_keyboard_keys(parent, rect, callback, keys):
result = []
x_sums = [0]
y_spans = []
#!/usr/bin/env python3
from pynput.keyboard import Controller as KeyboardController, Key, KeyCode
-from pynput._util.xorg_keysyms import KEYPAD_KEYS
+from pynput.mouse import Controller as MouseController, Button
from launch import pygame
from multitouch import MultitouchHandler
from ui import BaseRoot, FPSWidget
-from .key import CURSOR_KEYS, KEYBOARD, KEYPAD_KEYS, get_keyboard_keys
-from .settings import Settings
+from .clock import ClockWidget
+from .key import KEYBOARD, get_keyboard_keys
+from .mousepad import MousePadWidget
+from .settings import SettingsModal
# add a launcher symbol
pygame.display.set_mode((0, 0), pygame.NOFRAME, display=1),
pygame.font.SysFont("DejaVu Sans", size=64),
)
- size = self.surf.get_size()
- widget_size = (size[0] // 3, size[1] // 2)
- get_keyboard_keys(
- pygame.Rect((0, 0), (size[0], widget_size[1])), self, self.key_cb, KEYBOARD
- )
- get_keyboard_keys(
- pygame.Rect((0, widget_size[1]), widget_size),
- self,
- self.key_cb,
- CURSOR_KEYS,
- )
- get_keyboard_keys(
- pygame.Rect(widget_size, widget_size), self, self.key_cb, KEYPAD_KEYS
- )
self.pushed = set()
self.mt = MultitouchHandler(
MultitouchHandler.find_device(name="ELAN9009:00 04F3:425A")
)
self.keyboard = KeyboardController()
+ self.mouse = MouseController()
+ self.mouse_fingers = {}
self.display = self.keyboard._display
self.led_mask = self.display.get_keyboard_control().led_mask
self.muted = False
FPSWidget(self)
- self.settings = Settings(
+ size = self.surf.get_size()
+ self.settings_modal = SettingsModal(
self,
pygame.Rect((size[0] // 4, size[1] // 4), (size[0] // 2, size[1] // 2)),
)
+ self.keys_index = len(self.children)
+ self.setup_widgets()
+
+ def setup_widgets(self):
+ while len(self.children) > self.keys_index:
+ self.children.pop()
+ size = self.surf.get_size()
+ half_height = size[1] // 2
+ for row in range(2):
+ widget_top = half_height * row
+ if row == self.settings_modal.layout_modal.keyboard_row:
+ get_keyboard_keys(
+ self,
+ pygame.Rect((widget_top, 0), (size[0], half_height)),
+ self.key_cb,
+ KEYBOARD,
+ )
+ continue
+ widget_row = self.settings_modal.layout_modal.widget_row
+ widget_width = size[0] // (3 if len(widget_row) == 3 else 2)
+ x = 0 if len(widget_row) > 0 else size[0] // 4
+ for keyset in widget_row:
+ rect = pygame.Rect((x, half_height), (widget_width, half_height))
+ if keyset is MousePadWidget:
+ keyset(self, rect, self.click_cb, self.move_cb)
+ elif keyset is ClockWidget:
+ keyset(self, rect)
+ else:
+ get_keyboard_keys(self, rect, self.key_cb, keyset)
+ x += widget_width
def key_cb(self, key, release=False):
if key == "Settings":
- self.settings.activate()
+ self.settings_modal.activate()
if isinstance(key, (Key, KeyCode)):
- (self.keyboard.press, self.keyboard.release)[release](key)
- if release and key in self.pushed:
- self.pushed.remove(key)
- elif not release:
+ if release:
+ self.keyboard.release(key)
+ if key in self.pushed:
+ self.pushed.remove(key)
+ else:
+ self.keyboard.press(key)
self.pushed.add(key)
+ def click_cb(self, button, *args):
+ if len(args) > 2:
+ raise TypeError(f"Too many arguments to click_callback: {args}")
+ elif len(args) == 2:
+ n, release = args
+ else:
+ n, release = None, *args
+ if release:
+ if button in self.pushed:
+ self.mouse.release(button)
+ self.pushed.remove(button)
+ elif n is not None:
+ self.mouse.click(button, n)
+ else:
+ self.mouse.press(button)
+ self.pushed.add(button)
+
+ def move_cb(self, pos, scroll=False):
+ (self.mouse.scroll if scroll else self.mouse.move)(*pos)
+
def handle_events(self):
self.mt.handle_events()
super().handle_events()
if led_mask != self.led_mask:
self.led_mask = led_mask
self.dirty = True
+# if self.mouse_frame:
+# self.move_mouse()
def run(self):
try:
super().run()
finally:
for key in self.pushed:
- self.keyboard.release(key)
+ (self.mouse if isinstance(key, Button) else self.keyboard).release(key)
--- /dev/null
+import pygame
+
+from ui import Child, QuittableModal, Rect
+
+from .clock import ClockWidget
+from .draggable import DraggableButton
+from .key import apply_y_spans, get_key_size
+from .mousepad import MousePadWidget
+from .touchbutton import TouchButton
+from .key import KEYBOARD, KEYPAD_KEYS, CURSOR_KEYS
+
+
+
+
+class LayoutWidget(Child):
+ padding = 8
+
+ @classmethod
+ def get_keyboard_keys(cls, surf, size, keys):
+ result = []
+ x_sums = [0]
+ y_spans = []
+ for key in keys:
+ if key == "\n":
+ x_sums.append(0)
+ apply_y_spans(x_sums, y_spans)
+ elif len(key) > 2:
+ width = key[2].get("x_hint", 1)
+ x_sums[-1] += width
+ y_hint = key[2].get("y_hint", 1)
+ if y_hint > 1:
+ y_spans.append([width, y_hint - 1])
+ else:
+ x_sums[-1] += 1
+ apply_y_spans(x_sums, y_spans)
+ assert all(item[1] <= 0 for item in y_spans)
+ x, y = (0, 0)
+ x_sums_iter = iter(x_sums)
+ x_sums_len = len(x_sums)
+ key_size = get_key_size(size, x_sums_iter, x_sums_len)
+ for key in keys:
+ if key == "\n":
+ x, y = 0, y + key_size[1]
+ key_size = get_key_size(size, x_sums_iter, x_sums_len)
+ continue
+ key_width, key_height = key_size
+ if key != (None, None):
+ if len(key) > 2:
+ key_width *= key[2].get("x_hint", 1)
+ key_height *= key[2].get("y_hint", 1)
+ rect = pygame.Rect(
+ (x + cls.padding / 2, y + cls.padding / 2),
+ (key_width - cls.padding, key_height - cls.padding),
+ )
+ pygame.draw.rect(surf, "gray", rect, 1)
+ pygame.draw.ellipse(
+ surf,
+ "0x333333",
+ pygame.Rect(
+ (
+ rect.centerx - key_size[0] / 6,
+ rect.centery - key_size[1] / 4,
+ ),
+ (key_size[0] / 3, key_size[1] / 2),
+ )
+ )
+ x += key_width
+ return result
+
+ def get_widget_row_rects(self, num_widgets):
+ if self.parent.keyboard_row == 0:
+ top = self.rect.centery
+ else:
+ top = self.rect.top
+ size = (self.rect.width / 3, self.rect.height / 2)
+ rects = []
+ if num_widgets == 1:
+ rects.append(
+ pygame.Rect(
+ (self.rect.left + size[0], top),
+ size,
+ )
+ )
+ elif num_widgets == 2:
+ rects.append(
+ pygame.Rect(
+ (self.rect.left + self.rect.width / 4 - size[0] / 2, top),
+ size,
+ )
+ )
+ rects.append(
+ pygame.Rect(
+ (self.rect.left + self.rect.width * 3 / 4 - size[0] / 2, top),
+ size,
+ )
+ )
+ elif num_widgets == 3:
+ for i in range(3):
+ rects.append(
+ pygame.Rect(
+ (self.rect.left + i * size[0], top),
+ size,
+ )
+ )
+ return rects
+
+ def get_widget_row_rect_dests(self, len_widget_row, keyset, source_rect):
+ if keyset in self.parent.widget_row:
+ keypad_dests = self.get_widget_row_rects(len_widget_row)
+ keypad_idx = self.parent.widget_row.index(KEYPAD_KEYS)
+ keypad_dests[keypad_idx], source_rect = source_rect, keypad_dests[keypad_idx]
+ elif len_widget_row < 3:
+ keypad_dests = self.get_widget_row_rects(len_widget_row + 1)
+ else:
+ keypad_dests = []
+ return source_rect, keypad_dests
+
+ def __init__(self, parent, rect):
+ super().__init__(parent)
+ self.rect = rect
+ keyboard_size = (rect.width, rect.height / 2)
+ self.keyboard_rects = [
+ pygame.Rect(rect.topleft, keyboard_size),
+ pygame.Rect((rect.left, rect.centery), keyboard_size),
+ ]
+ widget_size = (rect.width / 3, keyboard_size[1])
+ self.widget_rects = [
+ [
+ pygame.Rect(
+ (rect.left + rect.width / 4 - widget_size[0] / 2, rect.top),
+ widget_size,
+ ),
+ pygame.Rect(
+ (rect.left + rect.width * 3 / 4 - widget_size[0] / 2, rect.top),
+ widget_size,
+ )
+ ],
+ [
+ pygame.Rect((rect.left, rect.top), widget_size),
+ pygame.Rect((rect.left + widget_size[0], rect.top), widget_size),
+ pygame.Rect((rect.left + 2 * widget_size[1], rect.top), widget_size),
+ ],
+ ]
+ surf = pygame.Surface(keyboard_size)
+ surf.fill("black")
+ self.get_keyboard_keys(surf, keyboard_size, KEYBOARD)
+ self.keyboard = DraggableButton(
+ self.parent,
+ self.keyboard_rects[self.parent.keyboard_row],
+ surf,
+ [self.keyboard_rects[not self.parent.keyboard_row]],
+ self.keyboard_callback,
+ )
+ #self.keypad_rect = pygame.Rect(
+ # (
+ # self.parent.rect.left + (
+ # self.rect.left - self.parent.rect.left
+ # ) / 2 - self.rect.width / 6,
+ # self.parent.rect.top + 96,
+ # ),
+ # (self.rect.width / 3, self.rect.height / 2),
+ #)
+ #surf = pygame.Surface(self.keypad_rect.size)
+ #surf.fill("black")
+ #self.get_keyboard_keys(surf, self.keypad_rect.size, KEYPAD_KEYS)
+ #keypad_rect, keypad_dests = self.get_widget_row_rect_dests(
+ # len(parent.widget_row), KEYPAD_KEYS, self.keypad_rect
+ #)
+ #self.keypad = DraggableButton(
+ # self.parent,
+ # keypad_rect,
+ # surf,
+ # keypad_dests,
+ # self.keypad_callback,
+ #)
+
+ def keyboard_callback(self):
+ self.parent.keyboard_row = int(
+ (self.keyboard.rect.top - self.rect.top) * 2 / self.rect.height
+ )
+ # move the visual widget row out of the way here
+
+ def keypad_callback(self):
+ if KEYPAD_KEYS in self.parent.widget_row:
+ self.parent.widget_row.remove(KEYPAD_KEYS)
+ if self.keypad.rect != self.keypad_rect:
+ if len(self.parent.widget_row) == 0:
+ self.parent.widget_row.append(KEYPAD_KEYS)
+ return
+ # insert KEYPAD_KEYS to widget_row at the right index
+ # rearrange the visual widget row
+
+ def draw(self):
+ pygame.draw.rect(self.surf, "gray", self.rect.inflate((-2, -2)), 1)
+
+
+class LayoutModal(QuittableModal):
+ def __init__(self, parent):
+ super().__init__(parent)
+ size = self.surf.get_size()
+ self.rect = pygame.Rect(150, 150, size[0] - 300, size[1] - 300)
+ Rect(self, self.rect, "black", "gray")
+ TouchButton(
+ self,
+ pygame.Rect((self.rect.centerx - 128, self.rect.bottom - 128), (256, 128)),
+ "Close",
+ self.deactivate,
+ )
+ self.keyboard_row = 0
+ self.widget_row = [CURSOR_KEYS, KEYPAD_KEYS, ClockWidget]
+ half_size = (size[0] / 2, size[1] / 2)
+ LayoutWidget(self, pygame.Rect((half_size[0] / 2, half_size[1] / 2), half_size))
--- /dev/null
+from functools import partial
+
+import pygame
+from pynput.mouse import Button
+
+from ui import Child
+from ui.multitouch import MultitouchHandler
+
+from .touchbutton import TouchButton
+
+
+BUTTONS = (
+ ("Dbl←", (Button.left, 2)),
+ ("←", (Button.left,)),
+ ("↓", (Button.middle,)),
+ ("→", (Button.right,)),
+)
+
+
+class MousePadWidget(Child):
+ def __init__(self, parent, rect, click_cb, move_cb):
+ super().__init__(parent)
+ self.rect = rect
+ button_size = (rect.width / len(BUTTONS), rect.height / 6)
+ for i, (value, args) in enumerate(BUTTONS):
+ TouchButton(
+ parent,
+ pygame.Rect(
+ (
+ rect.left + i * button_size[0],
+ rect.top + rect.height - button_size[1],
+ ),
+ button_size,
+ ),
+ value,
+ partial(click_cb, *args)
+ )
+ MousePadArea(
+ parent,
+ pygame.Rect(rect.topleft, (rect.width, rect.height - button_size[1])),
+ move_cb,
+ )
+
+
+class MousePadArea(Child):
+ def __init__(self, parent, rect, move_cb):
+ super().__init__(parent)
+ self.rect = rect
+ self.move_cb = move_cb
+ self.fingers = {}
+
+ def draw(self):
+ pygame.draw.rect(self.surf, "yellow", self.rect, 1)
+
+ def handle_fingerdown(self, event):
+ pos = (event.x, event.y)
+ if self.rect.collidepoint(
+ MultitouchHandler.map_coords(pos, self.surf.get_size())
+ ):
+ self.fingers[(event.touch_id, event.finger_id)] = [pos]
+
+ def handle_fingermotion(self, event):
+ key = (event.touch_id, event.finger_id)
+ if key not in self.fingers:
+ return
+ pos = (event.x, event.y)
+ if self.rect.collidepoint(
+ MultitouchHandler.map_coords(pos, self.surf.get_size())
+ ):
+ self.fingers[key].append(pos)
+ else:
+ self.fingers.pop(key)
+
+ def handle_fingerup(self, event):
+ key = (event.touch_id, event.finger_id)
+ if key not in self.fingers:
+ return
+ pos = (event.x, event.y)
+ if self.rect.collidepoint(
+ MultitouchHandler.map_coords(pos, self.surf.get_size())
+ ):
+ pass
+ self.fingers.pop(key)
import os
import sys
+from functools import partial
from pathlib import Path
import pygame
-from ui import Modal, Rect
+from ui import QuittableModal, Rect
from .touchbutton import TouchButton
+from .layout import LayoutModal
# what settings do we need?
# - configure widgets
# - three available widgets: touchpad mouse, number keypad, cursor and navkeys
-class Settings(Modal):
+class SettingsModal(QuittableModal):
def __init__(self, parent, rect):
super().__init__(parent)
self.rect = rect
- Rect(self, self.rect, "black", "gray")
+ Rect(self, rect, "black", "gray")
width = rect.width * 2 / 3
height = rect.height / 7
left = rect.left + (rect.width - width) / 2 + 8
y = rect.top + height + 8
rect_size = (width - 16, height - 16)
- self.buttons = []
+ self.layout_modal = LayoutModal(parent)
for args in (
- ("Mute", self.mute, self.root.muted),
- ("Layout...", self.layout),
+ ("Mute", None, self.root.muted),
+ ("Layout...", self.layout_modal.activate),
("Restart", self.restart),
("Exit", self.root.handle_quit),
("Back", self.deactivate),
):
- self.buttons.append(
- TouchButton(self, pygame.Rect((left, y), rect_size), *args)
- )
+ button = TouchButton(self, pygame.Rect((left, y), rect_size), *args)
+ if args[0] == "Mute":
+ button.callback = partial(self.mute, button)
y += height
- def mute(self):
+ def mute(self, button):
self.root.muted ^= True
- self.buttons[0].highlight = self.root.muted
-
- def layout(self):
- print("stub layout")
+ button.highlight = self.root.muted
def restart(self):
executable = Path(sys.executable).name
from traceback import format_exception
executable = Path(sys.executable).name
+pkgs = ("pygame", "evdev", "pynput")
def tty_connected():
raise ChildProcessError(os.linesep.join(lines))
-def check_has_pygame():
- return b"No module named pygame.__main__;" in run_cmd(
- (executable, "-m", "pygame"), 1
+def check_has_pkg(pkg):
+ return f"No module named {pkg}.__main__;".encode() in run_cmd(
+ (executable, "-m", pkg), 1
).stderr
raise SystemError("'venv' is not a directory.")
run_cmd((sys.executable, "-m", "venv", str(venv_dir)))
os.environ.update(get_venv_environ(venv_dir))
- has_pygame = venv_dir_is_dir and check_has_pygame()
+ has_pkgs = (
+ venv_dir_is_dir
+ and all(check_has_pkg(pkg) for pkg in pkgs)
+ )
if get_ipv4_is_connected():
yesterday = time() - 86400
- if not has_pygame or all(
+ if not has_pkgs or all(
p.stat().st_mtime < yesterday for p in walk_outer(venv_dir)
):
run_cmd(
"install",
"-qU",
"pip",
- "pygame",
- "pynput",
- "evdev",
+ *pkgs
)
)
venv_dir.touch(0o755, True)
- elif not has_pygame:
+ elif not has_pkgs:
raise ConnectionError("Internet needed to install requirement: pygame")
def pre_run():
- pg = "pygame"
- if pg in sys.modules:
- return sys.modules[pg]
- try:
- with redirect_stdout(None):
- pygame = import_module(pg)
- except ImportError:
- with exception_wrapper():
- setup_venv()
- os.execl(
- sys.executable,
- executable,
- *sys.orig_argv[sys.orig_argv[0] == executable:],
- )
- exit(1)
- pygame.init()
- return pygame
+ modules = []
+ for pkg in pkgs:
+ if pkg in sys.modules:
+ modules.append(sys.modules[pkg])
+ continue
+ try:
+ with redirect_stdout(None):
+ modules.append(import_module(pkg))
+ except ImportError:
+ with exception_wrapper():
+ setup_venv()
+ os.execl(
+ sys.executable,
+ executable,
+ *sys.orig_argv[sys.orig_argv[0] == executable:],
+ )
+ exit(1)
+ if pkg == "pygame":
+ modules[-1].init()
+ return modules
def resolve_symlink(path):
try_run(".".join((module_name, module_name)))
-pygame = pre_run()
+pygame, evdev, pynput = pre_run()
if __name__ == "__main__":
main()
from secrets import choice
from launch import pygame
-from ui import BaseRoot, Button, Child, Label, Modal, TextInput
-
-
-class QuittableModal(Modal):
- def handle_quit(self, _=None):
- self.parent.handle_quit()
-
- key_methods = {frozenset(): {pygame.K_ESCAPE: handle_quit}}
+from ui import BaseRoot, Button, Child, Label, QuittableModal, TextInput
class NameMenu(QuittableModal):
from .icon_button import IconButton
from .label import Label
from .message_box import MessageBox
-from .modal import Modal
+from .modal import Modal, QuittableModal
from .parent import Parent
from .rect import Rect
from .root import BaseRoot
"MessageBox",
"Modal",
"Parent",
+ "QuittableModal",
"Rect",
"RepeatButton",
"Scroll",
def __init__(self, parent):
super().__init__(parent)
- self.current_fps = None
+ self.current_fps = 0
def update(self):
new_fps = int(self.root.clock.get_fps())
def draw(self):
self.draw_modal()
super().draw()
+
+
+class QuittableModal(Modal):
+ def handle_quit(self, _=None):
+ self.parent.handle_quit()
+
+ key_methods = {frozenset(): {pygame.K_ESCAPE: handle_quit}}