diff --git a/Launcher.py b/Launcher.py index d7ed44a8878b..516f0c32bbf9 100644 --- a/Launcher.py +++ b/Launcher.py @@ -250,10 +250,10 @@ def create_shortcut(button, component: Component): def run_gui(): from kvui import (MDApp, MDFloatLayout, MDGridLayout, MDButton, MDLabel, MDButtonText, MDButtonIcon, ScrollBox, - ContainerLayout, Widget, MDBoxLayout) + ContainerLayout, Widget, MDBoxLayout, ApAsyncImage) from kivy.core.window import Window from kivy.metrics import dp - from kivy.uix.image import AsyncImage + # from kivy.uix.image import AsyncImage from kivymd.uix.button import MDIconButton from kivymd.uix.card import MDCard from kivymd.uix.menu import MDDropdownMenu @@ -301,8 +301,8 @@ def build_card(component: Component) -> Widget: button_card.add_widget(button_layout) source = icon_paths[component.icon] - image = AsyncImage(source=source, size=(40, 40), size_hint_y=None, - pos_hint={"center_x": 0.1, "center_y": 0.5}) + image = ApAsyncImage(source=source, size=(40, 40), size_hint_y=None, + pos_hint={"center_x": 0.1, "center_y": 0.5}) button_layout.add_widget(image) button_layout.add_widget(MDLabel(text=component.display_name, diff --git a/kvui.py b/kvui.py index 17f0631e6ea4..9e08d2db9145 100644 --- a/kvui.py +++ b/kvui.py @@ -3,6 +3,8 @@ import sys import typing import re +import io +import pkgutil from collections import deque assert "kivy" not in sys.modules, "kvui should be imported before kivy for frozen compatibility" @@ -38,6 +40,7 @@ from kivy.core.window import Window from kivy.core.clipboard import Clipboard from kivy.core.text.markup import MarkupLabel +from kivy.core.image import ImageLoader, ImageLoaderBase, ImageData from kivy.base import ExceptionHandler, ExceptionManager from kivy.clock import Clock from kivy.factory import Factory @@ -81,10 +84,44 @@ remove_between_brackets = re.compile(r"\[.*?]") -class ImageIcon(MDButtonIcon, AsyncImage): +class ApAsyncImage(AsyncImage): + def is_uri(self, filename: str) -> bool: + if filename.startswith("ap:"): + return True + else: + return super().is_uri(filename) + + +class ImageLoaderPkgutil(ImageLoaderBase): + def load(self, filename: str) -> typing.List[ImageData]: + # take off the "ap:" prefix + module, path = filename[3:].split("/", 1) + data = pkgutil.get_data(module, path) + return self._bytes_to_data(data) + + def _bytes_to_data(self, data: typing.Union[bytes, bytearray]) -> typing.List[ImageData]: + loader = next(loader for loader in ImageLoader.loaders if loader.can_load_memory()) + return loader.load(loader, io.BytesIO(data)) + + +# grab the default loader method so we can override it but use it as a fallback +_original_image_loader_load = ImageLoader.load + + +def load_override(filename: str, default_load=_original_image_loader_load, **kwargs): + if filename.startswith("ap:"): + return ImageLoaderPkgutil(filename) + else: + return default_load(filename, **kwargs) + + +ImageLoader.load = load_override + + +class ImageIcon(MDButtonIcon, ApAsyncImage): def __init__(self, *args, **kwargs): super().__init__(args, kwargs) - self.image = AsyncImage(**kwargs) + self.image = ApAsyncImage(**kwargs) self.add_widget(self.image) def add_widget(self, widget, index=0, canvas=None): @@ -99,7 +136,7 @@ def __init__(self, **kwargs): if val != "None": image_args[kwarg.replace("image_", "")] = val super().__init__() - self.image = AsyncImage(**image_args) + self.image = ApAsyncImage(**image_args) def set_center(button, center): self.image.center_x = self.center_x self.image.center_y = self.center_y