--- /dev/null
+from pathlib import Path
+
+import pygame
+
+
+class BookManager:
+ SUFFIX = ".png"
+
+ def get_pages(self):
+ free_page_no = 1
+ pages = []
+ numbered_pages = []
+ for p in self.path.iterdir():
+ if p.suffix != self.SUFFIX:
+ continue
+ try:
+ page_no = int(p.stem)
+ except ValueError:
+ pages.append(p)
+ else:
+ numbered_pages.append(p)
+ free_page_no = max(free_page_no, page_no + 1)
+ pages = sorted(pages)
+ pages.extend(sorted(numbered_pages))
+ return free_page_no, pages
+
+ def __init__(self, path: Path):
+ self.path = path
+ self.free_page_no, self.pages = self.get_pages()
+ self.current_file = self.get_new_file()
+
+ def get_new_file(self):
+ return self.path / f"{self.free_page_no}{self.SUFFIX}"
+
+ def save_file(self, surf):
+ if self.current_file not in self.pages:
+ if self.current_file.exists():
+ self.pages.clear()
+ self.free_page_no, pages = self.get_pages()
+ self.pages.extend(pages)
+ self.current_file = self.get_new_file()
+ self.pages.append(self.current_file)
+ self.free_page_no += 1
+ pygame.image.save(surf, self.current_file)
+
+ NewFile = type("NewFileType", (), {})()
+
+ def next_file(self):
+ if self.current_file not in self.pages:
+ return None
+ i = self.pages.index(self.current_file)
+ if i == len(self.pages) - 1:
+ self.current_file = self.get_new_file()
+ return self.NewFile
+ self.current_file = self.pages[i + 1]
+ return pygame.image.load(self.current_file)
+
+ def prev_file(self):
+ if self.current_file not in self.pages:
+ i = len(self.pages)
+ else:
+ i = self.pages.index(self.current_file)
+ if i == 0:
+ return None
+ self.current_file = self.pages[i - 1]
+ return pygame.image.load(self.current_file)
+from pathlib import Path
+
import pygame
from ui import Root
-from zenbook_conf.xrandr import XrandrConf
-from .bookpaint_menu import BookPaintMenu
+from .book_manager import BookManager
from .draw_image import DrawImage
+from .menu import Menu
+
+
+# - color menu; set background (default for new pages) and foreground color
+# - stub with a 6 hex-digit textinputs and a little rectangle
+# - page menu; browse through next and prev pages using stylus
+# - info layer (F3)
+# - list color and page menus in the esc menu but also using keyboard shortcuts c/p
class BookPaint(Root):
def __init__(self):
pygame.init()
- self.current_display = 0
- self.display_flags = pygame.FULLSCREEN
super().__init__(
- pygame.display.set_mode(
- (0, 0), self.display_flags, display=self.current_display
- ),
+ pygame.display.set_mode((0, 0), pygame.FULLSCREEN),
pygame.font.Font(None, size=96),
)
- self.draw_image = DrawImage(self, self.BACKGROUND_COLOR, "white")
- self.menu = BookPaintMenu(self, self.draw_image)
- self.xrandr_conf = XrandrConf(True)
+ self.menu = Menu(self)
+ self.draw_image = DrawImage(self, "white")
+ book_path = Path("/dev/shm/book")
+ if not book_path.exists():
+ book_path.mkdir(0o755)
+ self.book_manager = BookManager(book_path)
def key_escape(self):
self.menu.activate()
- KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: key_escape}}
-
- def quit(self):
- self.running = False
-
- def next_display(self):
- self.xrandr_conf.renew()
- if self.xrandr_conf.count_active() != len(pygame.display.get_desktop_sizes()):
- pygame.display.quit()
- pygame.display.init()
- reset = True
- else:
- reset = False
- num_displays = len(pygame.display.get_desktop_sizes())
- current_display = (self.current_display + 1) % num_displays
- if current_display != self.current_display:
- self.current_display = current_display
- reset = True
- if not reset:
- return
- self.surf = pygame.display.set_mode(
- (0, 0), self.display_flags, display=self.current_display
- )
- self.dirty = True
+ def save_file(self):
+ self.book_manager.save_file(self.draw_image.surface)
+ self.draw_image.image_dirty = False
+
+ def set_image(self, surf):
+ if surf == BookManager.NewFile:
+ self.draw_image.clear()
+ elif surf:
+ self.draw_image.set_image(surf)
+
+ def next_file(self):
+ if self.draw_image.image_dirty:
+ self.save_file()
+ self.set_image(self.book_manager.next_file())
+
+ def prev_file(self):
+ if self.draw_image.image_dirty:
+ self.save_file()
+ self.set_image(self.book_manager.prev_file())
+
+ KEY_METHODS = {
+ frozenset(): {pygame.K_ESCAPE: key_escape},
+ frozenset({pygame.KMOD_CTRL}): {
+ pygame.K_s: save_file,
+ pygame.K_PAGEDOWN: next_file,
+ pygame.K_PAGEUP: prev_file,
+ },
+ }
+++ /dev/null
-from functools import partial
-from pathlib import Path
-
-import pygame
-
-from layout import BarLayout, GridLayout
-from ui import (
- Button,
- CenterLabel,
- ColorButton,
- DropDown,
- Modal,
- Rect,
- RepeatButton,
- RightLabel,
- Slider,
- TextInput,
-)
-
-from .color_chooser import ColorChooser
-from .draw_image import InputMethod, StrokeMethod
-from .utils import color_to_hex
-
-
-class BookPaintMenu(Modal):
- def get_stroke_methods(self):
- return (
- self.draw_image.stroke_method.name.title(),
- [stroke_method.name.title() for stroke_method in StrokeMethod],
- )
-
- def get_input_methods(self):
- labels = InputMethod.labels()
- return (
- labels[tuple(InputMethod).index(self.draw_image.input_method)],
- labels,
- )
-
- def __init__(self, parent, draw_image):
- super().__init__(parent)
- self.draw_image = draw_image
- Button(
- self,
- pygame.Rect((self.surf.get_width() - 128, 0), (128, 128)),
- "×",
- self.root.quit,
- )
- Button(
- self,
- pygame.Rect((self.surf.get_width() - 256, 0), (128, 128)),
- "_",
- pygame.display.iconify,
- )
- Button(
- self,
- pygame.Rect((self.surf.get_width() - 384, 0), (128, 128)),
- "»",
- self.root.next_display,
- )
- size = self.surf.get_size()
- grid_layout = GridLayout(
- pygame.Rect((512, 256), (size[0] - 1024, size[1] - 768)), (3, 6)
- )
- RightLabel(
- self,
- grid_layout.get_rect((0, 0)),
- "Book Path",
- )
- RightLabel(
- self,
- grid_layout.get_rect((0, 1)),
- "New Page Color",
- )
- RightLabel(
- self,
- grid_layout.get_rect((0, 2)),
- "Line Color",
- )
- RightLabel(
- self,
- grid_layout.get_rect((0, 3)),
- "Line Width",
- )
- RightLabel(
- self,
- grid_layout.get_rect((0, 4)),
- "Stroke Method",
- )
- RightLabel(
- self,
- grid_layout.get_rect((0, 5)),
- "Input",
- )
- rect = grid_layout.get_rect((1, 0), (2, 1))
- pad = 96 - rect.height
- self.path_input = TextInput(
- self,
- rect.inflate((pad, pad)),
- self.set_book_path,
- str(Path("book/").absolute()),
- unfocused_align=TextInput.RIGHT,
- )
- color_chooser = ColorChooser(self)
- self.background_color = ColorButton(
- self,
- grid_layout.get_rect((1, 1)).inflate((pad, pad)),
- self.draw_image.background_color,
- partial(color_chooser.activate_for, "background_color"),
- )
- Button(
- self,
- grid_layout.get_rect((2, 1)).inflate((pad, pad)),
- "Fill Page",
- self.draw_image.clear,
- )
- self.foreground_color = ColorButton(
- self,
- grid_layout.get_rect((1, 2)).inflate((pad, pad)),
- self.draw_image.foreground_color,
- partial(color_chooser.activate_for, "foreground_color"),
- )
- self.foreground_color_value = CenterLabel(
- self,
- grid_layout.get_rect((2, 2)),
- color_to_hex(self.draw_image.foreground_color),
- )
- self.width_slider = Slider(
- self,
- grid_layout.get_rect((1, 3)).inflate((pad, pad)),
- Slider.HORIZONTAL,
- self.draw_image.line_width - 1,
- 96,
- self.set_width,
- )
- self.width_label = CenterLabel(
- self,
- grid_layout.get_rect((2, 3)),
- "1",
- )
- self.stroke_method = DropDown(
- self,
- grid_layout.get_rect((1, 4)).inflate((pad, pad)),
- *self.get_stroke_methods(),
- self.set_stroke_method,
- )
- self.input_method = DropDown(
- self,
- grid_layout.get_rect((1, 5), (2, 1)).inflate((pad, pad)),
- *self.get_input_methods(),
- self.set_input_method,
- )
- rect = pygame.Rect(
- (512, size[1] - 384),
- (size[0] - 1024, 256),
- )
- Rect(self, rect, "black", "darkgray")
-
- self.page_label = CenterLabel(
- self,
- pygame.Rect(
- (rect.left, rect.top + rect.height // 4 - 48), (rect.width, 96)
- ),
- "0 / 0",
- )
- button_bar_layout = BarLayout(
- pygame.Rect(
- (rect.left, rect.top + rect.height * 3 // 4 - 48), (rect.width, 96)
- ),
- abs(pad),
- )
- Button(
- self,
- button_bar_layout.get_rect(),
- "|<",
- lambda: None,
- )
- RepeatButton(
- self,
- button_bar_layout.get_rect(),
- "<",
- lambda: None,
- )
- RepeatButton(
- self,
- button_bar_layout.get_rect(),
- ">",
- lambda: None,
- )
- Button(
- self,
- button_bar_layout.get_rect(),
- ">|",
- lambda: None,
- )
- Button(
- self,
- button_bar_layout.get_rect(),
- "New",
- lambda: None,
- )
-
- KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
-
- def set_book_path(self, value):
- print("book path", value)
-
- def set_color(self, attr, color):
- setattr(self.draw_image, attr, color)
- getattr(self, attr).color = color
- self.dirty = True
-
- def set_width(self, slider_width):
- if slider_width < 0:
- slider_width = 0
- if self.width_slider.value != slider_width:
- self.width_slider.value = slider_width
- self.dirty = True
- width = (slider_width * 256 // self.width_slider.extent) + 1
- if self.draw_image.line_width != width:
- self.draw_image.line_width = width
- self.width_label.value = str(width)
- self.dirty = True
-
- def set_stroke_method(self, i):
- self.draw_image.stroke_method = StrokeMethod(i + 1)
- self.stroke_method.value = self.draw_image.stroke_method.name.title()
-
- def set_input_method(self, i):
- self.draw_image.input_method = InputMethod(i + 1)
- self.input_method.value = InputMethod.labels()[i]
+++ /dev/null
-from functools import partial
-
-import pygame
-
-from ui import Button, ColorButton, Label, Modal, Rect, TabBar
-
-from .color_circle import ColorCircle
-from .color_plane import ColorPlane
-from .utils import hsv_to_color
-
-
-class ColorChooser(Modal):
- def get_color_grid(self, lefttop, cell_size):
- """
- 8 * 8 * 8 hsv colors => 351 unique colors of that cube.
- Arrange them in a visually pleasing way.
- """
- x = y = 0
- pos = list(lefttop)
- colors_seen = set()
- grays = []
- grays_pos = None
- for v_range in (range(7, 3, -1), range(3, -1, -1)):
- for s in range(7, -1, -1):
- for v in v_range:
- for h in range(7):
- color = hsv_to_color((h * 255 / 7, s * 255 / 7, v * 255 / 7))
- int_color = (color.r << 16) | (color.g << 8) | color.b
- if int_color not in colors_seen:
- colors_seen.add(int_color)
- if v == 0:
- grays_pos = pos.copy()
- grays.insert(0, color)
- elif s == 0:
- grays.append(color)
- else:
- yield (pygame.Rect(pos, cell_size), color)
- x += 1
- pos[0] += cell_size[0]
- if x % 7 == 0:
- pos[0] += cell_size[0] // 2
- if x < 28:
- continue
- x, pos[0] = 0, lefttop[0]
- y += 1
- if y % 8 == 0:
- pos[1] += cell_size[1] // 2
- else:
- pos[1] += cell_size[1]
- for i, gray in enumerate((*grays[1:], grays[0])):
- yield (
- pygame.Rect(
- (
- grays_pos[0] + (i % 4) * cell_size[0],
- grays_pos[1] + (i // 4) * cell_size[1],
- ),
- cell_size,
- ),
- gray,
- )
-
- def __init__(self, parent):
- super().__init__(parent)
- size = self.surf.get_size()
- rect = pygame.Rect(
- (448, 192),
- (size[0] - 896, size[1] - 384),
- )
- Rect(self, rect, "black", "gray34")
- self.attr = None
- self.color = pygame.Color("red")
- buttons = []
- for r, color in self.get_color_grid((rect.left + 48, rect.top + 192), (64, 64)):
- buttons.append(ColorButton(self, r, color, partial(self.set_color, color)))
- self.color_circle = ColorCircle(
- self,
- pygame.Rect((rect.left + 1024, rect.top + 256), (512, 512)),
- self.chooser_set_hsv,
- self.color,
- )
- self.chooser_button = ColorButton(
- self,
- pygame.Rect((rect.right - 1024, rect.bottom - 192), (768, 128)),
- self.color,
- self.deactivate,
- )
- #self.hsv_rgb_buttons = (
- # Button(
- # self,
- # pygame.Rect((rect.left + 1024, rect.top + 160), (96, 96)),
- # "H",
- # partial(self.set_plane_axis, 0, AxisSetting.HUE),
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1120, rect.top + 160), (96, 96)),
- # "S",
- # partial(self.set_plane_axis, 0, AxisSetting.SATURATION),
- # True,
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1216, rect.top + 160), (96, 96)),
- # "V",
- # partial(self.set_plane_axis, 0, AxisSetting.VALUE)
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1312, rect.top + 160), (96, 96)),
- # "R",
- # partial(self.set_plane_axis, 0, AxisSetting.RED)
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1408, rect.top + 160), (96, 96)),
- # "G",
- # partial(self.set_plane_axis, 0, AxisSetting.GREEN)
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1504, rect.top + 160), (96, 96)),
- # "B",
- # partial(self.set_plane_axis, 0, AxisSetting.BLUE)
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1792, rect.top + 256), (96, 96)),
- # "H",
- # partial(self.set_plane_axis, 1, AxisSetting.HUE)
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1792, rect.top + 352), (96, 96)),
- # "S",
- # partial(self.set_plane_axis, 1, AxisSetting.SATURATION)
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1792, rect.top + 448), (96, 96)),
- # "V",
- # partial(self.set_plane_axis, 1, AxisSetting.VALUE),
- # True,
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1792, rect.top + 544), (96, 96)),
- # "R",
- # partial(self.set_plane_axis, 1, AxisSetting.RED)
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1792, rect.top + 640), (96, 96)),
- # "G",
- # partial(self.set_plane_axis, 1, AxisSetting.GREEN)
- # ),
- # Button(
- # self,
- # pygame.Rect((rect.left + 1792, rect.top + 736), (96, 96)),
- # "B",
- # partial(self.set_plane_axis, 1, AxisSetting.BLUE)
- # ),
- #)
- #self.hsv_rgb_slider = Slider(
- # self,
- # pygame.Rect((rect.left + 928, rect.top + 256), (96, 768)),
- # Slider.VERTICAL,
- # 0,
- # 96,
- # self.set_hsv_rgb_slider,
- #)
- #self.hsv_rgb_slider.value = self.hsv_rgb_slider.extent
- #self.hsv_rgb_slider_label = Label(
- # self, pygame.Rect((rect.left + 928, rect.top + 160), (96, 96)), "H"
- #)
- #self.color_plane = ColorPlane(
- # self,
- # pygame.Rect((rect.left + 1024, rect.top + 256), (768, 768)),
- # self.color,
- #)
- TabBar(
- self,
- pygame.Rect(rect.topleft, (1024, 128)),
- ("Grid", "Chooser"),
- (
- buttons,
- (
- self.color_circle,
- #self.hsv_rgb_slider_label,
- #self.hsv_rgb_slider,
- #*self.hsv_rgb_buttons,
- #self.color_plane,
- self.chooser_button,
- ),
- ),
- 1,
- )
-
- KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
-
- def activate_for(self, attr):
- self.attr = attr
- super().activate()
-
- def set_color(self, color):
- self.color = color
- self.deactivate()
-
- def deactivate(self):
- super().deactivate()
- self.parent.set_color(self.attr, self.color)
-
- def chooser_set_hsv(self, hsv):
- self.chooser_button.color = self.color = hsv_to_color(hsv)
- self.dirty = True
-
- def set_plane_axis(self, axis, axis_setting):
- if axis == 0:
- self.color_plane.set_horizontal(axis_setting)
- else:
- self.color_plane.set_vertical(axis_setting)
- #for button in self.hsv_rgb_buttons:
- # if button.callback.args[0] == 0:
- # button.highlight = button.callback.args[1] == self.color_plane.horizontal_setting
- # else: # button.callback.args[0] == 1
- # button.highlight = button.callback.args[1] == self.color_plane.vertical_setting
-
- def set_hsv_rgb_slider(self, value):
- if value < 0:
- value = 0
- elif value > self.hsv_rgb_slider.extent:
- value = self.hsv_rgb_slider.extent
- self.hsv_rgb_slider.value = value
- self.color_plane.set_base_value(value * 255 / self.hsv_rgb_slider.extent)
+++ /dev/null
-from math import atan2, cos, sin, tau
-from operator import itemgetter
-
-import pygame
-
-from ui import Child
-
-from .utils import distance, hsv_to_color, color_to_hsv
-
-
-class ColorCircle(Child):
- @staticmethod
- def get_hue_circle(size):
- surf = pygame.Surface(size, pygame.SRCALPHA, 32)
- surf.fill(pygame.Color(0, 0, 0, 0))
- center = tuple(x // 2 for x in size)
- outer_radius = min(center)
- inner_radius = outer_radius * 3 // 4
- inner_radius_squared = inner_radius ** 2
- outer_radius_squared = outer_radius ** 2
- with pygame.PixelArray(surf) as pa:
- for x, col in enumerate(pa):
- for y in range(len(col)):
- pos = (x - center[0]), (y - center[1])
- distance_squared = pos[0] ** 2 + pos[1] ** 2
- if inner_radius_squared <= distance_squared <= outer_radius_squared:
- angle = atan2(pos[1], pos[0])
- if angle < 0:
- angle += tau
- pa[x][y] = hsv_to_color((angle * 255 / tau, 255, 255))
- return inner_radius, surf
-
- get_alphas = (itemgetter(1), itemgetter(0))
-
- @classmethod
- def get_overlay_surfs(cls, size):
- overlay_surfs = (
- pygame.Surface(size, pygame.SRCALPHA, 32),
- pygame.Surface(size, pygame.SRCALPHA, 32),
- )
- for surf, get_alpha in zip(overlay_surfs, cls.get_alphas):
- with pygame.PixelArray(surf) as pa:
- for x, col in enumerate(pa):
- for y in range(len(col)):
- pa[x][y] = pygame.Color(
- y // 2, y // 2, y // 2, 255 - get_alpha((x, y)),
- )
- return overlay_surfs
-
- def __init__(self, parent, rect, callback, color):
- super().__init__(parent)
- self.rect = rect
- min_size = min(rect.size) // 2
- self.inner_radius, self.hue_circle = self.get_hue_circle(rect.size)
- self.overlay_surfs = self.get_overlay_surfs((min_size, min_size))
- self.callback = callback
- self.hsv = color_to_hsv(color)
- self.sv_surf = None
- self.pushed = None
-
- def set_hue(self, rel_pos):
- angle = atan2(rel_pos[1], rel_pos[0])
- if angle < 0:
- angle += tau
- hue = angle * 256 / tau
- if hue >= 256:
- hue -= 256
- if hue == self.hsv[0]:
- return
- self.sv_surf = None
- self.hsv = (hue, *self.hsv[1:])
- self.callback(self.hsv)
- self.dirty = True
-
- def set_sv(self, rel_pos):
- hue_angle = self.hsv[0] * tau / 255
- sv_surf_angle = tau * 9 / 8 - hue_angle
- if sv_surf_angle >= tau:
- sv_surf_angle -= tau
- center = tuple(a // 2 for a in self.overlay_surfs[0].get_size())
- dist = distance(rel_pos, (0, 0))
- angle = atan2(rel_pos[1], rel_pos[0]) + sv_surf_angle
- self.hsv = (
- self.hsv[0],
- min(max(0, center[0] + cos(angle) * dist), 255),
- min(max(0, center[1] + sin(angle) * dist), 255),
- )
- self.callback(self.hsv)
- self.dirty = True
-
- def handle_mousebuttondown(self, ev):
- if ev.button != 1 or not self.rect.collidepoint(ev.pos):
- return
- rel_pos = (ev.pos[0] - self.rect.centerx, ev.pos[1] - self.rect.centery)
- self.pushed = distance(rel_pos, (0, 0)) >= self.inner_radius
- if self.pushed:
- self.set_hue(rel_pos)
- else:
- self.set_sv(rel_pos)
-
- def handle_mousemotion(self, ev):
- if self.pushed is None:
- return
- rel_pos = tuple(p - c for p, c in zip(ev.pos, self.rect.center))
- if self.pushed:
- self.set_hue(rel_pos)
- else:
- self.set_sv(rel_pos)
-
- def handle_mousebuttonup(self, ev):
- if self.pushed is None:
- return
- rel_pos = tuple(p - c for p, c in zip(ev.pos, self.rect.center))
- if self.pushed is True:
- self.set_hue(rel_pos)
- else:
- self.set_sv(rel_pos)
- self.pushed = None
-
- def draw_cursor(self, pos):
- pygame.draw.circle(self.surf, "black", pos, 12)
- pygame.draw.circle(self.surf, "darkgray", pos, 12, 2)
-
- def draw_sv_surf(self, hue_angle):
- sv_surf_size = self.overlay_surfs[0].get_size()
- if self.sv_surf is None:
- self.sv_surf = pygame.Surface(sv_surf_size, pygame.SRCALPHA, 32)
- self.sv_surf.fill(hsv_to_color((self.hsv[0], 255, 255)))
- for overlay_surf in self.overlay_surfs:
- self.sv_surf.blit(overlay_surf, (0, 0))
- sv_surf_angle = tau * 9 / 8 - hue_angle
- if sv_surf_angle >= tau:
- sv_surf_angle -= tau
- sv_surf = pygame.transform.rotate(self.sv_surf, sv_surf_angle * 360 / tau)
- self.surf.blit(
- sv_surf,
- tuple(
- rc - sc
- for rc, sc in zip(
- self.rect.center, tuple(a // 2 for a in sv_surf.get_size())
- )
- )
- )
-
- center = tuple(a // 2 for a in sv_surf_size)
- cursor_distance = distance(self.hsv[1:], center)
- cursor_angle = atan2(
- self.hsv[2] - center[0], self.hsv[1] - center[1]
- ) - sv_surf_angle
- while cursor_angle < 0:
- cursor_angle += tau
- self.draw_cursor(
- tuple(
- c + f(cursor_angle) * cursor_distance
- for c, f in zip(self.rect.center, (cos, sin))
- )
- )
-
- def draw(self):
- self.surf.blit(self.hue_circle, self.rect.topleft)
- hue_angle = self.hsv[0] * tau / 255
- radius = min(x // 2 for x in self.rect.size) * 7 // 8
- self.draw_cursor(
- (
- self.rect.centerx + cos(hue_angle) * radius,
- self.rect.centery + sin(hue_angle) * radius,
- )
- )
- self.draw_sv_surf(hue_angle)
+++ /dev/null
-from enum import Enum, auto
-
-import pygame
-
-from ui import Child
-from .utils import color_to_hsv, hsv_to_color
-
-
-class AxisSetting(Enum):
- HUE = auto()
- SATURATION = auto()
- VALUE = auto()
- RED = auto()
- GREEN = auto()
- BLUE = auto()
-
-
-class ColorPlane(Child):
- """
- 6 modes at the top R/G/B/H/S/V
- 6 modes on the side R/G/B/H/S/V
-
- Both modes can probably be the same, but they must at all times be
- from the same group: R/G/B or H/S/V
-
- So when you turn on a new group, we can set the other dimension
- to the same - or not the same - value.
- """
- def get_surface(self):
- size = self.rect.size
- surf = pygame.Surface(size)
- with pygame.PixelArray(surf) as pa:
- for x, col in enumerate(pa):
- for y in range(size[1]):
- col[y] = hsv_to_color(
- (int(y * 256 / size[1]), int(x * 256 / size[0]), 255)
- )
- return surf
-
- def __init__(self, parent, rect, color):
- super().__init__(parent)
- self.rect = rect
- self.horizontal_setting = AxisSetting.SATURATION
- self.vertical_setting = AxisSetting.VALUE
- hsv = color_to_hsv(color)
- self.values = hsv[1:]
- self.base = hsv[0]
- self.surface = self.get_surface()
-
- def draw(self):
- self.surf.blit(self.surface, self.rect.topleft)
-
- HSV = (AxisSetting.HUE, AxisSetting.SATURATION, AxisSetting.VALUE)
- RGB = (AxisSetting.RED, AxisSetting.GREEN, AxisSetting.BLUE)
-
- def set_horizontal(self, axis_setting):
- self.horizontal_setting = axis_setting
- group = self.HSV if self.horizontal_setting in self.HSV else self.RGB
- if self.vertical_setting not in group:
- self.vertical_setting = group[group[0] == self.horizontal_setting]
-
- def set_vertical(self, axis_setting):
- self.vertical_setting = axis_setting
- group = self.HSV if self.vertical_setting in self.HSV else self.RGB
- if self.horizontal_setting not in group:
- self.horizontal_setting = group[group[0] == self.vertical_setting]
-
- def set_base_value(self, value):
- if value == self.base:
- return
- self.base = value
- self.surface = self.get_surface()
from enum import Enum, auto
-from time import time
import pygame
PYGAME = auto()
-class InputMethod(Enum):
- SDL_ANY = auto()
- SDL_MOUSE = auto()
- SDL_FINGERS = auto()
- SDL_KEYBOARD = auto()
-
- @staticmethod
- def labels():
- return (
- "Mouse or Fingers (SDL)",
- "Mouse/Stylus (SDL)",
- "Fingers (SDL)",
- "Keyboard (SDL)",
- )
-
-
class DrawImage(Child):
FINGER_TIMEOUT = 30
- def __init__(self, parent, background_color, foreground_color):
+ def __init__(self, parent, color):
super().__init__(parent)
- self.background_color = background_color
- self.foreground_color = foreground_color
- self.drawing_color = "foreground_color"
+ self.color = color
self.line_width = 1
self.surface = pygame.Surface(self.surf.get_size(), 0, 24)
self.clear()
+ self.image_dirty = False
self.stroke_method = next(iter(StrokeMethod))
- self.input_method = next(iter(InputMethod))
- self.mouse_pos = None
- self.fingers = {}
+ self.prev_pos = None
def clear(self):
- self.surface.fill(self.background_color)
-
- def handle_mousebuttondown(self, ev):
- if self.input_method in (InputMethod.SDL_ANY, InputMethod.SDL_MOUSE):
- if ev.button == 1:
- self.drawing_color = "foreground_color"
- self.mouse_pos = ev.pos
- elif ev.button == 3:
- self.drawing_color = "background_color"
- self.mouse_pos = ev.pos
-
- def mouse_stroke(self, buttons, pos):
- if (
- self.input_method in (InputMethod.SDL_ANY, InputMethod.SDL_MOUSE)
- and self.mouse_pos is not None
- and (buttons[0] or buttons[2])
- ):
- self.STROKE_MAPPING[self.stroke_method](self, self.mouse_pos, pos)
- self.mouse_pos = pos
- self.dirty = True
-
- def handle_mousemotion(self, ev):
- self.mouse_stroke(ev.buttons, ev.pos)
-
- def handle_mousebuttonup(self, ev):
- self.mouse_stroke(tuple(ev.button == i for i in range(1, 4)), ev.pos)
-
- def get_finger_pos(self, key):
- item = self.fingers.get(key)
- if item is None:
- return None
- prev_pos, t = item
- if t < time():
- self.fingers.pop(key)
- return None
- return prev_pos
-
- def handle_fingerdown(self, ev):
- if self.input_method not in (InputMethod.SDL_ANY, InputMethod.SDL_FINGERS):
- self.fingers.clear()
- return
- key = (ev.touch_id, ev.finger_id)
- size = self.surface.get_size()
- pos = (int(size[0] * ev.x), int(size[1] * ev.y))
- prev_pos = self.get_finger_pos(key)
- if prev_pos is not None:
- self.STROKE_MAPPING[self.stroke_method](self, prev_pos, pos)
- self.dirty = True
- if ev.type == pygame.FINGERUP:
- self.fingers.pop(key, None)
- else:
- self.fingers[key] = pos, time() + self.FINGER_TIMEOUT
-
- handle_fingermotion = handle_fingerdown
- handle_fingerup = handle_fingerdown
+ self.surface.fill("black")
+ self.image_dirty = False
+ self.dirty = True
- def update(self):
- if not self.root.focused:
- if self.mouse_pos is not None:
- self.mouse_pos = None
- self.dirty = True
- if len(self.fingers) > 0:
- self.fingers.clear()
- self.dirty = True
- return
- buttons = pygame.mouse.get_pressed()
- if buttons[0] or buttons[2]:
- return
- pos = pygame.mouse.get_pos()
- if pos != self.mouse_pos:
- self.mouse_pos = pos
- self.dirty = True
+ def dot(self, pos):
+ pygame.draw.circle(self.surf, self.color, pos, self.line_width // 2)
def draw(self):
self.surf.blit(self.surface, (0, 0))
- if self.mouse_pos is not None:
- pygame.draw.circle(
- self.surf,
- getattr(self, self.drawing_color),
- self.mouse_pos,
- self.line_width // 2,
- )
- pygame.draw.circle(
- self.surf,
- "black",
- self.mouse_pos,
- self.line_width // 2,
- 2,
- )
+ self.dot(pygame.mouse.get_pos())
- def pygame_stroke(self, a, b):
- pygame.draw.line(
- self.surface, getattr(self, self.drawing_color), a, b, self.line_width
- )
+ def handle_mousebuttondown(self, ev):
+ if ev.button == 1:
+ self.prev_pos = ev.pos
+ self.dirty = True
- def round_stroke(self, a, b):
- StrokeRoundLine(
- a, b, self.line_width
- ).draw(self.surface, getattr(self, self.drawing_color))
+ def handle_mousemotion(self, ev):
+ if self.prev_pos is not None:
+ if self.stroke_method == StrokeMethod.ROUND:
+ if ev.pos == self.prev_pos:
+ self.dot(ev.pos)
+ else:
+ StrokeRoundLine(
+ self.prev_pos, ev.pos, self.line_width
+ ).draw(self.surface, self.color)
+ elif self.stroke_method == StrokeMethod.SQUARE:
+ if ev.pos == self.prev_pos:
+ pygame.draw.rect(
+ self.surface,
+ self.color,
+ pygame.Rect(
+ (
+ ev.pos[0] - self.line_width // 2,
+ ev.pos[1] - self.line_width // 2,
+ ),
+ (self.line_width, self.line_width),
+ )
+ )
+ else:
+ StrokeSquareLine(
+ self.prev_pos, ev.pos, self.line_width
+ ).draw(self.surface, self.color)
+ elif self.stroke_method == StrokeMethod.PYGAME:
+ if ev.pos == self.prev_pos:
+ self.dot(ev.pos)
+ else:
+ pygame.draw.line(
+ self.surface, self.color, self.prev_pos, ev.pos, self.line_width
+ )
+ self.prev_pos = ev.pos
+ self.image_dirty = True
+ self.dirty = True
- def square_stroke(self, a, b):
- if a == b:
- return
- StrokeSquareLine(
- a, b, self.line_width
- ).draw(self.surface, getattr(self, self.drawing_color))
+ def handle_mousebuttonup(self, ev):
+ if ev.button == 1 and self.prev_pos is not None:
+ self.handle_mousemotion(ev)
+ self.prev_pos = None
- STROKE_MAPPING = {
- StrokeMethod.PYGAME: pygame_stroke,
- StrokeMethod.ROUND: round_stroke,
- StrokeMethod.SQUARE: square_stroke
- }
+ def set_image(self, surf):
+ self.surface.blit(surf, (0, 0))
+ self.dirty = True
--- /dev/null
+import pygame
+
+from ui import (
+ Button,
+ Modal,
+)
+
+
+class Menu(Modal):
+ def __init__(self, parent):
+ super().__init__(parent)
+ size = self.root.surf.get_size()
+ width_third = size[0] // 3
+ Button(
+ self,
+ pygame.Rect((width_third, size[1] * 2 // 3), (width_third, 128)),
+ "Quit",
+ self.root.handle_quit,
+ )
+
+ KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
+++ /dev/null
-from colorsys import hsv_to_rgb, rgb_to_hsv
-from math import sqrt
-
-import pygame
-
-
-def distance(a, b):
- return sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)
-
-
-def hsv_to_color(hsv):
- return pygame.Color(
- *(int(c * 255) for c in hsv_to_rgb(hsv[0] / 256, hsv[1] / 255, hsv[2] / 255))
- )
-
-
-def color_to_hsv(color):
- hsv = rgb_to_hsv(color.r / 255, color.g / 255, color.b / 255)
- hue = hsv[0] * 256
- if hue >= 256:
- hue -= 256
- return (hue, hsv[1] * 255, hsv[2] * 255)
-
-
-def color_to_hex(color):
- assert isinstance(color, pygame.Color)
- return str(, 16)
+++ /dev/null
-from ui import Slider
-
-
-class ValueSlider(Slider):
- pass
import pygame
-from ui import Button, CenterLabel, Child, Modal, Root, TextInput
+from ui import Button, Child, Label, Modal, Root, TextInput
class QuittableModal(Modal):
# add exit button
super().__init__(parent)
self.lw = parent.surf.get_width() / 3
- CenterLabel(
+ Label(
self,
pygame.Rect((int(self.lw / 2), 144), (self.lw, 128)),
"Enter player names",
+ Label.HAlign.CENTER,
)
self.inputs = [
TextInput(
self.rect = rect
th = rect.height // 2
self.labels = (
- CenterLabel(self, pygame.Rect(rect.topleft, (rect.width, th)), ""),
- CenterLabel(
+ Label(
+ self,
+ pygame.Rect(rect.topleft, (rect.width, th)),
+ "",
+ Label.HAlign.CENTER,
+ ),
+ Label(
self,
pygame.Rect((rect.left, rect.top + th), (rect.width, th)),
"NEW GAME",
+ Label.HAlign.CENTER,
),
)
from .fps_widget import FPSWidget
from .icon import Icon
from .icon_button import IconButton
-from .label import CenterLabel, Label, LeftLabel, RightLabel
+from .label import Label
from .message_box import MessageBox
from .modal import Modal
from .parent import Parent
__all__ = [
"Button",
- "CenterLabel",
"Child",
"ColorButton",
"DropDown",
"Icon",
"IconButton",
"Label",
- "LeftLabel",
"MessageBox",
"Modal",
"Parent",
"Rect",
"RepeatButton",
- "RightLabel",
"Root",
"Scroll",
"Slider",
rect.bottomleft, (rect.width, rect.height * len(entries))
)
- KEY_METHODS = {frozenset(set()): {pygame.K_ESCAPE: Modal.deactivate}}
+ KEY_METHODS = {frozenset(): {pygame.K_ESCAPE: Modal.deactivate}}
def choose(self, i=None):
self.deactivate()
+from enum import Enum
+
from .child import Child
-class CenterLabel(Child):
- def __init__(self, parent, rect, value):
+class Label(Child):
+ class HAlign(Enum):
+ LEFT = "left"
+ CENTER = "center"
+ RIGHT = "right"
+
+ def __init__(self, parent, rect, value, align=HAlign.LEFT):
super().__init__(parent)
self.rect = rect
self.value = value
+ self.get_blit_pos = {
+ self.HAlign.LEFT: self.get_blit_pos_left,
+ self.HAlign.CENTER: self.get_blit_pos_center,
+ self.HAlign.RIGHT: self.get_blit_pos_right,
+ }[align]
- def get_blit_pos(self):
- fs_size = self.font.size(self.value)
- return (
- self.rect.centerx - fs_size[0] // 2, self.rect.centery - fs_size[1] // 2
- )
-
- def draw(self):
- fs = self.font.render(self.value, True, "gray")
- self.surf.blit(fs, self.get_blit_pos())
-
-
-class LeftLabel(CenterLabel):
- def get_blit_pos(self):
+ def get_blit_pos_left(self):
return (
self.rect.left + 16,
self.rect.centery - self.font.size(self.value)[1] // 2,
)
+ def get_blit_pos_center(self):
+ fs_size = self.font.size(self.value)
+ return (
+ self.rect.centerx - fs_size[0] // 2, self.rect.centery - fs_size[1] // 2
+ )
-class RightLabel(CenterLabel):
- def get_blit_pos(self):
+ def get_blit_pos_right(self):
fs_size = self.font.size(self.value)
return (self.rect.right - 16 - fs_size[0], self.rect.centery - fs_size[1] // 2)
-
-Label = LeftLabel
+ def draw(self):
+ fs = self.font.render(self.value, True, "gray")
+ self.surf.blit(fs, self.get_blit_pos())
super().deactivate()
KEY_METHODS = {
- frozenset(set()): {
+ frozenset(): {
pygame.K_LEFT: key_left,
pygame.K_RIGHT: key_right,
pygame.K_HOME: key_home,
#!/usr/bin/env python3
-import sys
from pathlib import Path
from secrets import choice, randbelow
from time import time
import pygame
-sys.path.append(str(Path(__file__).parents[1]))
-
from ui import Button, Child, FloatSpinner, Label, Root, Spinner
pygame.font.Font(None, size=96),
)
self.teams = tuple(
- Team(file) for file in (Path(__file__).parent / "logos").iterdir()
+ Team(file)
+ for file in (Path(__file__).parent / "logos").iterdir()
+ if file.stem != "National League"
)
len_teams = len(self.teams)
# fits one column of memory destinations (todo)