Skip to content

Commit

Permalink
Merge pull request #7 from Conengmo/thumbnails
Browse files Browse the repository at this point in the history
Thumbnail buttons
  • Loading branch information
Conengmo authored Dec 9, 2023
2 parents 71c030b + 81cb8a2 commit e9e8f0d
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 20 deletions.
113 changes: 100 additions & 13 deletions battle_map_tv/gui_elements.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import os.path
import typing
from typing import Callable, Union, Optional

import cv2
import pyglet
from battle_map_tv.storage import set_in_storage, StorageKeys, get_from_storage, remove_from_storage
from pyglet.graphics import Batch
from pyglet.text import Label

from battle_map_tv.opencv_utils import opencv_to_pyglet_image, change_brightness

if typing.TYPE_CHECKING:
from battle_map_tv.window_image import ImageWindow

margin_y_label = 10

pyglet.resource.path = [os.path.join(pyglet.resource.get_script_home(), "resources")]
Expand Down Expand Up @@ -51,17 +59,17 @@ def __init__(
)
self.y_original = y
self.height = 30
self.label = Label(text=label, x=self.x, batch=batch)
self.label = Label(text=label, x=self.x, y=self.y2 + margin_y_label, batch=batch)

def hide(self):
self.y = -100
self.label.y = -100
self.label.visible = False

def show(self):
self.y = self.y_original
self._layout.x = self.x + 10
self._layout.y = self.y - 5
self.label.y = self.y2 + margin_y_label
self.label.visible = True


class PushButton(CoordinatesMixin, pyglet.gui.PushButton):
Expand Down Expand Up @@ -106,10 +114,12 @@ def __init__(self, x: int, y: int, batch: Batch, callback: Callable, effect: str
self.set_handler("on_toggle", callback)

def hide(self):
self.y = -100
self.enabled = False
self._sprite.visible = False

def show(self):
self.y = self.y_original
self._sprite.visible = True
self.enabled = True


class TabButton(CoordinatesMixin, pyglet.gui.PushButton):
Expand Down Expand Up @@ -140,6 +150,79 @@ def __init__(
self.set_handler("on_release", callback)


class ThumbnailButton(CoordinatesMixin, pyglet.gui.ToggleButton):
width: int = 100
height: int = 100

def __init__(
self,
index: int,
x: int,
y: int,
batch: Batch,
image_window: "ImageWindow",
all_thumbnail_buttons: list["ThumbnailButton"],
):
self.index = index
self.image_path: Optional[str] = None
self.image_window = image_window
self.all_thumbnail_buttons = all_thumbnail_buttons
button_img = pyglet.resource.image("button_file_drop.png")
super().__init__(x=x, y=y, pressed=button_img, depressed=button_img, batch=batch)
image_path = get_from_storage(self._storage_key, optional=True)
if image_path is not None:
if os.path.exists(image_path):
self.add_thumbnail_image(image_path)
else:
remove_from_storage(self._storage_key)
self.set_handler("on_toggle", self._custom_on_toggle)

def _custom_on_toggle(self, value):
if self.image_path is None:
return
if value:
for thumbnail_button in self.all_thumbnail_buttons:
if thumbnail_button is not self and thumbnail_button.value:
thumbnail_button.value = False
self.image_window.add_image(self.image_path)
else:
self.image_window.remove_image()

@property
def _storage_key(self) -> StorageKeys:
return StorageKeys[f"thumbnail_{self.index}"]

def hide(self):
self.enabled = False
self._sprite.visible = False

def show(self):
self._sprite.visible = True
self.enabled = True

def on_file_drop(self, x: int, y: int, image_path: str) -> bool:
if not self.enabled or not self._check_hit(x, y):
return False
self.add_thumbnail_image(image_path)
if self.value:
self.image_window.add_image(image_path)
set_in_storage(self._storage_key, image_path)
return True

def add_thumbnail_image(self, image_path: str):
self.image_path = image_path
assert self.enabled
assert self._sprite.visible
image_cv = cv2.imread(image_path)
image_cv = cv2.resize(image_cv, (self.width, self.height), interpolation=cv2.INTER_AREA)
self._depressed_img = opencv_to_pyglet_image(image_cv)
image_cv_pressed = change_brightness(image_cv, -50)
self._pressed_img = opencv_to_pyglet_image(image_cv_pressed)
image_cv_hover = change_brightness(image_cv, 50)
self._hover_img = opencv_to_pyglet_image(image_cv_hover)
self._sprite.image = self._depressed_img


class Slider(CoordinatesMixin, pyglet.gui.Slider):
base = pyglet.resource.image("slider_base.png").get_texture()
knob = pyglet.resource.image("slider_knob.png").get_texture()
Expand Down Expand Up @@ -174,16 +257,17 @@ def __init__(
self.label = Label(
text=label,
x=self.x,
y=self.y2 + margin_y_label,
batch=batch,
)
self.label_value = Label(
text=label_formatter(default),
x=super().x2 + 20,
y=self.y + self.height / 2,
anchor_y="center",
batch=batch,
)
self.label_formatter = label_formatter
self.show()

@property
def x2(self) -> int:
Expand Down Expand Up @@ -215,12 +299,15 @@ def reset(self):
self.value = self.default

def hide(self):
self.y = -100
self.label.y = -100
self.label_value.y = -100
self.enabled = False
self._base_spr.visible = False
self._knob_spr.visible = False
self.label.visible = False
self.label_value.visible = False

def show(self):
self.y = self.y_original
self.label.y = self.y2 + margin_y_label
self.label_value.y = self.y + self.height / 2
self.value = self.value
self.enabled = True
self._base_spr.visible = True
self._knob_spr.visible = True
self.label.visible = True
self.label_value.visible = True
29 changes: 29 additions & 0 deletions battle_map_tv/opencv_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from io import BytesIO

import cv2
import pyglet
from pyglet.image import AbstractImage


def change_brightness(img, value: int):
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)

if value > 0:
lim = 255 - value
v[v > lim] = 255
v[v <= lim] += value
elif value < 0:
lim = 0 - value
v[v < lim] = 0
v[v >= lim] = v[v >= lim] + value

final_hsv = cv2.merge((h, s, v))
img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
return img


def opencv_to_pyglet_image(opencv_image) -> AbstractImage:
as_bytes = cv2.imencode(".png", opencv_image)[1].tobytes()
image = pyglet.image.load(filename=".png", file=BytesIO(as_bytes))
return image
Binary file added battle_map_tv/resources/button_file_drop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions battle_map_tv/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class StorageKeys(Enum):
width_mm = "width_mm"
height_mm = "height_mm"
previous_image = "previous_image"
thumbnail_0 = "thumbnail_0"
thumbnail_1 = "thumbnail_1"
thumbnail_2 = "thumbnail_2"
thumbnail_3 = "thumbnail_3"


def get_from_storage(key: StorageKeys, optional: bool = False):
Expand Down
60 changes: 53 additions & 7 deletions battle_map_tv/window_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
PushButton,
TabButton,
EffectToggleButton,
ThumbnailButton,
)
from battle_map_tv.scale_detection import find_image_scale
from battle_map_tv.storage import get_from_storage, StorageKeys, set_in_storage
Expand All @@ -37,6 +38,8 @@ def __init__(self, image_window: ImageWindow, *args, **kwargs):
self.batch_background = Batch()
self.frame = Frame(window=self)

self.thumbnail_buttons: List[ThumbnailButton] = []

row_y = margin_y

def slider_scale_callback(value: Union[float, str]):
Expand Down Expand Up @@ -90,11 +93,17 @@ def button_callback_autoscale(button_value: bool) -> bool:

row_y += 100

def button_callback_remove():
for thumbnail_button in self.thumbnail_buttons:
if thumbnail_button.value:
thumbnail_button.value = False
image_window.remove_image()

self.button_remove_image = PushButton(
x=margin_x,
y=row_y,
batch=self.batch,
callback=lambda: image_window.remove_image(),
callback=button_callback_remove,
label="Remove",
icon="remove",
)
Expand Down Expand Up @@ -194,32 +203,42 @@ def button_callback_grid(button_value: bool) -> bool:

self.tab_buttons: List[TabButton] = []

self._add_tab_images(tab_index=0, row_y=row_y)

self.text_entry_screen_width: TextEntry
self.text_entry_screen_height: TextEntry
self._add_tab_screen_size(tab_index=0, row_y=row_y)
self._add_tab_screen_size(tab_index=1, row_y=row_y)

self.slider_grid_opacity: Slider
self._add_tab_grid_opacity(tab_index=1, row_y=row_y)
self._add_tab_grid_opacity(tab_index=2, row_y=row_y)

self.effect_buttons: List[EffectToggleButton] = []
self._add_tab_effects(tab_index=2, row_y=row_y)
self._add_tab_effects(tab_index=3, row_y=row_y)

# Start with showing the first tab
self._hide_tab_content()
self.text_entry_screen_width.show()
self.text_entry_screen_height.show()
for thumbnail in self.thumbnail_buttons:
thumbnail.show()

def on_draw(self):
self.clear()
self.batch_background.draw()
self.batch.draw()

def on_file_drop(self, x: int, y: int, paths: List[str]):
self.image_window.add_image(image_path=paths[0])
self.switch_to()
for thumbnail in self.thumbnail_buttons:
is_hit = thumbnail.on_file_drop(x=x, y=y, image_path=paths[0])
if is_hit:
break
else:
self.image_window.add_image(image_path=paths[0])
self.switch_to()
self.slider_scale.reset()

def _hide_tab_content(self):
for thumbnail in self.thumbnail_buttons:
thumbnail.hide()
self.text_entry_screen_width.hide()
self.text_entry_screen_height.hide()
self.slider_grid_opacity.hide()
Expand Down Expand Up @@ -331,3 +350,30 @@ def callback_tab_effects():
callback=callback_tab_effects,
label="Effects",
)

def _add_tab_images(self, tab_index: int, row_y: int):
thumbnail_y = row_y + (tab_height - ThumbnailButton.height) // 2
for i in range(4):
thumbnail_button = ThumbnailButton(
index=i,
x=(2 + i) * margin_x + i * ThumbnailButton.width,
y=thumbnail_y,
batch=self.batch,
image_window=self.image_window,
all_thumbnail_buttons=self.thumbnail_buttons,
)
self.thumbnail_buttons.append(thumbnail_button)
self.frame.add_widget(thumbnail_button)

def callback_tab_images():
self.switch_to()
self._hide_tab_content()
for thumbnail in self.thumbnail_buttons:
thumbnail.show()

self._create_tab_button(
tab_index=tab_index,
row_y=row_y,
callback=callback_tab_images,
label="Images",
)

0 comments on commit e9e8f0d

Please sign in to comment.