Skip to content

Instantly share code, notes, and snippets.

@SealtielFreak
Last active January 4, 2023 18:51
Show Gist options
  • Save SealtielFreak/fb7fa035f197bdd79bee61d40c42c964 to your computer and use it in GitHub Desktop.
Save SealtielFreak/fb7fa035f197bdd79bee61d40c42c964 to your computer and use it in GitHub Desktop.
import importlib
import importlib.util
from typing import Optional, Any, List, Type, TypeVar, Generic, Dict, Callable, Tuple
C = TypeVar('C')
def exists_module(name: str | Tuple[str, ...]) -> bool:
_module = None
if isinstance(name, str):
try:
_module = importlib.import_module(name)
except ModuleNotFoundError:
return False
else:
return all((exists_module(n) for n in name))
return True
def raise_none_value(f):
def inner(*args, **kwargs):
value = f(*args, **kwargs)
if value is None:
raise ValueError('Value is empty')
return value
return inner
class Singleton(type, Generic[C]):
__instances: Dict[C, Type[C]] = {}
def __call__(cls, *args, **kwargs):
if cls not in cls.__instances:
cls.__instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls.__instances[cls]
class DOMCanvasModule(metaclass=Singleton):
__name_main_id_format = 'snakey-main-'
__name_canvas_id_format = 'snakey-canvas-'
__brython_module = 'browser'
__pyscript_module = 'js', 'pyodide'
def __init__(self):
self.create_object = lambda obj: obj
self._pyodide: Optional[Any] = None
self._dom: Optional[Any] = None
self._document: Optional[Any] = None
self._window: Optional[Any] = None
if exists_module(self.__brython_module):
self._dom = importlib.import_module(self.__brython_module)
elif exists_module(self.__pyscript_module):
self._dom = importlib.import_module(self.__pyscript_module[0])
self._pyodide = importlib.import_module(self.__pyscript_module[0])
self.create_object = lambda obj: self._pyodide.create_proxy(obj)
else:
raise RuntimeError('This module only work for Brython and Pyscript')
self._document = self.dom.document
self._window = self.dom.window
self._current_id: int | str = 0
self._id_windows: List[Any] = []
self._main_id = lambda n: self.__name_main_id_format + str(n)
self._canvas_id = lambda n: self.__name_canvas_id_format + str(n)
self.current_id = 0
@property
@raise_none_value
def dom(self) -> Any:
return self._dom
@property
@raise_none_value
def window(self) -> Any:
return self._window
@property
@raise_none_value
def document(self) -> Any:
return self._document
@property
def current_id(self) -> int | str:
return self._current_id
@current_id.setter
def current_id(self, _id: int | str):
self._current_id = _id
self._create_main(_id)
@property
@raise_none_value
def container(self) -> Any:
return self._get_container(self.current_id)
@property
@raise_none_value
def canvas(self) -> Any:
return self._get_canvas(self.current_id)
@property
@raise_none_value
def context(self) -> Any:
return self.canvas.getContext('2d') if self.canvas is not None else None
@staticmethod
def _setattr(elm: Any, **options) -> Any:
if hasattr(elm, 'attrs'):
for attr, value in options.items():
elm.attrs[attr.lower()] = value
else:
for attr, value in options.items():
if hasattr(elm, attr):
setattr(elm, attr, value)
return elm
def _get_container(self, _id) -> Optional[Any]:
return self.document.getElementById(self._main_id(_id))
def _get_canvas(self, _id) -> Optional[Any]:
return self.document.getElementById(self._canvas_id(_id))
def create_element(self, _tag: str, **options):
elm = self.document.createElement('canvas', **options)
self._setattr(elm, **options)
return elm
def _create_main(self, _id: int | str, **kwargs):
container: Optional[Any] = self._get_container(_id)
canvas: Optional[Any] = self._get_canvas(_id)
if container is None:
raise IndexError(f"Container {self._main_id(_id)} no found")
if canvas is None:
canvas = self.document.createElement('canvas', **kwargs)
self._setattr(canvas, id=self._canvas_id(_id))
container.append(canvas)
return container, canvas, canvas.getContext('2d')
def bind(self, elm: Any, name: str):
def inner(f: Callable[[Any], None]) -> Callable[[Any], None]:
elm.addEventListener(name, self.create_object(f))
return f
return inner
def set_interval(self, callback: Callable[[], None], delay: int = 0) -> int:
return self.window.setInterval(self.create_object(callback), delay)
def clear_interval(self, _id: int) -> None:
self.window.clearInterval(id)
def set_timeout(self, callback: Callable[[], None], delay: int) -> int:
return self.window.setTimeout(callback, delay)
dom = DOMCanvasModule()
if __name__ == "__main__":
print(dom.current_id)
print(dom.container, dom.canvas, dom.context)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment