ok-script 0.0.9__tar.gz → 0.0.10__tar.gz

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 (137) hide show
  1. {ok-script-0.0.9 → ok-script-0.0.10}/PKG-INFO +9 -6
  2. {ok-script-0.0.9 → ok-script-0.0.10}/README.md +8 -5
  3. {ok-script-0.0.9 → ok-script-0.0.10}/ok/OK.py +5 -2
  4. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/HwndWindow.py +6 -2
  5. ok-script-0.0.10/ok/capture/adb/ADBCaptureMethod.py +57 -0
  6. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/DeviceManager.py +87 -64
  7. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/WindowsGraphicsCaptureMethod.py +4 -2
  8. {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/Box.py +4 -4
  9. {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/FeatureSet.py +1 -1
  10. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/App.py +8 -7
  11. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/Communicate.py +6 -2
  12. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/__init__.py +2 -0
  13. ok-script-0.0.10/ok/gui/debug/DebugTab.py +31 -0
  14. ok-script-0.0.10/ok/gui/debug/FrameWidget.py +58 -0
  15. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/debug/LoggerWidget.py +1 -0
  16. ok-script-0.0.10/ok/gui/debug/Screenshot.py +140 -0
  17. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/loading/LoadingWindow.py +45 -10
  18. ok-script-0.0.10/ok/gui/loading/SelectCaptureListView.py +31 -0
  19. ok-script-0.0.10/ok/gui/loading/SelectHwndWindow.py +70 -0
  20. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/resources.py +140 -128
  21. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/RoundCornerContainer.py +1 -0
  22. ok-script-0.0.10/ok/interaction/BaseInteraction.py +24 -0
  23. {ok-script-0.0.9 → ok-script-0.0.10}/ok/logging/Logger.py +0 -1
  24. {ok-script-0.0.9 → ok-script-0.0.10}/ok/ocr/OCR.py +2 -2
  25. {ok-script-0.0.9 → ok-script-0.0.10}/ok/scene/Scene.py +0 -5
  26. {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/BaseTask.py +3 -1
  27. {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/ExecutorOperation.py +21 -17
  28. {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/TaskExecutor.py +18 -8
  29. ok-script-0.0.10/ok/util/path.py +77 -0
  30. {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/PKG-INFO +9 -6
  31. {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/SOURCES.txt +3 -0
  32. {ok-script-0.0.9 → ok-script-0.0.10}/setup.py +2 -2
  33. ok-script-0.0.9/ok/capture/adb/ADBCaptureMethod.py +0 -29
  34. ok-script-0.0.9/ok/gui/debug/DebugTab.py +0 -37
  35. ok-script-0.0.9/ok/gui/debug/FrameWidget.py +0 -125
  36. ok-script-0.0.9/ok/interaction/BaseInteraction.py +0 -36
  37. ok-script-0.0.9/ok/util/path.py +0 -37
  38. {ok-script-0.0.9 → ok-script-0.0.10}/ok/__init__.py +0 -0
  39. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/BaseCaptureMethod.py +0 -0
  40. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/__init__.py +0 -0
  41. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/__init__.py +0 -0
  42. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/bin/__init__.py +0 -0
  43. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/targets.py +0 -0
  44. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/vbox.py +0 -0
  45. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/__init__.py +0 -0
  46. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/d3d11.py +0 -0
  47. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/utils.py +0 -0
  48. {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/window.py +0 -0
  49. {ok-script-0.0.9 → ok-script-0.0.10}/ok/color/Color.py +0 -0
  50. {ok-script-0.0.9 → ok-script-0.0.10}/ok/color/__init__.py +0 -0
  51. {ok-script-0.0.9 → ok-script-0.0.10}/ok/config/Config.py +0 -0
  52. {ok-script-0.0.9 → ok-script-0.0.10}/ok/config/InfoDict.py +0 -0
  53. {ok-script-0.0.9 → ok-script-0.0.10}/ok/config/__init__.py +0 -0
  54. {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/Feature.py +0 -0
  55. {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/FindFeature.py +0 -0
  56. {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/__init__.py +0 -0
  57. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/MainWindow.py +0 -0
  58. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/TabTitles.py +0 -0
  59. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/about/AboutTab.py +0 -0
  60. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/about/__init__.py +0 -0
  61. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/debug/AspectRatioWidget.py +0 -0
  62. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/debug/InfoWidget.py +0 -0
  63. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/debug/__init__.py +0 -0
  64. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/i18n/__init__.py +0 -0
  65. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/i18n/path.py +0 -0
  66. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/icon/__init__.py +0 -0
  67. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/icon/icon.py +0 -0
  68. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/loading/__init__.py +0 -0
  69. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/overlay/OverlayWindow.py +0 -0
  70. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/overlay/__init__.py +0 -0
  71. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/ConfigItemFactory.py +0 -0
  72. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/StartButton.py +0 -0
  73. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/TaskOpButton.py +0 -0
  74. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/TaskTab.py +0 -0
  75. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/TooltipTableWidget.py +0 -0
  76. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/__init__.py +0 -0
  77. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/util/Alert.py +0 -0
  78. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/util/InitWorker.py +0 -0
  79. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/util/__init__.py +0 -0
  80. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/ListTableWidgetItem.py +0 -0
  81. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/NumericTableWidgetItem.py +0 -0
  82. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/TabWidget.py +0 -0
  83. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/UpdateConfigWidgetItem.py +0 -0
  84. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/YesNonWidgetItem.py +0 -0
  85. {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/__init__.py +0 -0
  86. {ok-script-0.0.9 → ok-script-0.0.10}/ok/interaction/ADBInteraction.py +0 -0
  87. {ok-script-0.0.9 → ok-script-0.0.10}/ok/interaction/Win32Interaction.py +0 -0
  88. {ok-script-0.0.9 → ok-script-0.0.10}/ok/interaction/__init__.py +0 -0
  89. {ok-script-0.0.9 → ok-script-0.0.10}/ok/logging/__init__.py +0 -0
  90. {ok-script-0.0.9 → ok-script-0.0.10}/ok/ocr/__init__.py +0 -0
  91. {ok-script-0.0.9 → ok-script-0.0.10}/ok/predict/__init__.py +0 -0
  92. {ok-script-0.0.9 → ok-script-0.0.10}/ok/predict/predict.py +0 -0
  93. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
  94. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
  95. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
  96. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
  97. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
  98. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
  99. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
  100. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
  101. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
  102. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Media/__init__.py +0 -0
  103. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
  104. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Security/__init__.py +0 -0
  105. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
  106. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Storage/__init__.py +0 -0
  107. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/__init__.py +0 -0
  108. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/__init__.py +0 -0
  109. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/delegate.py +0 -0
  110. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/export.py +0 -0
  111. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/idldsl.py +0 -0
  112. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/inspectable.py +0 -0
  113. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/roapi.py +0 -0
  114. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/types.py +0 -0
  115. {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/winstring.py +0 -0
  116. {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/BlackBarProcessor.py +0 -0
  117. {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/PostProcessor.py +0 -0
  118. {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/SaveByInterval.py +0 -0
  119. {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/SaveByKeyPress.py +0 -0
  120. {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/SaveMethodBase.py +0 -0
  121. {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/__init__.py +0 -0
  122. {ok-script-0.0.9 → ok-script-0.0.10}/ok/scene/FeatureScene.py +0 -0
  123. {ok-script-0.0.9 → ok-script-0.0.10}/ok/scene/__init__.py +0 -0
  124. {ok-script-0.0.9 → ok-script-0.0.10}/ok/stats/StreamStats.py +0 -0
  125. {ok-script-0.0.9 → ok-script-0.0.10}/ok/stats/__init__.py +0 -0
  126. {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/FindFeatureTask.py +0 -0
  127. {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/__init__.py +0 -0
  128. {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/__init__.py +0 -0
  129. {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/json.py +0 -0
  130. {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/list.py +0 -0
  131. {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/thread.py +0 -0
  132. {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/win32_process.py +0 -0
  133. {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/yaml.py +0 -0
  134. {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/dependency_links.txt +0 -0
  135. {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/requires.txt +0 -0
  136. {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/top_level.txt +0 -0
  137. {ok-script-0.0.9 → ok-script-0.0.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ok-script
3
- Version: 0.0.9
3
+ Version: 0.0.10
4
4
  Summary: Automation with Computer Vision for Python
5
5
  Home-page: https://github.com/ok-oldking/ok-script
6
6
  Author: ok-oldking
@@ -20,13 +20,16 @@ Requires-Dist: PySide6~=6.6.2
20
20
 
21
21
  # ok-script
22
22
 
23
- This project aims to provide a universal framework for developing automation tool for PC/Android games, running in
24
- windows,
25
- emulator, or physical Android Phone. Currently still working in progress, there is a working
23
+ 类似于AirScript, 使用Python
26
24
 
27
- ## Showcase
25
+ ## 优势
28
26
 
29
- ![docs/img_showcase.png](docs/img_showcase.png)
27
+ 1. 开源免费, 依赖库均为开源方案, 支持pip install任何第三方库
28
+ 2. 代码和打包流程完全由开发者控制, 不会上传服务器, 没有安全性隐患, 不容易被侦测和识别
29
+ 3. 一套代码即可支持Windows安卓模拟器/ADB连接的虚拟机, Windows客户端游戏, 安卓系统直接运行(开发中)
30
+ 4. 支持GUI以及Console运行, 也可以完全自己开发GUI
31
+ 5. 支持Python3.7以上版本
32
+ 6. 学习成本低, 自定义函数少, 也可以直接使用opencv, RapidOCR等api
30
33
 
31
34
  ## Example Usage
32
35
 
@@ -1,12 +1,15 @@
1
1
  # ok-script
2
2
 
3
- This project aims to provide a universal framework for developing automation tool for PC/Android games, running in
4
- windows,
5
- emulator, or physical Android Phone. Currently still working in progress, there is a working
3
+ 类似于AirScript, 使用Python
6
4
 
7
- ## Showcase
5
+ ## 优势
8
6
 
9
- ![docs/img_showcase.png](docs/img_showcase.png)
7
+ 1. 开源免费, 依赖库均为开源方案, 支持pip install任何第三方库
8
+ 2. 代码和打包流程完全由开发者控制, 不会上传服务器, 没有安全性隐患, 不容易被侦测和识别
9
+ 3. 一套代码即可支持Windows安卓模拟器/ADB连接的虚拟机, Windows客户端游戏, 安卓系统直接运行(开发中)
10
+ 4. 支持GUI以及Console运行, 也可以完全自己开发GUI
11
+ 5. 支持Python3.7以上版本
12
+ 6. 学习成本低, 自定义函数少, 也可以直接使用opencv, RapidOCR等api
10
13
 
11
14
  ## Example Usage
12
15
 
@@ -9,6 +9,7 @@ import ok
9
9
  from ok.feature.FeatureSet import FeatureSet
10
10
  from ok.gui.App import App
11
11
  from ok.gui.Communicate import communicate
12
+ from ok.gui.debug.Screenshot import Screenshot
12
13
  from ok.gui.overlay.OverlayWindow import OverlayWindow
13
14
  from ok.gui.util.InitWorker import InitWorker
14
15
  from ok.logging.Logger import get_logger, config_logger
@@ -27,16 +28,18 @@ class OK:
27
28
  ocr = None
28
29
  overlay_window = None
29
30
  app = None
31
+ screenshot = None
30
32
 
31
33
  def __init__(self, config: Dict[str, Any]):
32
34
  print(f"AutoHelper init, config: {config}")
35
+ ok.gui.ok = self
33
36
  self.debug = config.get("debug", False)
34
37
  self.exit_event = threading.Event()
35
38
  self.config = config
36
39
  self.init_device_manager()
40
+ self.screenshot = Screenshot()
37
41
  if config.get("use_gui"):
38
- self.app = App(self.config.get('gui_icon'), self.debug, self.config.get('gui_title'),
39
- self.config['tasks'], self.config.get('about'), self.exit_event)
42
+ self.app = App(config, self.exit_event)
40
43
  ok.gui.app = self.app
41
44
  else:
42
45
  self.device_manager.set_preferred_device()
@@ -54,6 +54,7 @@ class HwndWindow:
54
54
  self.title = re.compile(title)
55
55
 
56
56
  def update_frame_size(self, width, height):
57
+ logger.debug(f"update_frame_size:{self.frame_width}x{self.frame_height} to {width}x{height}")
57
58
  if width != self.frame_width or height != self.frame_height:
58
59
  self.frame_width = width
59
60
  self.frame_height = height
@@ -127,8 +128,11 @@ def find_hwnds_by_title(title):
127
128
  hwnds = []
128
129
 
129
130
  def enum_windows_proc(hwnd, lParam):
130
- if win32gui.IsWindowVisible(hwnd) and win32gui.GetWindowText(hwnd):
131
- if re.search(title, win32gui.GetWindowText(hwnd)):
131
+ text = win32gui.GetWindowText(hwnd)
132
+ if text and win32gui.IsWindowVisible(hwnd):
133
+ if isinstance(title, str) and title == text:
134
+ hwnds.append(hwnd)
135
+ elif re.search(title, text):
132
136
  hwnds.append(hwnd)
133
137
 
134
138
  win32gui.EnumWindows(enum_windows_proc, None)
@@ -0,0 +1,57 @@
1
+ # original https://github.com/Toufool/AutoSplit/blob/master/src/capture_method/WindowsGraphicsCaptureMethod.py
2
+ import cv2
3
+ import numpy as np
4
+ from typing_extensions import override
5
+
6
+ from ok.capture.BaseCaptureMethod import BaseCaptureMethod
7
+ from ok.logging.Logger import get_logger
8
+
9
+ logger = get_logger(__name__)
10
+
11
+
12
+ class ADBCaptureMethod(BaseCaptureMethod):
13
+ name = "ADB command line Capture"
14
+ description = "use the adb screencap command, slow but works when in background/minimized, takes 300ms per frame"
15
+
16
+ def __init__(self, device, exit_event, width=0, height=0):
17
+ super().__init__()
18
+ self.exit_event = exit_event
19
+ self._size = (width, height)
20
+ self.device = device
21
+
22
+ @property
23
+ def width(self):
24
+ if self._size[0] == 0:
25
+ self.screencap()
26
+ return self._size[0]
27
+
28
+ @property
29
+ def height(self):
30
+ if self._size[1] == 0:
31
+ self.screencap()
32
+ return self._size[1]
33
+
34
+ @override
35
+ def get_frame(self) -> np.ndarray | None:
36
+ return self.screencap()
37
+
38
+ def screencap(self):
39
+ if self.exit_event.is_set():
40
+ return None
41
+ frame = do_screencap(self.device)
42
+ if frame is not None:
43
+ height, width, _ = frame.shape
44
+ self._size = (width, height)
45
+ return frame
46
+
47
+
48
+ def do_screencap(device) -> np.ndarray | None:
49
+ if device is not None:
50
+ png_bytes = device.shell("screencap -p", encoding=None)
51
+ if png_bytes is not None and len(png_bytes) > 0:
52
+ image_data = np.frombuffer(png_bytes, dtype=np.uint8)
53
+ image = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
54
+ if image is not None:
55
+ return image
56
+ else:
57
+ logger.error(f"Screencap image decode error, probably disconnected")
@@ -1,11 +1,9 @@
1
1
  import threading
2
2
 
3
3
  import adbutils
4
- import cv2
5
- import numpy as np
6
4
 
7
5
  from ok.capture.HwndWindow import HwndWindow
8
- from ok.capture.adb.ADBCaptureMethod import ADBCaptureMethod
6
+ from ok.capture.adb.ADBCaptureMethod import ADBCaptureMethod, do_screencap
9
7
  from ok.capture.adb.vbox import installed_emulator
10
8
  from ok.capture.windows.WindowsGraphicsCaptureMethod import WindowsGraphicsCaptureMethod
11
9
  from ok.config.Config import Config
@@ -29,7 +27,6 @@ class DeviceManager:
29
27
  if hwnd_title is not None:
30
28
  self.hwnd = HwndWindow(hwnd_title, exit_event)
31
29
  self.config = Config({"devices": {}}, config_folder, "DeviceManager")
32
- self._size = (0, 0)
33
30
  self.thread = None
34
31
  self.capture_method = None
35
32
  self.refresh()
@@ -68,8 +65,7 @@ class DeviceManager:
68
65
 
69
66
  device_list = sorted(device_list, key=lambda x: x.serial)
70
67
 
71
- for device in self.device_dict.values():
72
- device['connected'] = False
68
+ adb_connected = []
73
69
 
74
70
  for device in device_list:
75
71
  imei = device.shell("settings get secure android_id") or device.shell(
@@ -78,7 +74,8 @@ class DeviceManager:
78
74
  width, height = 0, 0
79
75
  if frame is not None:
80
76
  height, width, _ = frame.shape
81
- adb_device = {"address": device.serial, "imei": imei, "method": "adb",
77
+ adb_device = {"address": device.serial, "imei": imei, "device": "adb", "capture": "adb", "width": width,
78
+ "height": height,
82
79
  "model": device.prop.model, "nick": device.prop.model, "connected": True, "preferred": False,
83
80
  "resolution": f"{width}x{height}"}
84
81
  found = False
@@ -88,19 +85,31 @@ class DeviceManager:
88
85
  found = True
89
86
  if self.device_dict.get(imei):
90
87
  if found or not self.device_dict[imei]['connected']:
88
+ old_capture = self.device_dict[imei].get("capture")
89
+ old_hwnd = self.device_dict[imei].get("hwnd")
91
90
  self.device_dict[imei] = adb_device
91
+ if old_capture is not None:
92
+ self.device_dict[imei]['capture'] = old_capture
93
+ if old_hwnd is not None:
94
+ self.device_dict[imei]['hwnd'] = old_hwnd
92
95
  else:
93
96
  self.device_dict[imei] = adb_device
97
+ adb_connected.append(imei)
98
+ for imei in self.device_dict:
99
+ if imei not in adb_connected:
100
+ self.device_dict[imei]['connected'] = False
94
101
  if self.hwnd_title:
95
- nick = self.hwnd_title
102
+ nick = ""
96
103
  width = 0
97
104
  height = 0
98
105
  if self.hwnd:
99
106
  nick = self.hwnd.title_text()
100
107
  width = self.hwnd.width
101
108
  height = self.hwnd.height
102
- self.device_dict[self.hwnd_title] = {"address": "", "imei": self.hwnd_title, "method": "windows",
103
- "model": "", "nick": nick or self.hwnd_title,
109
+ self.device_dict[self.hwnd_title] = {"address": "", "imei": self.hwnd_title, "device": "windows",
110
+ "model": "", "nick": nick or self.hwnd_title, "width": width,
111
+ "height": height,
112
+ "hwnd": nick, "capture": "windows",
104
113
  "connected": self.hwnd is not None and self.hwnd.exists,
105
114
  "resolution": f"{width}x{height}"}
106
115
  communicate.adb_devices.emit()
@@ -114,41 +123,77 @@ class DeviceManager:
114
123
  preferred = self.device_dict.get(imei)
115
124
  if preferred is None and len(self.device_dict) > 0:
116
125
  preferred = next(iter(self.device_dict.values()))
117
- if preferred is not None:
118
126
  imei = preferred['imei']
119
- preferred['preferred'] = True
120
- if preferred['method'] == 'windows':
127
+ if self.config.get("preferred") != imei:
128
+ self.config["preferred"] = imei
129
+ self.config.save_file()
130
+ logger.debug(f'preferred device: {preferred}')
131
+
132
+ def get_preferred_device(self):
133
+ imei = self.config.get("preferred")
134
+ preferred = self.device_dict.get(imei)
135
+ if preferred is None:
136
+ raise ValueError(f"preferred device {imei} is none")
137
+ return preferred
138
+
139
+ def set_hwnd_name(self, hwnd_name):
140
+ preferred = self.get_preferred_device()
141
+ if preferred.get("hwnd") != hwnd_name:
142
+ preferred['hwnd'] = hwnd_name
143
+ self.config.save_file()
144
+
145
+ def set_capture(self, capture):
146
+ preferred = self.get_preferred_device()
147
+ preferred['capture'] = capture
148
+ self.config.save_file()
149
+
150
+ def get_hwnd_name(self):
151
+ preferred = self.get_preferred_device()
152
+ return preferred.get('hwnd')
153
+
154
+ def start(self):
155
+ preferred = self.get_preferred_device()
156
+ if preferred['device'] == 'windows':
157
+ if not isinstance(self.capture_method, WindowsGraphicsCaptureMethod):
158
+ if self.capture_method is not None:
159
+ self.capture_method.close()
160
+ self.capture_method = WindowsGraphicsCaptureMethod(self.hwnd)
161
+ self.interaction = Win32Interaction(self.capture_method)
162
+ self.capture_method.hwnd_window = self.hwnd
163
+ else:
164
+ for adb_device in self.adb.device_list():
165
+ if adb_device.serial == preferred.get('address'):
166
+ self._device = adb_device
167
+ hwnd_name = preferred.get('hwnd')
168
+ width = preferred.get('width', 0)
169
+ height = preferred.get('height', 0)
170
+ if preferred.get('capture') == "windows":
171
+ if not hwnd_name:
172
+ raise ValueError(f"adb device preferred hwnd is none, capture is windows {preferred}")
173
+ if self.hwnd is None:
174
+ self.hwnd = HwndWindow(hwnd_name, self.exit_event, width, height)
121
175
  if not isinstance(self.capture_method, WindowsGraphicsCaptureMethod):
122
176
  if self.capture_method is not None:
123
177
  self.capture_method.close()
124
178
  self.capture_method = WindowsGraphicsCaptureMethod(self.hwnd)
125
- self.interaction = Win32Interaction(self.capture_method)
179
+ self.hwnd.title = hwnd_name
126
180
  self.capture_method.hwnd_window = self.hwnd
127
- else:
128
- for adb_device in self.adb.device_list():
129
- if adb_device.serial == preferred.get('address'):
130
- self._device = adb_device
131
- if not isinstance(self.capture_method, ADBCaptureMethod):
132
- if self.capture_method is not None:
133
- self.capture_method.close()
134
- self.capture_method = ADBCaptureMethod(self)
135
- self.interaction = ADBBaseInteraction(self, self.capture_method)
136
- self.config["preferred"] = imei
137
- self.config.save_file()
138
- logger.debug(f'preferred device: {preferred}')
139
-
140
- def start(self):
141
- if isinstance(self.capture_method, ADBCaptureMethod):
181
+ elif not isinstance(self.capture_method, ADBCaptureMethod):
182
+ if self.capture_method is not None:
183
+ self.capture_method.close()
184
+ self.capture_method = ADBCaptureMethod(self._device, self.exit_event, width=width,
185
+ height=height)
186
+ self.interaction = ADBBaseInteraction(self, self.capture_method)
142
187
  if self.debug:
143
- if self.hwnd is not None:
144
- self.hwnd.update_title_re("ok_debug")
145
- self.hwnd.update_frame_size(self.capture_method.width, self.capture_method.height)
146
- else:
147
- self.hwnd = HwndWindow("ok_debug", self.exit_event, self.capture_method.width,
148
- self.capture_method.height)
149
- else:
188
+ if self.hwnd is None and hwnd_name:
189
+ if self.hwnd is None:
190
+ self.hwnd = HwndWindow(hwnd_name, self.exit_event, width, height)
191
+ self.hwnd.title = hwnd_name
192
+ elif self.hwnd is not None:
150
193
  self.hwnd.stop()
151
194
  self.hwnd = None
195
+ if self.hwnd:
196
+ self.hwnd.update_frame_size(width, height)
152
197
 
153
198
  @property
154
199
  def device(self):
@@ -156,15 +201,15 @@ class DeviceManager:
156
201
 
157
202
  @property
158
203
  def width(self):
159
- if self._size[0] == 0:
160
- self.screencap()
161
- return self._size[0]
204
+ if self.capture_method is not None:
205
+ return self.capture_method.width
206
+ return 0
162
207
 
163
208
  @property
164
209
  def height(self):
165
- if self._size[1] == 0:
166
- self.screencap()
167
- return self._size[1]
210
+ if self.capture_method is not None:
211
+ return self.capture_method.height
212
+ return 0
168
213
 
169
214
  def update_device_list(self):
170
215
  pass
@@ -176,25 +221,3 @@ class DeviceManager:
176
221
  return device.shell(*args, **kwargs)
177
222
  except Exception as e:
178
223
  logger.error(f"shell_wrapper error occurred: {e}")
179
-
180
- def screencap(self):
181
- if self.exit_event.is_set():
182
- return None
183
- self._size = (0, 0)
184
- frame = do_screencap(self.device)
185
- if frame is not None:
186
- height, width, _ = frame.shape
187
- self._size = (width, height)
188
- return frame
189
-
190
-
191
- def do_screencap(device) -> np.ndarray | None:
192
- if device is not None:
193
- png_bytes = device.shell("screencap -p", encoding=None)
194
- if png_bytes is not None and len(png_bytes) > 0:
195
- image_data = np.frombuffer(png_bytes, dtype=np.uint8)
196
- image = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
197
- if image is not None:
198
- return image
199
- else:
200
- logger.error(f"Screencap image decode error, probably disconnected")
@@ -162,8 +162,10 @@ class WindowsGraphicsCaptureMethod(BaseCaptureMethod):
162
162
  self.session.Close() # E_UNEXPECTED ???
163
163
  self.session = None
164
164
  self.item = None
165
- self.rtdevice.Release()
166
- self.dxdevice.Release()
165
+ if self.rtdevice:
166
+ self.rtdevice.Release()
167
+ if self.dxdevice:
168
+ self.dxdevice.Release()
167
169
  if self.cputex:
168
170
  self.cputex.Release()
169
171
 
@@ -6,10 +6,10 @@ import re
6
6
  class Box:
7
7
  def __init__(self, x: int, y: int, width: int, height: int, confidence: float = 1, name=None) -> None:
8
8
  self.name = name
9
- self.x = x
10
- self.y = y
11
- self.width = width
12
- self.height = height
9
+ self.x = int(x)
10
+ self.y = int(y)
11
+ self.width = int(width)
12
+ self.height = int(height)
13
13
  self.confidence = confidence
14
14
 
15
15
  def __eq__(self, other):
@@ -184,7 +184,7 @@ class FeatureSet:
184
184
  # cv2.imwrite("images/test.jpg", mat)
185
185
 
186
186
  result = sort_boxes(boxes)
187
- communicate.draw_box.emit(category_name, result, "red")
187
+ communicate.emit_draw_box(category_name, result, "red")
188
188
  return result
189
189
 
190
190
 
@@ -18,20 +18,21 @@ logger = get_logger(__name__)
18
18
 
19
19
 
20
20
  class App:
21
- def __init__(self, icon=None, overlay=False, title="AutoUI", tasks=None, about=None,
21
+ def __init__(self, config,
22
22
  exit_event=None):
23
23
  super().__init__()
24
+ self.config = config
24
25
  self.app = QApplication(sys.argv)
25
26
  self.app.setStyle(QStyleFactory.create("Fusion"))
26
- self.tasks = tasks
27
- self.about = about
28
- self.title = title
29
- self.overlay = overlay
27
+ self.tasks = self.config['tasks']
28
+ self.about = self.config.get('about')
29
+ self.title = self.config.get('gui_title')
30
+ self.overlay = self.config.get('debug')
30
31
  self.loading_window = None
31
32
  self.overlay_window = None
32
33
  self.main_window = None
33
34
  self.exit_event = exit_event
34
- self.icon = QIcon(icon or ":/icon/icon.ico")
35
+ self.icon = QIcon(self.config.get('gui_icon') or ":/icon/icon.ico")
35
36
  self.tray = QSystemTrayIcon(self.icon)
36
37
 
37
38
  locale = QLocale.system().name()
@@ -64,7 +65,7 @@ class App:
64
65
 
65
66
  def show_loading(self):
66
67
  self.loading_window = LoadingWindow(self, self.exit_event)
67
- size = self.size_relative_to_screen(width=0.4, height=0.4)
68
+ size = self.size_relative_to_screen(width=0.6, height=0.4)
68
69
  self.loading_window.resize(size)
69
70
  self.loading_window.setMinimumSize(size)
70
71
  self.center_window(self.loading_window)
@@ -3,12 +3,11 @@ from PySide6.QtCore import Signal, QObject
3
3
 
4
4
 
5
5
  class Communicate(QObject):
6
- frame = Signal(np.ndarray)
7
6
  log = Signal(int, str)
8
7
  fps = Signal(int)
9
8
  frame_time = Signal(int)
10
9
  scene = Signal(str)
11
- draw_box = Signal(str, object, str)
10
+ draw_box = Signal(str, object, str, np.ndarray)
12
11
  tasks = Signal()
13
12
  task_info = Signal()
14
13
  window = Signal(bool, int, int, int, int, int, int, float)
@@ -16,7 +15,12 @@ class Communicate(QObject):
16
15
  init = Signal(bool, str)
17
16
  notification = Signal(str, str)
18
17
  executor_paused: Signal = Signal(bool)
18
+ screenshot = Signal(np.ndarray, str)
19
+ update_overlay = Signal()
19
20
  adb_devices: Signal = Signal()
20
21
 
22
+ def emit_draw_box(self, key: str = None, boxes=None, color=None, frame=None):
23
+ self.draw_box.emit(key, boxes, color, frame)
24
+
21
25
 
22
26
  communicate = Communicate()
@@ -1,5 +1,7 @@
1
+ from ok.OK import OK
1
2
  from ok.capture.adb.DeviceManager import DeviceManager
2
3
 
4
+ ok: OK | None = None
3
5
  executor = None
4
6
  app = None
5
7
  device_manager: DeviceManager | None = None
@@ -0,0 +1,31 @@
1
+ from PySide6.QtWidgets import QWidget, QHBoxLayout, QSizePolicy
2
+
3
+ from ok.gui.debug.InfoWidget import InfoWidget
4
+ from ok.gui.debug.LoggerWidget import LoggerWidget
5
+
6
+
7
+ class DebugTab(QWidget):
8
+ def __init__(self):
9
+ super().__init__()
10
+
11
+ self.mainLayout = QHBoxLayout()
12
+ self.setLayout(self.mainLayout)
13
+
14
+ self.info_widget = InfoWidget()
15
+ self.info_widget.setFixedWidth(200)
16
+ # self.frame_widget = FrameWidget(True)
17
+ self.mainLayout.addWidget(self.info_widget)
18
+ # self.topSplitter.addWidget(self.frame_widget)
19
+
20
+ # Bottom row setup
21
+ self.logger = LoggerWidget()
22
+
23
+ self.mainLayout.addWidget(self.logger)
24
+
25
+ p1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
26
+ p1.setHorizontalStretch(1) # 33% of the layout
27
+ self.info_widget.setSizePolicy(p1)
28
+
29
+ p2 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
30
+ p2.setHorizontalStretch(2) # 67% of the layout
31
+ self.logger.setSizePolicy(p2)
@@ -0,0 +1,58 @@
1
+ import os
2
+
3
+ from PySide6.QtCore import Qt
4
+ from PySide6.QtGui import QPainter, QColor, QPen
5
+ from PySide6.QtWidgets import QWidget
6
+
7
+ import ok.gui
8
+ from ok.gui.Communicate import communicate
9
+ from ok.logging.Logger import get_logger
10
+
11
+ logger = get_logger(__name__)
12
+
13
+
14
+ class FrameWidget(QWidget):
15
+ def __init__(self):
16
+ super(FrameWidget, self).__init__()
17
+
18
+ self._visible = True
19
+ communicate.update_overlay.connect(self.update)
20
+
21
+ def frame_ratio(self):
22
+ if ok.gui.device_manager.width == 0:
23
+ return 1
24
+ return self.width() / ok.gui.device_manager.width
25
+
26
+ def paintEvent(self, event):
27
+ if not self._visible:
28
+ return
29
+ painter = QPainter(self)
30
+ self.paint_border(painter)
31
+ self.paint_boxes(painter)
32
+
33
+ def paint_boxes(self, painter):
34
+ pen = QPen() # Set the brush to red color
35
+ pen.setWidth(2) # Set the width of the pen (border thickness)
36
+ painter.setPen(pen) # Apply the pen to the painter
37
+ painter.setBrush(Qt.NoBrush) # Ensure no fill
38
+
39
+ frame_ratio = self.frame_ratio()
40
+ for key, value in ok.gui.ok.screenshot.ui_dict.items():
41
+ boxes = value[0]
42
+ pen.setColor(value[2])
43
+ painter.setPen(pen)
44
+ for box in boxes:
45
+ width = box.width * frame_ratio
46
+ height = box.height * frame_ratio
47
+ x = box.x * frame_ratio
48
+ y = box.y * frame_ratio
49
+ painter.drawRect(x, y, width, height)
50
+ painter.drawText(x, y + height + 12, f"{box.name or key}_{round(box.confidence * 100)}")
51
+
52
+ def paint_border(self, painter):
53
+ pen = QPen(QColor(255, 0, 0, 255)) # Solid red color for the border
54
+ pen.setWidth(1) # Set the border width
55
+ painter.setPen(pen)
56
+
57
+ # Draw the border around the widget
58
+ painter.drawRect(0, 0, self.width() - 1, self.height() - 1)
@@ -47,6 +47,7 @@ class LoggerWidget(QWidget):
47
47
  # Log display
48
48
  self.log_display = QTextEdit()
49
49
  self.log_display.setReadOnly(True)
50
+ self.log_display.setStyleSheet("line-height:120%;")
50
51
 
51
52
  # Add widgets to layout
52
53
  self.layout.addLayout(controls_layout)