apparser 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. apparser/__init__.py +1 -0
  2. apparser/core/__init__.py +4 -0
  3. apparser/core/app.py +95 -0
  4. apparser/core/ui/__init__.py +8 -0
  5. apparser/core/ui/base.py +51 -0
  6. apparser/core/ui/coordinates.py +118 -0
  7. apparser/core/ui/desktop.py +72 -0
  8. apparser/core/ui/window.py +75 -0
  9. apparser/core/ui/window_by_display.py +90 -0
  10. apparser/cv/__init__.py +6 -0
  11. apparser/cv/events/__init__.py +8 -0
  12. apparser/cv/events/base.py +14 -0
  13. apparser/cv/events/detected.py +13 -0
  14. apparser/cv/events/moved.py +13 -0
  15. apparser/cv/events/resized.py +13 -0
  16. apparser/cv/events/undetected.py +13 -0
  17. apparser/cv/handlers/__init__.py +5 -0
  18. apparser/cv/handlers/base.py +30 -0
  19. apparser/cv/handlers/default.py +74 -0
  20. apparser/cv/models/__init__.py +4 -0
  21. apparser/cv/models/data.py +33 -0
  22. apparser/cv/models/handler.py +15 -0
  23. apparser/cv/processes/__init__.py +5 -0
  24. apparser/cv/processes/base.py +31 -0
  25. apparser/cv/processes/default.py +64 -0
  26. apparser/cv/readers/__init__.py +4 -0
  27. apparser/cv/readers/base.py +19 -0
  28. apparser/cv/readers/yolo.py +69 -0
  29. apparser/cv/utils/__init__.py +3 -0
  30. apparser/cv/utils/changes_checker.py +84 -0
  31. apparser/exceptions/__init__.py +10 -0
  32. apparser/exceptions/debug.py +14 -0
  33. apparser/exceptions/instruction_not_found.py +37 -0
  34. apparser/exceptions/text_not_found.py +18 -0
  35. apparser/exceptions/timeout.py +13 -0
  36. apparser/exceptions/window_action_with_desktop.py +6 -0
  37. apparser/geometry/__init__.py +9 -0
  38. apparser/geometry/distance.py +21 -0
  39. apparser/geometry/relatively_point.py +45 -0
  40. apparser/instructions/__init__.py +4 -0
  41. apparser/instructions/base.py +24 -0
  42. apparser/instructions/debuggers/__init__.py +4 -0
  43. apparser/instructions/debuggers/base.py +23 -0
  44. apparser/instructions/debuggers/default.py +44 -0
  45. apparser/instructions/default/__init__.py +23 -0
  46. apparser/instructions/default/click.py +73 -0
  47. apparser/instructions/default/play_audio.py +88 -0
  48. apparser/instructions/default/play_audio_file.py +77 -0
  49. apparser/instructions/default/press.py +99 -0
  50. apparser/instructions/default/say_audio.py +92 -0
  51. apparser/instructions/default/say_audio_file.py +77 -0
  52. apparser/instructions/default/sleep.py +26 -0
  53. apparser/instructions/default/write_text.py +36 -0
  54. apparser/instructions/ocr/__init__.py +16 -0
  55. apparser/instructions/ocr/base.py +36 -0
  56. apparser/instructions/ocr/click_on_text.py +52 -0
  57. apparser/instructions/ocr/move_to_text.py +71 -0
  58. apparser/instructions/ocr/plot_text.py +81 -0
  59. apparser/instructions/ocr/print_all_text.py +33 -0
  60. apparser/instructions/ocr/text_getter.py +92 -0
  61. apparser/instructions/ocr/wait_text.py +69 -0
  62. apparser/instructions/speak/__init__.py +5 -0
  63. apparser/instructions/speak/base.py +34 -0
  64. apparser/instructions/speak/play_text.py +52 -0
  65. apparser/instructions/speak/say_text.py +52 -0
  66. apparser/instructions/ui/__init__.py +15 -0
  67. apparser/instructions/ui/algorithms/__init__.py +16 -0
  68. apparser/instructions/ui/algorithms/algorithm.py +60 -0
  69. apparser/instructions/ui/algorithms/base.py +47 -0
  70. apparser/instructions/ui/algorithms/ids.py +99 -0
  71. apparser/instructions/ui/algorithms/names.py +99 -0
  72. apparser/instructions/ui/algorithms/ocr.py +72 -0
  73. apparser/instructions/ui/algorithms/speak.py +79 -0
  74. apparser/instructions/ui/algorithms/unique.py +80 -0
  75. apparser/instructions/ui/base.py +29 -0
  76. apparser/instructions/ui/click.py +41 -0
  77. apparser/instructions/ui/mouse_move.py +38 -0
  78. apparser/instructions/ui/move_window.py +27 -0
  79. apparser/instructions/ui/resize_window.py +27 -0
  80. apparser/instructions/ui/to_window.py +25 -0
  81. apparser/instructions/utils/__init__.py +7 -0
  82. apparser/instructions/utils/get_all_instructions.py +21 -0
  83. apparser/instructions/utils/get_by_id.py +26 -0
  84. apparser/instructions/utils/get_by_name.py +26 -0
  85. apparser/key_codes/__init__.py +11 -0
  86. apparser/key_codes/base.py +14 -0
  87. apparser/key_codes/keyboard_keys.py +49 -0
  88. apparser/key_codes/mouse_keys.py +25 -0
  89. apparser/movers/__init__.py +7 -0
  90. apparser/movers/base.py +16 -0
  91. apparser/movers/default.py +34 -0
  92. apparser/movers/math_antirobot.py +123 -0
  93. apparser/speakers/__init__.py +5 -0
  94. apparser/speakers/base.py +18 -0
  95. apparser/speakers/chat_tts.py +124 -0
  96. apparser/speakers/torch.py +87 -0
  97. apparser/text_readers/__init__.py +14 -0
  98. apparser/text_readers/base.py +20 -0
  99. apparser/text_readers/easy_ocr.py +42 -0
  100. apparser/text_readers/models/__init__.py +0 -0
  101. apparser/text_readers/models/text_data.py +11 -0
  102. apparser/text_readers/paddle.py +126 -0
  103. apparser/text_readers/screens_controller.py +40 -0
  104. apparser/text_readers/white_black_reader.py +30 -0
  105. apparser-1.0.0.dist-info/METADATA +89 -0
  106. apparser-1.0.0.dist-info/RECORD +109 -0
  107. apparser-1.0.0.dist-info/WHEEL +5 -0
  108. apparser-1.0.0.dist-info/licenses/LICENSE.md +28 -0
  109. apparser-1.0.0.dist-info/top_level.txt +1 -0
apparser/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from apparser.core import *
@@ -0,0 +1,4 @@
1
+ from apparser.core.app import App
2
+ from apparser.core.ui import BaseUi, DesktopUi, CoordinatesUi, WindowUi, WindowByDisplayUi
3
+
4
+ __all__ = ["App", "BaseUi", "DesktopUi", "CoordinatesUi", "WindowUi", "WindowByDisplayUi"]
apparser/core/app.py ADDED
@@ -0,0 +1,95 @@
1
+ import os
2
+ import subprocess
3
+ import time
4
+
5
+ from appwindows import get_finder
6
+ from appwindows.exceptions import WindowDoesNotFoundException, WindowDoesNotValidException
7
+
8
+ from apparser.core.ui import WindowUi, BaseUi
9
+
10
+
11
+ class App:
12
+ """Manage an application process and its UI wrapper."""
13
+
14
+ def __init__(self, path_to_exe: str,
15
+ window_title: str | None = None,
16
+ timeout: float = 1):
17
+ """Initialize an application controller.
18
+
19
+ :param path_to_exe: Path to the executable file.
20
+ :type path_to_exe: str
21
+ :param window_title: Title of the window to attach to.
22
+ :type window_title: str
23
+ :param timeout: Delay before the window lookup starts.
24
+ :type timeout: float
25
+ :raises TypeError: If any argument has an invalid type.
26
+ """
27
+ if not isinstance(path_to_exe, str):
28
+ raise TypeError('path_to_exe must be a string')
29
+
30
+ if window_title is not None and not isinstance(window_title, str):
31
+ raise TypeError('window_title must be a string')
32
+
33
+ if not (isinstance(timeout, float) or isinstance(timeout, int)):
34
+ raise TypeError('timeout must be a number')
35
+
36
+ self.__window_finder = get_finder()
37
+ self.__process: subprocess.Popen | None = None
38
+ self.__path = path_to_exe
39
+ self.__timeout = timeout
40
+ self.__window_title_name: str = window_title
41
+ self.__ui: BaseUi | None = None
42
+ self.start_app()
43
+
44
+ def __find_window_by_title(self):
45
+ try:
46
+ window = self.__window_finder.get_window_by_title(self.__window_title_name)
47
+ self.__ui = WindowUi(window)
48
+ except WindowDoesNotFoundException:
49
+ pass
50
+
51
+ def __find_window_by_process_id(self, process_id: int):
52
+ try:
53
+ window = self.__window_finder.get_window_by_process_id(process_id)
54
+ self.__ui = WindowUi(window)
55
+ except WindowDoesNotFoundException:
56
+ pass
57
+
58
+ def start_app(self):
59
+ """Start the application process and bind its UI.
60
+
61
+ :raises WindowDoesNotValidException: If the window is not found or the application cannot be opened.
62
+ """
63
+ if self.__window_title_name is not None:
64
+ self.__find_window_by_title()
65
+ if self.__ui is not None:
66
+ return
67
+ window_processes = [i.get_process_id() for i in get_finder().get_all_windows()]
68
+ self.__process = subprocess.Popen([self.__path])
69
+ time.sleep(self.__timeout)
70
+ self.__find_window_by_process_id(os.getpid())
71
+ for i in get_finder().get_all_windows():
72
+ if self.__ui is not None:
73
+ return
74
+ if i.get_process_id() not in window_processes:
75
+ self.__find_window_by_process_id(i.get_process_id())
76
+ if self.__ui is not None:
77
+ return
78
+ self.__find_window_by_title()
79
+ if self.__ui is not None:
80
+ raise WindowDoesNotValidException()
81
+
82
+ def stop_app(self):
83
+ """Close the application window and stop the process."""
84
+ self.ui.window.close()
85
+ if self.__process is not None:
86
+ self.__process.kill()
87
+
88
+ @property
89
+ def ui(self) -> BaseUi:
90
+ """Return the UI wrapper for the running application.
91
+
92
+ :return: UI wrapper bound to the application window.
93
+ :rtype: BaseUi
94
+ """
95
+ return self.__ui
@@ -0,0 +1,8 @@
1
+ from apparser.core.ui.base import BaseUi
2
+ from apparser.core.ui.desktop import DesktopUi
3
+ from apparser.core.ui.coordinates import CoordinatesUi
4
+ from apparser.core.ui.window import WindowUi
5
+ from apparser.core.ui.window_by_display import WindowByDisplayUi
6
+
7
+
8
+ __all__ = ["BaseUi", "DesktopUi", "CoordinatesUi", "WindowUi", "WindowByDisplayUi"]
@@ -0,0 +1,51 @@
1
+ import abc
2
+
3
+ import numpy
4
+ from appwindows import Window
5
+
6
+ from apparser.geometry import Point, RelativelyPoint
7
+
8
+
9
+ class BaseUi(abc.ABC):
10
+ """Define the common interface for UI coordinate systems."""
11
+
12
+ @abc.abstractmethod
13
+ def point_to_global(self, coordinates: Point | RelativelyPoint) -> Point:
14
+ """Convert coordinates to the global screen space.
15
+
16
+ :param coordinates: Local or relative coordinates to convert.
17
+ :type coordinates: Point | RelativelyPoint
18
+ :return: Converted global point.
19
+ :rtype: Point
20
+ """
21
+ raise NotImplementedError()
22
+
23
+ @abc.abstractmethod
24
+ def point_to_local(self, coordinates: Point) -> Point:
25
+ """Convert coordinates to the local UI space.
26
+
27
+ :param coordinates: Global point to convert.
28
+ :type coordinates: Point
29
+ :return: Converted local point.
30
+ :rtype: Point
31
+ """
32
+ raise NotImplementedError()
33
+
34
+ @abc.abstractmethod
35
+ def get_screenshot(self) -> numpy.ndarray:
36
+ """Capture the current UI screenshot.
37
+
38
+ :return: Screenshot data.
39
+ :rtype: numpy.ndarray
40
+ """
41
+ raise NotImplementedError()
42
+
43
+ @property
44
+ @abc.abstractmethod
45
+ def window(self) -> Window:
46
+ """Return the window associated with the UI context.
47
+
48
+ :return: Underlying application window.
49
+ :rtype: Window
50
+ """
51
+ raise NotImplementedError()
@@ -0,0 +1,118 @@
1
+ from functools import singledispatchmethod
2
+
3
+ import numpy
4
+ from appwindows import Window
5
+ from appwindows.geometry import Size
6
+
7
+ from apparser.core.ui.base import BaseUi
8
+ from apparser.geometry import Point, RelativelyPoint
9
+
10
+
11
+ class CoordinatesUi(BaseUi):
12
+ """Represent a UI region defined by two points inside another UI context."""
13
+
14
+ def __init__(
15
+ self,
16
+ from_ui: BaseUi,
17
+ point_one: Point | RelativelyPoint,
18
+ point_two: Point | RelativelyPoint
19
+ ):
20
+ """Initialize a nested coordinate-based UI context.
21
+
22
+ :param from_ui: Source UI used as a parent context.
23
+ :type from_ui: BaseUi
24
+ :param point_one: First point of the nested region.
25
+ :type point_one: Point | RelativelyPoint
26
+ :param point_two: Second point of the nested region or region size.
27
+ :type point_two: Point | RelativelyPoint | Size
28
+ :raises TypeError: If any argument has an invalid type.
29
+ """
30
+ if not isinstance(from_ui, BaseUi):
31
+ raise TypeError('from_ui must be BaseUi')
32
+
33
+ if not isinstance(point_one, (Point, RelativelyPoint)):
34
+ raise TypeError('point1 must be Point or RelativelyPoint')
35
+
36
+ elif not isinstance(point_two, (Point, RelativelyPoint)):
37
+ raise TypeError('point2 must be Point, RelativelyPoint')
38
+
39
+ self.__from_ui = from_ui
40
+ self.__point_one = point_one
41
+ self.__point_two = point_two
42
+
43
+ def __point_to_main_ui_local(self, point: Point | RelativelyPoint) -> Point:
44
+ if isinstance(point, RelativelyPoint):
45
+ return self.__from_ui.point_to_local(self.__from_ui.point_to_global(point))
46
+ return point
47
+
48
+ def __get_local_bounds(self) -> tuple[Point, Point]:
49
+ point1 = self.__point_to_main_ui_local(self.__point_one)
50
+ point2 = self.__point_to_main_ui_local(self.__point_two)
51
+ left_top_point = Point(
52
+ min(point1.x, point2.x),
53
+ min(point1.y, point2.y),
54
+ )
55
+ right_bottom_point = Point(
56
+ max(point1.x, point2.x),
57
+ max(point1.y, point2.y),
58
+ )
59
+ return left_top_point, right_bottom_point
60
+
61
+ @singledispatchmethod
62
+ def point_to_global(self, coordinates: Point | RelativelyPoint) -> Point:
63
+ """Convert region coordinates to the global screen space.
64
+
65
+ :param coordinates: Local or relative coordinates to convert.
66
+ :type coordinates: Point | RelativelyPoint
67
+ :return: Converted global point.
68
+ :rtype: Point
69
+ """
70
+ raise NotImplementedError()
71
+
72
+ @point_to_global.register(Point)
73
+ def _(self, coordinates: Point) -> Point:
74
+ left_top_point, _ = self.__get_local_bounds()
75
+ return self.__from_ui.point_to_global(coordinates + left_top_point)
76
+
77
+ @point_to_global.register(RelativelyPoint)
78
+ def _(self, coordinates: RelativelyPoint) -> Point:
79
+ left_top_point, right_bottom_point = self.__get_local_bounds()
80
+ width = abs(round(right_bottom_point.x - left_top_point.x))
81
+ height = abs(round(right_bottom_point.y - left_top_point.y))
82
+ x = round(coordinates.x * width)
83
+ y = round(coordinates.y * height)
84
+ local_point = Point(x, y)
85
+ return self.point_to_global(local_point)
86
+
87
+ def point_to_local(self, coordinates: Point) -> Point:
88
+ """Convert global coordinates to the local region space.
89
+
90
+ :param coordinates: Global point to convert.
91
+ :type coordinates: Point
92
+ :return: Converted local point.
93
+ :rtype: Point
94
+ """
95
+ left_top_point, _ = self.__get_local_bounds()
96
+ return self.__from_ui.point_to_local(coordinates) - left_top_point
97
+
98
+ def get_screenshot(self) -> numpy.ndarray:
99
+ """Capture a screenshot cropped to the nested region.
100
+
101
+ :return: Screenshot data for the nested region.
102
+ :rtype: numpy.ndarray
103
+ """
104
+ screenshot = self.__from_ui.get_screenshot()
105
+ left_top_point, right_bottom_point = self.__get_local_bounds()
106
+ return screenshot[
107
+ left_top_point.y:right_bottom_point.y,
108
+ left_top_point.x:right_bottom_point.x,
109
+ ]
110
+
111
+ @property
112
+ def window(self) -> Window:
113
+ """Return the parent window for the nested region.
114
+
115
+ :return: Underlying parent window.
116
+ :rtype: Window
117
+ """
118
+ return self.__from_ui.window
@@ -0,0 +1,72 @@
1
+ from functools import singledispatchmethod
2
+
3
+ import numpy
4
+ from appwindows import Window
5
+ from PIL import ImageGrab
6
+ from screeninfo import get_monitors
7
+
8
+ from apparser.core.ui.base import BaseUi
9
+ from apparser.geometry import Point, RelativelyPoint
10
+ from apparser.exceptions import WindowActionWithDesktopException
11
+
12
+
13
+ class DesktopUi(BaseUi):
14
+ """Represent the full desktop as a UI context."""
15
+
16
+ def __init__(self, display_id: int = 0):
17
+ """Initialize a desktop UI context.
18
+
19
+ :param display_id: Monitor index used for relative coordinates.
20
+ :type display_id: int
21
+ """
22
+ self.__display_id = display_id
23
+
24
+ @singledispatchmethod
25
+ def point_to_global(self, coordinates: Point | RelativelyPoint) -> Point:
26
+ """Convert desktop coordinates to the global screen space.
27
+
28
+ :param coordinates: Local or relative coordinates to convert.
29
+ :type coordinates: Point | RelativelyPoint
30
+ :return: Converted global point.
31
+ :rtype: Point
32
+ """
33
+ raise NotImplementedError()
34
+
35
+ @point_to_global.register(Point)
36
+ def _(self, coordinates: Point):
37
+ return coordinates
38
+
39
+ @point_to_global.register(RelativelyPoint)
40
+ def _(self, coordinates: RelativelyPoint):
41
+ monitor = get_monitors()[self.__display_id]
42
+ x = round(coordinates.x * monitor.width)
43
+ y = round(coordinates.y * monitor.height)
44
+ local_point = Point(x, y)
45
+ return self.point_to_global(local_point)
46
+
47
+ def point_to_local(self, coordinates: Point) -> Point:
48
+ """Convert global coordinates to the desktop local space.
49
+
50
+ :param coordinates: Global point to convert.
51
+ :type coordinates: Point
52
+ :return: Converted local point.
53
+ :rtype: Point
54
+ """
55
+ return Point(coordinates.x, coordinates.y)
56
+
57
+ def get_screenshot(self) -> numpy.ndarray:
58
+ """Capture a screenshot of the desktop.
59
+
60
+ :return: Desktop screenshot data.
61
+ :rtype: numpy.ndarray
62
+ """
63
+ screenshot = ImageGrab.grab()
64
+ return numpy.asarray(screenshot)
65
+
66
+ @property
67
+ def window(self) -> Window:
68
+ """Raise an exception because the desktop has no window.
69
+
70
+ :raises WindowActionWithDesktopException: Always raised for desktop UI contexts.
71
+ """
72
+ raise WindowActionWithDesktopException()
@@ -0,0 +1,75 @@
1
+ from functools import singledispatchmethod
2
+
3
+ import numpy
4
+
5
+ from appwindows import Window
6
+ from appwindows.geometry import Point, Size
7
+
8
+ from apparser.core.ui.base import BaseUi
9
+ from apparser.geometry.relatively_point import RelativelyPoint
10
+
11
+
12
+ class WindowUi(BaseUi):
13
+ """Represent a window as a UI context."""
14
+
15
+ def __init__(self, window: Window):
16
+ """Initialize a window UI context.
17
+
18
+ :param window: Window instance to wrap.
19
+ :type window: Window
20
+ :raises TypeError: If ``window`` has an invalid type.
21
+ """
22
+ if not isinstance(window, Window):
23
+ raise TypeError('window must be Window')
24
+
25
+ self.__window = window
26
+
27
+ @singledispatchmethod
28
+ def point_to_global(self, coordinates: Point | RelativelyPoint) -> Point:
29
+ """Convert window coordinates to the global screen space.
30
+
31
+ :param coordinates: Local or relative coordinates to convert.
32
+ :type coordinates: Point | RelativelyPoint
33
+ :return: Converted global point.
34
+ :rtype: Point
35
+ """
36
+ raise NotImplementedError()
37
+
38
+ @point_to_global.register(Point)
39
+ def _(self, coordinates: Point):
40
+ return coordinates + self.__window.get_points().left_top
41
+
42
+ @point_to_global.register(RelativelyPoint)
43
+ def _(self, coordinates: RelativelyPoint):
44
+ size: Size = self.__window.get_size()
45
+ x = round(coordinates.x * size.width)
46
+ y = round(coordinates.y * size.height)
47
+ local_point = Point(x, y)
48
+ return self.point_to_global(local_point)
49
+
50
+ def point_to_local(self, coordinates: Point) -> Point:
51
+ """Convert global coordinates to the window local space.
52
+
53
+ :param coordinates: Global point to convert.
54
+ :type coordinates: Point
55
+ :return: Converted local point.
56
+ :rtype: Point
57
+ """
58
+ return coordinates - self.__window.get_points().left_top
59
+
60
+ def get_screenshot(self) -> numpy.ndarray:
61
+ """Capture a screenshot of the window.
62
+
63
+ :return: Window screenshot data.
64
+ :rtype: numpy.ndarray
65
+ """
66
+ return self.__window.get_screenshot()
67
+
68
+ @property
69
+ def window(self):
70
+ """Return the wrapped window instance.
71
+
72
+ :return: Wrapped window.
73
+ :rtype: Window
74
+ """
75
+ return self.__window
@@ -0,0 +1,90 @@
1
+ from functools import singledispatchmethod
2
+
3
+ import numpy
4
+ from PIL import ImageGrab
5
+
6
+ from appwindows import Window
7
+ from appwindows.geometry import Point, Size
8
+
9
+ from apparser.core.ui.base import BaseUi
10
+ from apparser.geometry.relatively_point import RelativelyPoint
11
+
12
+
13
+ class WindowByDisplayUi(BaseUi):
14
+ """
15
+ Represent a window as a display-captured UI context.
16
+ Unlike the WindowUi class, it retrieves the application's image based on its borders rather than from the graphical shell.
17
+ """
18
+
19
+ def __init__(self, window: Window) -> None:
20
+ """Initialize a display-captured window UI context.
21
+
22
+ :param window: Window instance to wrap.
23
+ :type window: Window
24
+ :raises TypeError: If ``window`` has an invalid type.
25
+ """
26
+ if not isinstance(window, Window):
27
+ raise TypeError('window must be Window')
28
+
29
+ self.__window = window
30
+
31
+ @singledispatchmethod
32
+ def point_to_global(self, coordinates: Point | RelativelyPoint) -> Point:
33
+ """Convert window coordinates to the global screen space.
34
+
35
+ :param coordinates: Local or relative coordinates to convert.
36
+ :type coordinates: Point | RelativelyPoint
37
+ :return: Converted global point.
38
+ :rtype: Point
39
+ """
40
+ raise NotImplementedError()
41
+
42
+ @point_to_global.register(Point)
43
+ def _(self, coordinates: Point) -> Point:
44
+ return coordinates + self.__window.get_points().left_top
45
+
46
+ @point_to_global.register(RelativelyPoint)
47
+ def _(self, coordinates: RelativelyPoint) -> Point:
48
+ size: Size = self.__window.get_size()
49
+ x = round(coordinates.x * size.width)
50
+ y = round(coordinates.y * size.height)
51
+ local_point = Point(x, y)
52
+ return self.point_to_global(local_point)
53
+
54
+ def point_to_local(self, coordinates: Point) -> Point:
55
+ """Convert global coordinates to the window local space.
56
+
57
+ :param coordinates: Global point to convert.
58
+ :type coordinates: Point
59
+ :return: Converted local point.
60
+ :rtype: Point
61
+ """
62
+ return coordinates - self.__window.get_points().left_top
63
+
64
+ def get_screenshot(self) -> numpy.ndarray:
65
+ """Capture a screenshot of the window from the display.
66
+
67
+ :return: Window screenshot data captured by window bounds.
68
+ :rtype: numpy.ndarray
69
+ """
70
+ left_top = self.__window.get_points().left_top
71
+ size: Size = self.__window.get_size()
72
+ screenshot = ImageGrab.grab(
73
+ bbox=(
74
+ left_top.x,
75
+ left_top.y,
76
+ left_top.x + size.width,
77
+ left_top.y + size.height,
78
+ ),
79
+ all_screens=True,
80
+ )
81
+ return numpy.asarray(screenshot)
82
+
83
+ @property
84
+ def window(self) -> Window:
85
+ """Return the wrapped window instance.
86
+
87
+ :return: Wrapped window.
88
+ :rtype: Window
89
+ """
90
+ return self.__window
@@ -0,0 +1,6 @@
1
+ """Public computer vision interfaces and default implementations."""
2
+
3
+ from apparser.cv.handlers.default import DefaultHandlers
4
+ from apparser.cv.processes.default import DefaultCvProcess
5
+ from apparser.cv.readers.yolo import YoloReader
6
+ from apparser.cv.models import *
@@ -0,0 +1,8 @@
1
+ from apparser.cv.events.base import CvEvent
2
+ from apparser.cv.events.moved import Moved
3
+ from apparser.cv.events.detected import Detected
4
+ from apparser.cv.events.resized import Resized
5
+ from apparser.cv.events.undetected import UnDetected
6
+
7
+ __all__ = ["CvEvent", "Moved",
8
+ "Detected", "Resized", "UnDetected"]
@@ -0,0 +1,14 @@
1
+ import abc
2
+
3
+
4
+ class CvEvent(abc.ABC):
5
+ """Define the interface for computer vision change events."""
6
+
7
+ @abc.abstractmethod
8
+ def __str__(self) -> str:
9
+ """Return the human-readable event name.
10
+
11
+ :return: Event name.
12
+ :rtype: str
13
+ """
14
+ raise NotImplementedError()
@@ -0,0 +1,13 @@
1
+ from apparser.cv.events.base import CvEvent
2
+
3
+
4
+ class Detected(CvEvent):
5
+ """Represent a newly detected object."""
6
+
7
+ def __str__(self) -> str:
8
+ """Return the event name.
9
+
10
+ :return: Detected event name.
11
+ :rtype: str
12
+ """
13
+ return "Detected"
@@ -0,0 +1,13 @@
1
+ from apparser.cv.events.base import CvEvent
2
+
3
+
4
+ class Moved(CvEvent):
5
+ """Represent a detected object whose position changed."""
6
+
7
+ def __str__(self) -> str:
8
+ """Return the event name.
9
+
10
+ :return: Moved event name.
11
+ :rtype: str
12
+ """
13
+ return "Moved"
@@ -0,0 +1,13 @@
1
+ from apparser.cv.events.base import CvEvent
2
+
3
+
4
+ class Resized(CvEvent):
5
+ """Represent a detected object whose size changed."""
6
+
7
+ def __str__(self) -> str:
8
+ """Return the event name.
9
+
10
+ :return: Resized event name.
11
+ :rtype: str
12
+ """
13
+ return "Resized"
@@ -0,0 +1,13 @@
1
+ from apparser.cv.events.base import CvEvent
2
+
3
+
4
+ class UnDetected(CvEvent):
5
+ """Represent a previously tracked object that disappeared."""
6
+
7
+ def __str__(self) -> str:
8
+ """Return the event name.
9
+
10
+ :return: Undetected event name.
11
+ :rtype: str
12
+ """
13
+ return "UnDetected"
@@ -0,0 +1,5 @@
1
+ from apparser.cv.handlers.base import CvHandlers
2
+ from apparser.cv.handlers.default import DefaultHandlers
3
+
4
+
5
+ __all__ = ["CvHandlers", "DefaultHandlers"]
@@ -0,0 +1,30 @@
1
+ import abc
2
+ from typing import Type
3
+
4
+ from apparser.cv.events import CvEvent
5
+ from apparser.cv.models import CvChangeData
6
+
7
+
8
+ class CvHandlers(abc.ABC):
9
+ """Define the interface for computer vision event handler registries."""
10
+
11
+ @abc.abstractmethod
12
+ def register_handler(self, event: Type[CvEvent]):
13
+ """Return a decorator that registers a handler for an event.
14
+
15
+ :param event: Event type to subscribe to.
16
+ :type event: Type[CvEvent]
17
+ """
18
+ pass
19
+
20
+ @abc.abstractmethod
21
+ def call(self, event: Type[CvEvent], changed_data: CvChangeData, *args):
22
+ """Call handlers registered for the provided event.
23
+
24
+ :param event: Event type to dispatch.
25
+ :type event: Type[CvEvent]
26
+ :param changed_data: Event payload with current and previous box data.
27
+ :type changed_data: CvChangeData
28
+ :param args: Additional context passed to handler functions.
29
+ """
30
+ pass