events.py 4.7 KB

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