events.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import asyncio
  2. from inspect import signature
  3. from justpy.htmlcomponents import HTMLBaseComponent
  4. from pydantic import BaseModel
  5. import traceback
  6. from typing import Any, Callable, List, Optional
  7. from starlette.websockets import WebSocket
  8. from .elements.element import Element
  9. from .task_logger import create_task
  10. class EventArguments(BaseModel):
  11. class Config:
  12. arbitrary_types_allowed = True
  13. sender: Element
  14. socket: Optional[WebSocket]
  15. class ClickEventArguments(EventArguments):
  16. pass
  17. class MouseEventArguments(EventArguments):
  18. type: str
  19. image_x: float
  20. image_y: float
  21. class UploadEventArguments(EventArguments):
  22. files: List[bytes]
  23. class ValueChangeEventArguments(EventArguments):
  24. value: Any
  25. class KeyboardAction(BaseModel):
  26. keypress: bool
  27. keydown: bool
  28. keyup: bool
  29. repeat: bool
  30. class KeyboardModifiers(BaseModel):
  31. alt: bool
  32. ctrl: bool
  33. meta: bool
  34. shift: bool
  35. class KeyboardKey(BaseModel):
  36. name: str
  37. code: str
  38. location: int
  39. def __eq__(self, other: str) -> bool:
  40. return self.name == other or self.code == other
  41. def __repr__(self):
  42. return str(self.name)
  43. @property
  44. def is_cursorkey(self):
  45. return self.code.startswith('Arrow')
  46. @property
  47. def number(self) -> Optional[int]:
  48. """Integer value of a number key."""
  49. return int(self.code.removeprefix('Digit')) if self.code.startswith('Digit') else None
  50. @property
  51. def backspace(self) -> bool:
  52. return self.name == 'Backspace'
  53. @property
  54. def tab(self) -> bool:
  55. return self.name == 'Tab'
  56. @property
  57. def enter(self) -> bool:
  58. return self.name == 'enter'
  59. @property
  60. def shift(self) -> bool:
  61. return self.name == 'Shift'
  62. @property
  63. def control(self) -> bool:
  64. return self.name == 'Control'
  65. @property
  66. def alt(self) -> bool:
  67. return self.name == 'Alt'
  68. @property
  69. def pause(self) -> bool:
  70. return self.name == 'Pause'
  71. @property
  72. def caps_lock(self) -> bool:
  73. return self.name == 'CapsLock'
  74. @property
  75. def escape(self) -> bool:
  76. return self.name == 'Escape'
  77. @property
  78. def space(self) -> bool:
  79. return self.name == 'Space'
  80. @property
  81. def page_up(self) -> bool:
  82. return self.name == 'PageUp'
  83. @property
  84. def page_down(self) -> bool:
  85. return self.name == 'PageDown'
  86. @property
  87. def end(self) -> bool:
  88. return self.name == 'End'
  89. @property
  90. def home(self) -> bool:
  91. return self.name == 'Home'
  92. @property
  93. def arrow_left(self) -> bool:
  94. return self.name == 'ArrowLeft'
  95. @property
  96. def arrow_up(self) -> bool:
  97. return self.name == 'ArrowUp'
  98. @property
  99. def arrow_right(self) -> bool:
  100. return self.name == 'ArrowRight'
  101. @property
  102. def arrow_down(self) -> bool:
  103. return self.name == 'ArrowDown'
  104. @property
  105. def print_screen(self) -> bool:
  106. return self.name == 'PrintScreen'
  107. @property
  108. def insert(self) -> bool:
  109. return self.name == 'Insert'
  110. @property
  111. def delete(self) -> bool:
  112. return self.name == 'Delete'
  113. @property
  114. def meta(self) -> bool:
  115. return self.name == 'Meta'
  116. @property
  117. def f1(self) -> bool:
  118. return self.name == 'F1'
  119. @property
  120. def f2(self) -> bool:
  121. return self.name == 'F2'
  122. @property
  123. def f3(self) -> bool:
  124. return self.name == 'F3'
  125. @property
  126. def f4(self) -> bool:
  127. return self.name == 'F4'
  128. @property
  129. def f5(self) -> bool:
  130. return self.name == 'F5'
  131. @property
  132. def f6(self) -> bool:
  133. return self.name == 'F6'
  134. @property
  135. def f7(self) -> bool:
  136. return self.name == 'F7'
  137. @property
  138. def f8(self) -> bool:
  139. return self.name == 'F8'
  140. @property
  141. def f9(self) -> bool:
  142. return self.name == 'F9'
  143. @property
  144. def f10(self) -> bool:
  145. return self.name == 'F10'
  146. @property
  147. def f11(self) -> bool:
  148. return self.name == 'F11'
  149. @property
  150. def f12(self) -> bool:
  151. return self.name == 'F12'
  152. class KeyEventArguments(EventArguments):
  153. class Config:
  154. arbitrary_types_allowed = True
  155. action: KeyboardAction
  156. key: KeyboardKey
  157. modifiers: KeyboardModifiers
  158. def handle_event(handler: Optional[Callable], arguments: EventArguments, *, update: Optional[HTMLBaseComponent] = None):
  159. try:
  160. if handler is None:
  161. return
  162. no_arguments = not signature(handler).parameters
  163. result = handler() if no_arguments else handler(arguments)
  164. if asyncio.iscoroutinefunction(handler):
  165. async def async_handler():
  166. try:
  167. await result
  168. if update is not None:
  169. await update.update()
  170. except Exception:
  171. traceback.print_exc()
  172. create_task(async_handler(), name=str(handler))
  173. return False
  174. else:
  175. return result
  176. except Exception:
  177. traceback.print_exc()