native.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. # pylint: disable=C0116
  2. import inspect
  3. import warnings
  4. from multiprocessing import Queue
  5. from typing import Any, Callable, Tuple
  6. from .. import run
  7. from ..logging import log
  8. method_queue: Queue = Queue()
  9. response_queue: Queue = Queue()
  10. try:
  11. with warnings.catch_warnings():
  12. # webview depends on bottle which uses the deprecated CGI function (https://github.com/bottlepy/bottle/issues/1403)
  13. warnings.filterwarnings('ignore', category=DeprecationWarning)
  14. import webview
  15. from webview.window import FixPoint
  16. class WindowProxy(webview.Window):
  17. def __init__(self) -> None: # pylint: disable=super-init-not-called
  18. pass # NOTE we don't call super().__init__ here because this is just a proxy to the actual window
  19. async def get_always_on_top(self) -> bool:
  20. """Get whether the window is always on top."""
  21. return await self._request()
  22. def set_always_on_top(self, on_top: bool) -> None:
  23. """Set whether the window is always on top."""
  24. self._send(on_top)
  25. async def get_size(self) -> Tuple[int, int]:
  26. """Get the window size as tuple (width, height)."""
  27. return await self._request()
  28. async def get_position(self) -> Tuple[int, int]:
  29. """Get the window position as tuple (x, y)."""
  30. return await self._request()
  31. def load_url(self, url: str) -> None:
  32. self._send(url)
  33. def load_html(self, content: str, base_uri: str = ...) -> None: # type: ignore
  34. self._send(content, base_uri)
  35. def load_css(self, stylesheet: str) -> None:
  36. self._send(stylesheet)
  37. def set_title(self, title: str) -> None:
  38. self._send(title)
  39. async def get_cookies(self) -> Any: # pylint: disable=invalid-overridden-method
  40. return await self._request()
  41. async def get_current_url(self) -> str: # type: ignore # pylint: disable=invalid-overridden-method
  42. return await self._request()
  43. def destroy(self) -> None:
  44. self._send()
  45. def show(self) -> None:
  46. self._send()
  47. def hide(self) -> None:
  48. self._send()
  49. def set_window_size(self, width: int, height: int) -> None:
  50. self._send(width, height)
  51. def resize(self, width: int, height: int, fix_point: FixPoint = FixPoint.NORTH | FixPoint.WEST) -> None:
  52. self._send(width, height, fix_point)
  53. def maximize(self) -> None:
  54. self._send()
  55. def minimize(self) -> None:
  56. self._send()
  57. def restore(self) -> None:
  58. self._send()
  59. def toggle_fullscreen(self) -> None:
  60. self._send()
  61. def move(self, x: int, y: int) -> None:
  62. self._send(x, y)
  63. async def evaluate_js( # type: ignore # pylint: disable=arguments-differ,invalid-overridden-method
  64. self,
  65. script: str,
  66. ) -> str:
  67. return await self._request(script)
  68. async def create_confirmation_dialog( # type: ignore # pylint: disable=invalid-overridden-method
  69. self,
  70. title: str,
  71. message: str,
  72. ) -> bool:
  73. return await self._request(title, message)
  74. async def create_file_dialog( # type: ignore # pylint: disable=invalid-overridden-method
  75. self,
  76. dialog_type: int = webview.OPEN_DIALOG,
  77. directory: str = '',
  78. allow_multiple: bool = False,
  79. save_filename: str = '',
  80. file_types: Tuple[str, ...] = (),
  81. ) -> Tuple[str, ...]:
  82. return await self._request(
  83. dialog_type=dialog_type,
  84. directory=directory,
  85. allow_multiple=allow_multiple,
  86. save_filename=save_filename,
  87. file_types=file_types,
  88. )
  89. def expose(self, function: Callable) -> None: # type: ignore # pylint: disable=arguments-differ
  90. raise NotImplementedError(f'exposing "{function}" is not supported')
  91. def _send(self, *args: Any, **kwargs: Any) -> None:
  92. name = inspect.currentframe().f_back.f_code.co_name # type: ignore
  93. method_queue.put((name, args, kwargs))
  94. async def _request(self, *args: Any, **kwargs: Any) -> Any:
  95. def wrapper(*args: Any, **kwargs: Any) -> Any:
  96. try:
  97. method_queue.put((name, args, kwargs))
  98. return response_queue.get() # wait for the method to be called and writing its result to the queue
  99. except Exception:
  100. log.exception(f'error in {name}')
  101. return None
  102. name = inspect.currentframe().f_back.f_code.co_name # type: ignore
  103. return await run.io_bound(wrapper, *args, **kwargs)
  104. def signal_server_shutdown(self) -> None:
  105. """Signal the server shutdown."""
  106. self._send()
  107. except ModuleNotFoundError:
  108. class WindowProxy: # type: ignore
  109. pass # just a dummy if webview is not installed