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.
- {ok-script-0.0.9 → ok-script-0.0.10}/PKG-INFO +9 -6
- {ok-script-0.0.9 → ok-script-0.0.10}/README.md +8 -5
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/OK.py +5 -2
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/HwndWindow.py +6 -2
- ok-script-0.0.10/ok/capture/adb/ADBCaptureMethod.py +57 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/DeviceManager.py +87 -64
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/WindowsGraphicsCaptureMethod.py +4 -2
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/Box.py +4 -4
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/FeatureSet.py +1 -1
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/App.py +8 -7
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/Communicate.py +6 -2
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/__init__.py +2 -0
- ok-script-0.0.10/ok/gui/debug/DebugTab.py +31 -0
- ok-script-0.0.10/ok/gui/debug/FrameWidget.py +58 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/debug/LoggerWidget.py +1 -0
- ok-script-0.0.10/ok/gui/debug/Screenshot.py +140 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/loading/LoadingWindow.py +45 -10
- ok-script-0.0.10/ok/gui/loading/SelectCaptureListView.py +31 -0
- ok-script-0.0.10/ok/gui/loading/SelectHwndWindow.py +70 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/resources.py +140 -128
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/RoundCornerContainer.py +1 -0
- ok-script-0.0.10/ok/interaction/BaseInteraction.py +24 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/logging/Logger.py +0 -1
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/ocr/OCR.py +2 -2
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/scene/Scene.py +0 -5
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/BaseTask.py +3 -1
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/ExecutorOperation.py +21 -17
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/TaskExecutor.py +18 -8
- ok-script-0.0.10/ok/util/path.py +77 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/PKG-INFO +9 -6
- {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/SOURCES.txt +3 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/setup.py +2 -2
- ok-script-0.0.9/ok/capture/adb/ADBCaptureMethod.py +0 -29
- ok-script-0.0.9/ok/gui/debug/DebugTab.py +0 -37
- ok-script-0.0.9/ok/gui/debug/FrameWidget.py +0 -125
- ok-script-0.0.9/ok/interaction/BaseInteraction.py +0 -36
- ok-script-0.0.9/ok/util/path.py +0 -37
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/BaseCaptureMethod.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/bin/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/targets.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/adb/vbox.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/d3d11.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/utils.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/capture/windows/window.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/color/Color.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/color/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/config/Config.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/config/InfoDict.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/config/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/Feature.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/FindFeature.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/feature/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/MainWindow.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/TabTitles.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/about/AboutTab.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/about/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/debug/AspectRatioWidget.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/debug/InfoWidget.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/debug/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/i18n/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/i18n/path.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/icon/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/icon/icon.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/loading/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/overlay/OverlayWindow.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/overlay/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/ConfigItemFactory.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/StartButton.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/TaskOpButton.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/TaskTab.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/TooltipTableWidget.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/tasks/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/util/Alert.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/util/InitWorker.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/util/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/ListTableWidgetItem.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/NumericTableWidgetItem.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/TabWidget.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/UpdateConfigWidgetItem.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/YesNonWidgetItem.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/gui/widget/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/interaction/ADBInteraction.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/interaction/Win32Interaction.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/interaction/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/logging/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/ocr/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/predict/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/predict/predict.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Media/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Security/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/Storage/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/Windows/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/delegate.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/export.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/idldsl.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/inspectable.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/roapi.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/types.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/rotypes/winstring.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/BlackBarProcessor.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/PostProcessor.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/SaveByInterval.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/SaveByKeyPress.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/SaveMethodBase.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/save/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/scene/FeatureScene.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/scene/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/stats/StreamStats.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/stats/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/FindFeatureTask.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/task/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/__init__.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/json.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/list.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/thread.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/win32_process.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok/util/yaml.py +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/dependency_links.txt +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/requires.txt +0 -0
- {ok-script-0.0.9 → ok-script-0.0.10}/ok_script.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|
-
|
|
24
|
-
windows,
|
|
25
|
-
emulator, or physical Android Phone. Currently still working in progress, there is a working
|
|
23
|
+
类似于AirScript, 使用Python
|
|
26
24
|
|
|
27
|
-
##
|
|
25
|
+
## 优势
|
|
28
26
|
|
|
29
|
-
|
|
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
|
-
|
|
4
|
-
windows,
|
|
5
|
-
emulator, or physical Android Phone. Currently still working in progress, there is a working
|
|
3
|
+
类似于AirScript, 使用Python
|
|
6
4
|
|
|
7
|
-
##
|
|
5
|
+
## 优势
|
|
8
6
|
|
|
9
|
-
|
|
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(
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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, "
|
|
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 =
|
|
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, "
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
|
|
179
|
+
self.hwnd.title = hwnd_name
|
|
126
180
|
self.capture_method.hwnd_window = self.hwnd
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
144
|
-
self.hwnd
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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.
|
|
160
|
-
self.
|
|
161
|
-
return
|
|
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.
|
|
166
|
-
self.
|
|
167
|
-
return
|
|
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
|
|
166
|
-
|
|
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):
|
|
@@ -18,20 +18,21 @@ logger = get_logger(__name__)
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class App:
|
|
21
|
-
def __init__(self,
|
|
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 =
|
|
29
|
-
self.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(
|
|
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.
|
|
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()
|
|
@@ -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)
|