interactive_image.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. from __future__ import annotations
  2. import traceback
  3. from typing import Any, Callable, Dict, List, Optional
  4. from justpy import WebPage
  5. from ..events import MouseEventArguments, handle_event
  6. from .custom_view import CustomView
  7. from .element import Element
  8. CustomView.use(__file__)
  9. class InteractiveImageView(CustomView):
  10. def __init__(self, source: str, on_mouse: Callable, events: List[str], cross: bool):
  11. super().__init__('interactive_image', source=source, events=events, cross=cross, svg_content='')
  12. self.allowed_events = ['onMouse', 'onConnect']
  13. self.initialize(onMouse=on_mouse, onConnect=self.on_connect)
  14. self.sockets = []
  15. def on_connect(self, msg):
  16. self.prune_sockets()
  17. self.sockets.append(msg.websocket)
  18. def prune_sockets(self):
  19. page_sockets = [s for page_id in self.pages for s in WebPage.sockets.get(page_id, {}).values()]
  20. self.sockets = [s for s in self.sockets if s in page_sockets]
  21. class InteractiveImage(Element):
  22. def __init__(self, source: str, *,
  23. on_mouse: Optional[Callable] = None, events: List[str] = ['click'], cross: bool = False):
  24. """Interactive Image
  25. Create an image with an SVG overlay that handles mouse events and yields image coordinates.
  26. :param source: the source of the image; can be an URL or a base64 string
  27. :param on_mouse: callback for mouse events (yields `type`, `image_x` and `image_y`)
  28. :param events: list of JavaScript events to subscribe to (default: `['click']`)
  29. :param cross: whether to show crosshairs (default: `False`)
  30. """
  31. self.mouse_handler = on_mouse
  32. super().__init__(InteractiveImageView(source, self.handle_mouse, events, cross))
  33. def handle_mouse(self, msg: Dict[str, Any]):
  34. if self.mouse_handler is None:
  35. return
  36. try:
  37. arguments = MouseEventArguments(
  38. sender=self,
  39. socket=msg.get('websocket'),
  40. type=msg.get('mouse_event_type'),
  41. image_x=msg.get('image_x'),
  42. image_y=msg.get('image_y'),
  43. )
  44. handle_event(self.mouse_handler, arguments)
  45. except:
  46. traceback.print_exc()
  47. async def set_source(self, source: str):
  48. self.view.options.source = source
  49. self.view.prune_sockets()
  50. for socket in self.view.sockets:
  51. await self.view.run_method(f'set_source("{source}")', socket)
  52. @property
  53. def svg_content(self) -> str:
  54. return self.view.options.svg_content
  55. @svg_content.setter
  56. def svg_content(self, content: str):
  57. self.view.options.svg_content = content