ok-script 0.0.12__tar.gz → 0.0.13__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.12 → ok-script-0.0.13}/PKG-INFO +2 -1
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/OK.py +8 -7
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/BaseCaptureMethod.py +3 -0
- ok-script-0.0.13/ok/capture/HwndWindow.py +198 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/adb/ADBCaptureMethod.py +19 -8
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/adb/DeviceManager.py +67 -46
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/adb/vbox.py +12 -11
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/windows/WindowsGraphicsCaptureMethod.py +6 -1
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/windows/utils.py +0 -19
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/config/Config.py +6 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/feature/Box.py +42 -5
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/App.py +20 -18
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/Communicate.py +3 -2
- ok-script-0.0.13/ok/gui/MainWindow.py +89 -0
- ok-script-0.0.13/ok/gui/__init__.py +6 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/about/AboutTab.py +5 -6
- ok-script-0.0.13/ok/gui/common/OKIcon.py +13 -0
- ok-script-0.0.13/ok/gui/common/config.py +58 -0
- ok-script-0.0.13/ok/gui/common/style_sheet.py +23 -0
- ok-script-0.0.13/ok/gui/debug/DebugTab.py +17 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/debug/LoggerWidget.py +8 -6
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/debug/Screenshot.py +2 -2
- ok-script-0.0.13/ok/gui/i18n/update.py +29 -0
- ok-script-0.0.13/ok/gui/resources.py +1057 -0
- ok-script-0.0.13/ok/gui/settings/SettingTab.py +117 -0
- {ok-script-0.0.12/ok/gui/loading → ok-script-0.0.13/ok/gui/start}/SelectCaptureListView.py +6 -5
- {ok-script-0.0.12/ok/gui/loading → ok-script-0.0.13/ok/gui/start}/SelectHwndWindow.py +20 -23
- ok-script-0.0.13/ok/gui/start/StartCard.py +93 -0
- ok-script-0.0.13/ok/gui/start/StartTab.py +140 -0
- ok-script-0.0.13/ok/gui/tasks/ConfigItemFactory.py +20 -0
- ok-script-0.0.13/ok/gui/tasks/ConfigLabelAndWidget.py +10 -0
- ok-script-0.0.13/ok/gui/tasks/LabelAndDoubleSpinBox.py +21 -0
- ok-script-0.0.13/ok/gui/tasks/LabelAndLineEdit.py +21 -0
- ok-script-0.0.13/ok/gui/tasks/LabelAndList.py +18 -0
- ok-script-0.0.13/ok/gui/tasks/LabelAndSpinBox.py +21 -0
- ok-script-0.0.13/ok/gui/tasks/LabelAndSwitchButton.py +21 -0
- ok-script-0.0.13/ok/gui/tasks/LabelAndWidget.py +21 -0
- ok-script-0.0.13/ok/gui/tasks/ModifyListDialog.py +97 -0
- ok-script-0.0.13/ok/gui/tasks/ModifyListItem.py +32 -0
- ok-script-0.0.13/ok/gui/tasks/OneTimeTaskTab.py +63 -0
- ok-script-0.0.13/ok/gui/tasks/TaskCard.py +97 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/tasks/TooltipTableWidget.py +4 -3
- ok-script-0.0.13/ok/gui/tasks/TriggerTaskTab.py +17 -0
- ok-script-0.0.13/ok/gui/widget/Card.py +59 -0
- ok-script-0.0.13/ok/gui/widget/FramelessDialog.py +13 -0
- ok-script-0.0.13/ok/gui/widget/StatusBar.py +131 -0
- ok-script-0.0.13/ok/gui/widget/Tab.py +37 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/interaction/ADBInteraction.py +7 -10
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/interaction/BaseInteraction.py +3 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/interaction/Win32Interaction.py +37 -1
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/ocr/OCR.py +5 -1
- ok-script-0.0.13/ok/stats/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/task/BaseTask.py +43 -38
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/task/ExecutorOperation.py +21 -7
- ok-script-0.0.13/ok/task/OneTimeTask.py +35 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/task/TaskExecutor.py +86 -56
- ok-script-0.0.13/ok/task/TriggerTask.py +29 -0
- ok-script-0.0.13/ok/task/__init__.py +0 -0
- ok-script-0.0.13/ok/util/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok_script.egg-info/PKG-INFO +2 -1
- {ok-script-0.0.12 → ok-script-0.0.13}/ok_script.egg-info/SOURCES.txt +31 -7
- {ok-script-0.0.12 → ok-script-0.0.13}/ok_script.egg-info/requires.txt +1 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/setup.py +3 -2
- ok-script-0.0.12/ok/capture/HwndWindow.py +0 -142
- ok-script-0.0.12/ok/gui/MainWindow.py +0 -73
- ok-script-0.0.12/ok/gui/__init__.py +0 -7
- ok-script-0.0.12/ok/gui/debug/DebugTab.py +0 -31
- ok-script-0.0.12/ok/gui/loading/LoadingWindow.py +0 -182
- ok-script-0.0.12/ok/gui/resources.py +0 -533
- ok-script-0.0.12/ok/gui/tasks/ConfigItemFactory.py +0 -14
- ok-script-0.0.12/ok/gui/tasks/TaskTab.py +0 -121
- ok-script-0.0.12/ok/gui/widget/RoundCornerContainer.py +0 -60
- ok-script-0.0.12/ok/task/FindFeatureTask.py +0 -6
- {ok-script-0.0.12 → ok-script-0.0.13}/README.md +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/adb/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/adb/bin/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/adb/targets.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/windows/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/windows/d3d11.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/capture/windows/window.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/color/Color.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/color/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/config/InfoDict.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/config/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/feature/Feature.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/feature/FeatureSet.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/feature/FindFeature.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/feature/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/TabTitles.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/about/__init__.py +0 -0
- {ok-script-0.0.12/ok/gui/debug → ok-script-0.0.13/ok/gui/common}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/debug/AspectRatioWidget.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/debug/FrameWidget.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/debug/InfoWidget.py +0 -0
- {ok-script-0.0.12/ok/gui/i18n → ok-script-0.0.13/ok/gui/debug}/__init__.py +0 -0
- {ok-script-0.0.12/ok/gui/icon → ok-script-0.0.13/ok/gui/i18n}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/i18n/path.py +0 -0
- {ok-script-0.0.12/ok/gui/loading → ok-script-0.0.13/ok/gui/icon}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/icon/icon.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/overlay/OverlayWindow.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/overlay/__init__.py +0 -0
- {ok-script-0.0.12/ok/gui/tasks → ok-script-0.0.13/ok/gui/qss}/__init__.py +0 -0
- {ok-script-0.0.12/ok/gui/util → ok-script-0.0.13/ok/gui/settings}/__init__.py +0 -0
- {ok-script-0.0.12/ok/gui/widget → ok-script-0.0.13/ok/gui/start}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/tasks/StartButton.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/tasks/TaskOpButton.py +0 -0
- {ok-script-0.0.12/ok/interaction → ok-script-0.0.13/ok/gui/tasks}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/util/Alert.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/util/InitWorker.py +0 -0
- {ok-script-0.0.12/ok/logging → ok-script-0.0.13/ok/gui/util}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/widget/ListTableWidgetItem.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/widget/NumericTableWidgetItem.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/widget/TabWidget.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/widget/UpdateConfigWidgetItem.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/gui/widget/YesNonWidgetItem.py +0 -0
- {ok-script-0.0.12/ok/ocr → ok-script-0.0.13/ok/gui/widget}/__init__.py +0 -0
- {ok-script-0.0.12/ok/predict → ok-script-0.0.13/ok/interaction}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/logging/Logger.py +0 -0
- {ok-script-0.0.12/ok/rotypes/Windows/Media → ok-script-0.0.13/ok/logging}/__init__.py +0 -0
- {ok-script-0.0.12/ok/rotypes/Windows/Security → ok-script-0.0.13/ok/ocr}/__init__.py +0 -0
- {ok-script-0.0.12/ok/rotypes/Windows/Storage → ok-script-0.0.13/ok/predict}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/predict/predict.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
- {ok-script-0.0.12/ok/rotypes/Windows → ok-script-0.0.13/ok/rotypes/Windows/Media}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
- {ok-script-0.0.12/ok/save → ok-script-0.0.13/ok/rotypes/Windows/Security}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
- {ok-script-0.0.12/ok/scene → ok-script-0.0.13/ok/rotypes/Windows/Storage}/__init__.py +0 -0
- {ok-script-0.0.12/ok/stats → ok-script-0.0.13/ok/rotypes/Windows}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/delegate.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/export.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/idldsl.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/inspectable.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/roapi.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/types.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/rotypes/winstring.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/save/BlackBarProcessor.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/save/PostProcessor.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/save/SaveByInterval.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/save/SaveByKeyPress.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/save/SaveMethodBase.py +0 -0
- {ok-script-0.0.12/ok/task → ok-script-0.0.13/ok/save}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/scene/FeatureScene.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/scene/Scene.py +0 -0
- {ok-script-0.0.12/ok/util → ok-script-0.0.13/ok/scene}/__init__.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/stats/StreamStats.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/util/json.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/util/list.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/util/path.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/util/thread.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/util/win32_process.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok/util/yaml.py +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok_script.egg-info/dependency_links.txt +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/ok_script.egg-info/top_level.txt +0 -0
- {ok-script-0.0.12 → ok-script-0.0.13}/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.13
|
|
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
|
|
@@ -17,6 +17,7 @@ Requires-Dist: PyDirectInput~=1.0.4
|
|
|
17
17
|
Requires-Dist: pywin32==306
|
|
18
18
|
Requires-Dist: typing-extensions~=4.10.0
|
|
19
19
|
Requires-Dist: PySide6~=6.6.2
|
|
20
|
+
Requires-Dist: PySide6-Fluent-Widgets~=1.5.5
|
|
20
21
|
|
|
21
22
|
# ok-script
|
|
22
23
|
|
|
@@ -8,7 +8,6 @@ from PySide6.QtWidgets import QApplication
|
|
|
8
8
|
import ok
|
|
9
9
|
from ok.logging.Logger import get_logger, config_logger
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
logger = get_logger(__name__)
|
|
13
12
|
|
|
14
13
|
|
|
@@ -44,10 +43,8 @@ class OK:
|
|
|
44
43
|
|
|
45
44
|
def start(self):
|
|
46
45
|
if self.config.get("use_gui"):
|
|
47
|
-
self.
|
|
48
|
-
|
|
49
|
-
self.worker = InitWorker(self.do_init)
|
|
50
|
-
self.worker.start()
|
|
46
|
+
self.do_init()
|
|
47
|
+
self.app.show_main_window()
|
|
51
48
|
self.app.exec()
|
|
52
49
|
else:
|
|
53
50
|
self.task_executor.start()
|
|
@@ -102,7 +99,9 @@ class OK:
|
|
|
102
99
|
self.init_message("TaskExecutor init Start")
|
|
103
100
|
from ok.task.TaskExecutor import TaskExecutor
|
|
104
101
|
self.task_executor = TaskExecutor(self.device_manager, exit_event=self.exit_event,
|
|
105
|
-
|
|
102
|
+
onetime_tasks=self.config.get('onetime_tasks', []),
|
|
103
|
+
trigger_tasks=self.config.get('trigger_tasks', []),
|
|
104
|
+
scenes=self.config['scenes'],
|
|
106
105
|
feature_set=self.feature_set,
|
|
107
106
|
ocr=self.ocr, config_folder=self.config.get("config_folder") or "config")
|
|
108
107
|
self.init_message("TaskExecutor init Done")
|
|
@@ -118,6 +117,8 @@ class OK:
|
|
|
118
117
|
if self.device_manager is None:
|
|
119
118
|
from ok.capture.adb.DeviceManager import DeviceManager
|
|
120
119
|
self.device_manager = DeviceManager(self.config.get("config_folder") or "config",
|
|
121
|
-
self.config.get('capture_window_title'),
|
|
120
|
+
self.config.get('capture_window_title'),
|
|
121
|
+
self.config.get('capture_window_exe_name'),
|
|
122
|
+
self.config.get("debug"),
|
|
122
123
|
self.exit_event)
|
|
123
124
|
ok.gui.device_manager = self.device_manager
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# original https://github.com/dantmnf & https://github.com/hakaboom/winAuto
|
|
2
|
+
import ctypes
|
|
3
|
+
import re
|
|
4
|
+
import threading
|
|
5
|
+
|
|
6
|
+
import win32process
|
|
7
|
+
from win32 import win32gui
|
|
8
|
+
|
|
9
|
+
from ok.capture.windows.window import is_foreground_window, get_window_bounds
|
|
10
|
+
from ok.gui.Communicate import communicate
|
|
11
|
+
from ok.logging.Logger import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HwndWindow:
|
|
17
|
+
visible = True
|
|
18
|
+
x = 0
|
|
19
|
+
y = 0
|
|
20
|
+
width = 0
|
|
21
|
+
height = 0
|
|
22
|
+
title_height = 0
|
|
23
|
+
border = 0
|
|
24
|
+
scaling = 1
|
|
25
|
+
top_cut = 0
|
|
26
|
+
right_cut = 0
|
|
27
|
+
bottom_cut = 0
|
|
28
|
+
left_cut = 0
|
|
29
|
+
window_change_listeners = []
|
|
30
|
+
frame_aspect_ratio = 0
|
|
31
|
+
hwnd = None
|
|
32
|
+
frame_width = 0
|
|
33
|
+
frame_height = 0
|
|
34
|
+
exists = False
|
|
35
|
+
title = None
|
|
36
|
+
|
|
37
|
+
def __init__(self, title="", exe_name=None, exit_event=threading.Event(), frame_width=0, frame_height=0):
|
|
38
|
+
super().__init__()
|
|
39
|
+
self.app_exit_event = exit_event
|
|
40
|
+
self.exe_name = exe_name
|
|
41
|
+
self.stop_event = threading.Event()
|
|
42
|
+
self.update_title_re(title)
|
|
43
|
+
self.visible = False
|
|
44
|
+
self.update_frame_size(frame_width, frame_height)
|
|
45
|
+
self.do_update_window_size()
|
|
46
|
+
self.thread = threading.Thread(target=self.update_window_size)
|
|
47
|
+
self.thread.start()
|
|
48
|
+
|
|
49
|
+
def update_title_and_exe(self, title, exe):
|
|
50
|
+
self.update_title_re(title)
|
|
51
|
+
self.exe_name = exe
|
|
52
|
+
self.hwnd = None
|
|
53
|
+
self.visible = False
|
|
54
|
+
self.exists = False
|
|
55
|
+
self.width = 0
|
|
56
|
+
self.height = 0
|
|
57
|
+
self.do_update_window_size()
|
|
58
|
+
|
|
59
|
+
def stop(self):
|
|
60
|
+
self.stop_event.set()
|
|
61
|
+
|
|
62
|
+
def update_title_re(self, title):
|
|
63
|
+
if not title:
|
|
64
|
+
self.title = None
|
|
65
|
+
else:
|
|
66
|
+
self.title = re.compile(title)
|
|
67
|
+
|
|
68
|
+
def update_frame_size(self, width, height):
|
|
69
|
+
logger.debug(f"update_frame_size:{self.frame_width}x{self.frame_height} to {width}x{height}")
|
|
70
|
+
if width != self.frame_width or height != self.frame_height:
|
|
71
|
+
self.frame_width = width
|
|
72
|
+
self.frame_height = height
|
|
73
|
+
if width > 0 and height > 0:
|
|
74
|
+
self.frame_aspect_ratio = width / height
|
|
75
|
+
logger.debug(f"HwndWindow: frame ratio:{self.frame_aspect_ratio} width: {width}, height: {height}")
|
|
76
|
+
|
|
77
|
+
def update_window_size(self):
|
|
78
|
+
while not self.app_exit_event.is_set() and not self.stop_event.is_set():
|
|
79
|
+
self.do_update_window_size()
|
|
80
|
+
self.app_exit_event.wait(0.1)
|
|
81
|
+
|
|
82
|
+
def get_abs_cords(self, x, y):
|
|
83
|
+
return int(self.x * self.scaling + (self.border * self.scaling + x)), int(
|
|
84
|
+
self.y * self.scaling + (y + self.title_height * self.scaling))
|
|
85
|
+
|
|
86
|
+
def get_top_left_frame_offset(self):
|
|
87
|
+
return int(self.border * self.scaling), int(
|
|
88
|
+
self.title_height * self.scaling)
|
|
89
|
+
|
|
90
|
+
def do_update_window_size(self):
|
|
91
|
+
try:
|
|
92
|
+
visible, x, y, border, title_height, width, height, scaling = self.visible, self.x, self.y, self.border, self.title_height, self.width, self.height, self.scaling
|
|
93
|
+
if self.hwnd is None:
|
|
94
|
+
self.hwnd = find_hwnd_by_title_and_exe(self.title, self.exe_name)
|
|
95
|
+
self.exists = self.hwnd is not None
|
|
96
|
+
if self.hwnd is not None:
|
|
97
|
+
self.exists = win32gui.IsWindow(self.hwnd)
|
|
98
|
+
if self.exists:
|
|
99
|
+
visible = is_foreground_window(self.hwnd)
|
|
100
|
+
x, y, border, title_height, width, height, scaling = get_window_bounds(
|
|
101
|
+
self.hwnd)
|
|
102
|
+
if self.frame_aspect_ratio != 0:
|
|
103
|
+
window_ratio = width / height
|
|
104
|
+
if window_ratio < self.frame_aspect_ratio:
|
|
105
|
+
cropped_window_height = int(width / self.frame_aspect_ratio)
|
|
106
|
+
title_height += height - cropped_window_height
|
|
107
|
+
height = cropped_window_height
|
|
108
|
+
height = height
|
|
109
|
+
width = width
|
|
110
|
+
title_height = title_height
|
|
111
|
+
else:
|
|
112
|
+
self.hwnd = None
|
|
113
|
+
changed = False
|
|
114
|
+
if visible != self.visible or self.scaling != scaling:
|
|
115
|
+
self.visible = visible
|
|
116
|
+
self.scaling = scaling
|
|
117
|
+
changed = True
|
|
118
|
+
if (
|
|
119
|
+
x != self.x or y != self.y or border != self.border or title_height != self.title_height or width != self.width or height != self.height or scaling != self.scaling) and (
|
|
120
|
+
(x >= 0 and y >= 0) or self.visible):
|
|
121
|
+
self.x, self.y, self.border, self.title_height, self.width, self.height = x, y, border, title_height, width, height
|
|
122
|
+
changed = True
|
|
123
|
+
if changed:
|
|
124
|
+
logger.debug(
|
|
125
|
+
f"do_update_window_size changed: {self.visible} {self.x} {self.y} {self.border} {self.width} {self.height} {self.scaling}")
|
|
126
|
+
communicate.window.emit(self.visible, self.x, self.y, self.border, self.title_height, self.width,
|
|
127
|
+
self.height, self.scaling)
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.error(f"do_update_window_size exception", e)
|
|
130
|
+
|
|
131
|
+
def frame_ratio(self, size):
|
|
132
|
+
if self.frame_width > 0 and self.width > 0:
|
|
133
|
+
return int(size / self.frame_width * self.width)
|
|
134
|
+
else:
|
|
135
|
+
return size
|
|
136
|
+
|
|
137
|
+
def title_text(self):
|
|
138
|
+
if self.hwnd:
|
|
139
|
+
return win32gui.GetWindowText(self.hwnd)
|
|
140
|
+
return ""
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def find_hwnd_by_title_and_exe(title, exe):
|
|
144
|
+
if not title and not exe:
|
|
145
|
+
return None
|
|
146
|
+
hwnds = find_hwnds_by_title(title)
|
|
147
|
+
if exe is not None:
|
|
148
|
+
for hwnd in hwnds:
|
|
149
|
+
exe_2 = get_exe_name_by_hwnd(hwnd)
|
|
150
|
+
if exe_2 == exe:
|
|
151
|
+
return hwnd
|
|
152
|
+
if len(hwnds) > 0:
|
|
153
|
+
return hwnds[0]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def find_hwnds_by_title(title):
|
|
157
|
+
hwnds = []
|
|
158
|
+
if not title:
|
|
159
|
+
return hwnds
|
|
160
|
+
|
|
161
|
+
def enum_windows_proc(hwnd, lParam):
|
|
162
|
+
text = win32gui.GetWindowText(hwnd)
|
|
163
|
+
if text and win32gui.IsWindowVisible(hwnd):
|
|
164
|
+
if isinstance(title, str) and title == text:
|
|
165
|
+
hwnds.append(hwnd)
|
|
166
|
+
elif re.search(title, text):
|
|
167
|
+
hwnds.append(hwnd)
|
|
168
|
+
|
|
169
|
+
win32gui.EnumWindows(enum_windows_proc, None)
|
|
170
|
+
if len(hwnds) > 0:
|
|
171
|
+
if len(hwnds) > 1:
|
|
172
|
+
logger.warning(f"Found multiple hwnds {len(hwnds)}")
|
|
173
|
+
return hwnds
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
OpenProcess = ctypes.windll.kernel32.OpenProcess
|
|
177
|
+
CloseHandle = ctypes.windll.kernel32.CloseHandle
|
|
178
|
+
GetModuleFileNameExW = ctypes.windll.psapi.GetModuleFileNameExW
|
|
179
|
+
|
|
180
|
+
PROCESS_QUERY_INFORMATION = 0x0400
|
|
181
|
+
PROCESS_VM_READ = 0x0010
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def get_exe_name_by_hwnd(hwnd):
|
|
185
|
+
# Get the process ID of the window
|
|
186
|
+
_, pid = win32process.GetWindowThreadProcessId(hwnd)
|
|
187
|
+
|
|
188
|
+
# Open the process
|
|
189
|
+
h_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid)
|
|
190
|
+
if h_process:
|
|
191
|
+
# Get the executable name
|
|
192
|
+
exe_name = ctypes.create_unicode_buffer(1024)
|
|
193
|
+
GetModuleFileNameExW(h_process, None, exe_name, 1024)
|
|
194
|
+
|
|
195
|
+
# Close the process handle
|
|
196
|
+
CloseHandle(h_process)
|
|
197
|
+
|
|
198
|
+
return exe_name.value
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# original https://github.com/Toufool/AutoSplit/blob/master/src/capture_method/WindowsGraphicsCaptureMethod.py
|
|
2
2
|
import cv2
|
|
3
3
|
import numpy as np
|
|
4
|
+
from adbutils import AdbError
|
|
4
5
|
from typing_extensions import override
|
|
5
6
|
|
|
6
7
|
from ok.capture.BaseCaptureMethod import BaseCaptureMethod
|
|
@@ -17,6 +18,7 @@ class ADBCaptureMethod(BaseCaptureMethod):
|
|
|
17
18
|
super().__init__()
|
|
18
19
|
self.exit_event = exit_event
|
|
19
20
|
self._size = (width, height)
|
|
21
|
+
self._connected = (width != 0 and height != 0)
|
|
20
22
|
self.device = device
|
|
21
23
|
|
|
22
24
|
@property
|
|
@@ -41,17 +43,26 @@ class ADBCaptureMethod(BaseCaptureMethod):
|
|
|
41
43
|
frame = do_screencap(self.device)
|
|
42
44
|
if frame is not None:
|
|
43
45
|
height, width, _ = frame.shape
|
|
46
|
+
self._connected = True
|
|
44
47
|
self._size = (width, height)
|
|
48
|
+
else:
|
|
49
|
+
self._connected = False
|
|
45
50
|
return frame
|
|
46
51
|
|
|
52
|
+
def connected(self):
|
|
53
|
+
return self._connected and self.device is not None
|
|
54
|
+
|
|
47
55
|
|
|
48
56
|
def do_screencap(device) -> np.ndarray | None:
|
|
49
57
|
if device is not None:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
try:
|
|
59
|
+
png_bytes = device.shell("screencap -p", encoding=None)
|
|
60
|
+
if png_bytes is not None and len(png_bytes) > 0:
|
|
61
|
+
image_data = np.frombuffer(png_bytes, dtype=np.uint8)
|
|
62
|
+
image = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
|
|
63
|
+
if image is not None:
|
|
64
|
+
return image
|
|
65
|
+
else:
|
|
66
|
+
logger.error(f"Screencap image decode error, probably disconnected")
|
|
67
|
+
except AdbError as e:
|
|
68
|
+
logger.error(f"Device {device} error", e)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import threading
|
|
2
2
|
|
|
3
3
|
import adbutils
|
|
4
|
+
import win32gui
|
|
4
5
|
|
|
5
|
-
from ok.capture.HwndWindow import HwndWindow
|
|
6
|
+
from ok.capture.HwndWindow import HwndWindow, find_hwnd_by_title_and_exe
|
|
6
7
|
from ok.capture.adb.ADBCaptureMethod import ADBCaptureMethod, do_screencap
|
|
7
8
|
from ok.capture.adb.vbox import installed_emulator
|
|
8
9
|
from ok.capture.windows.WindowsGraphicsCaptureMethod import WindowsGraphicsCaptureMethod
|
|
10
|
+
from ok.capture.windows.window import get_window_bounds
|
|
9
11
|
from ok.config.Config import Config
|
|
10
12
|
from ok.gui.Communicate import communicate
|
|
11
13
|
from ok.interaction.ADBInteraction import ADBBaseInteraction
|
|
@@ -17,18 +19,22 @@ logger = get_logger(__name__)
|
|
|
17
19
|
|
|
18
20
|
class DeviceManager:
|
|
19
21
|
|
|
20
|
-
def __init__(self, config_folder, hwnd_title=None, debug=False, exit_event=None):
|
|
22
|
+
def __init__(self, config_folder, hwnd_title=None, exe_name=None, debug=False, exit_event=None):
|
|
21
23
|
self._device = None
|
|
22
|
-
self.
|
|
24
|
+
self._connect_all = False
|
|
25
|
+
self.adb = self.adb = adbutils.AdbClient(host="127.0.0.1")
|
|
26
|
+
logger.debug(f'connect adb')
|
|
23
27
|
self.hwnd_title = hwnd_title
|
|
28
|
+
self.exe_name = exe_name
|
|
24
29
|
self.debug = debug
|
|
25
30
|
self.interaction = None
|
|
26
31
|
self.exit_event = exit_event
|
|
27
32
|
if hwnd_title is not None:
|
|
28
|
-
self.hwnd = HwndWindow(hwnd_title, exit_event)
|
|
29
|
-
self.config = Config({"devices": {}}, config_folder, "DeviceManager")
|
|
33
|
+
self.hwnd = HwndWindow(hwnd_title, exe_name, exit_event)
|
|
34
|
+
self.config = Config({"devices": {}, "preferred": "none"}, config_folder, "DeviceManager")
|
|
30
35
|
self.thread = None
|
|
31
36
|
self.capture_method = None
|
|
37
|
+
self.start()
|
|
32
38
|
|
|
33
39
|
def refresh(self):
|
|
34
40
|
if self.thread is None or not self.thread.is_alive():
|
|
@@ -40,18 +46,40 @@ class DeviceManager:
|
|
|
40
46
|
return self.config.get("devices")
|
|
41
47
|
|
|
42
48
|
def connect_all(self):
|
|
43
|
-
|
|
44
|
-
self.
|
|
45
|
-
|
|
49
|
+
if not self._connect_all:
|
|
50
|
+
for device in self.device_dict.values():
|
|
51
|
+
if device["device"] == "adb":
|
|
52
|
+
self.adb.connect(device['address'])
|
|
53
|
+
self._connect_all = True
|
|
54
|
+
logger.debug(f'connect_all {self.adb.device_list()}')
|
|
46
55
|
|
|
47
56
|
def get_devices(self):
|
|
48
|
-
return self.device_dict.values()
|
|
57
|
+
return list(self.device_dict.values())
|
|
49
58
|
|
|
50
59
|
def do_refresh(self):
|
|
51
|
-
if self.adb is None:
|
|
52
|
-
self.adb = adbutils.AdbClient(host="127.0.0.1")
|
|
53
|
-
logger.debug(f'connect adb')
|
|
54
60
|
self.connect_all()
|
|
61
|
+
|
|
62
|
+
if self.hwnd_title:
|
|
63
|
+
nick = ""
|
|
64
|
+
width = 0
|
|
65
|
+
height = 0
|
|
66
|
+
hwnd = find_hwnd_by_title_and_exe(self.hwnd_title, self.exe_name)
|
|
67
|
+
if hwnd is not None:
|
|
68
|
+
nick = win32gui.GetWindowText(hwnd)
|
|
69
|
+
_, _, _, _, width, height, _ = get_window_bounds(
|
|
70
|
+
hwnd)
|
|
71
|
+
if not nick:
|
|
72
|
+
nick = self.exe_name
|
|
73
|
+
self.device_dict[self.hwnd_title] = {"address": "", "imei": self.hwnd_title, "device": "windows",
|
|
74
|
+
"model": "", "nick": nick, "width": width,
|
|
75
|
+
"height": height,
|
|
76
|
+
"hwnd": nick, "capture": "windows",
|
|
77
|
+
"connected": hwnd is not None
|
|
78
|
+
}
|
|
79
|
+
if width != 0:
|
|
80
|
+
self.device_dict[self.hwnd_title]["resolution"] = f"{width}x{height}"
|
|
81
|
+
communicate.adb_devices.emit(False)
|
|
82
|
+
|
|
55
83
|
installed_emulators = installed_emulator()
|
|
56
84
|
for device in self.adb.list():
|
|
57
85
|
logger.debug(f'adb.list() {device}')
|
|
@@ -75,7 +103,7 @@ class DeviceManager:
|
|
|
75
103
|
height, width, _ = frame.shape
|
|
76
104
|
adb_device = {"address": device.serial, "imei": imei, "device": "adb", "capture": "adb", "width": width,
|
|
77
105
|
"height": height,
|
|
78
|
-
"model": device.prop.model, "nick": device.prop.model, "connected": True,
|
|
106
|
+
"model": device.prop.model, "nick": device.prop.model, "connected": True,
|
|
79
107
|
"resolution": f"{width}x{height}"}
|
|
80
108
|
found = False
|
|
81
109
|
for emulator in installed_emulators:
|
|
@@ -95,24 +123,12 @@ class DeviceManager:
|
|
|
95
123
|
self.device_dict[imei] = adb_device
|
|
96
124
|
adb_connected.append(imei)
|
|
97
125
|
for imei in self.device_dict:
|
|
98
|
-
if imei not in adb_connected:
|
|
126
|
+
if imei not in adb_connected and self.device_dict[imei]['device'] == 'adb':
|
|
99
127
|
self.device_dict[imei]['connected'] = False
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
width = 0
|
|
103
|
-
height = 0
|
|
104
|
-
if self.hwnd:
|
|
105
|
-
nick = self.hwnd.title_text()
|
|
106
|
-
width = self.hwnd.width
|
|
107
|
-
height = self.hwnd.height
|
|
108
|
-
self.device_dict[self.hwnd_title] = {"address": "", "imei": self.hwnd_title, "device": "windows",
|
|
109
|
-
"model": "", "nick": nick or self.hwnd_title, "width": width,
|
|
110
|
-
"height": height,
|
|
111
|
-
"hwnd": nick, "capture": "windows",
|
|
112
|
-
"connected": self.hwnd is not None and self.hwnd.exists,
|
|
113
|
-
"resolution": f"{width}x{height}"}
|
|
114
|
-
communicate.adb_devices.emit()
|
|
128
|
+
|
|
129
|
+
communicate.adb_devices.emit(True)
|
|
115
130
|
self.config.save_file()
|
|
131
|
+
self.start()
|
|
116
132
|
logger.debug(f'refresh {self.device_dict}')
|
|
117
133
|
|
|
118
134
|
def set_preferred_device(self, imei=None):
|
|
@@ -127,12 +143,11 @@ class DeviceManager:
|
|
|
127
143
|
self.config["preferred"] = imei
|
|
128
144
|
self.config.save_file()
|
|
129
145
|
logger.debug(f'preferred device: {preferred}')
|
|
146
|
+
self.start()
|
|
130
147
|
|
|
131
148
|
def get_preferred_device(self):
|
|
132
149
|
imei = self.config.get("preferred")
|
|
133
150
|
preferred = self.device_dict.get(imei)
|
|
134
|
-
if preferred is None:
|
|
135
|
-
raise ValueError(f"preferred device {imei} is none")
|
|
136
151
|
return preferred
|
|
137
152
|
|
|
138
153
|
def set_hwnd_name(self, hwnd_name):
|
|
@@ -140,19 +155,32 @@ class DeviceManager:
|
|
|
140
155
|
if preferred.get("hwnd") != hwnd_name:
|
|
141
156
|
preferred['hwnd'] = hwnd_name
|
|
142
157
|
self.config.save_file()
|
|
158
|
+
self.start()
|
|
143
159
|
|
|
144
160
|
def set_capture(self, capture):
|
|
145
161
|
preferred = self.get_preferred_device()
|
|
146
|
-
preferred
|
|
147
|
-
|
|
162
|
+
if preferred.get("capture") != capture:
|
|
163
|
+
preferred['capture'] = capture
|
|
164
|
+
self.config.save_file()
|
|
165
|
+
self.start()
|
|
148
166
|
|
|
149
167
|
def get_hwnd_name(self):
|
|
150
168
|
preferred = self.get_preferred_device()
|
|
151
169
|
return preferred.get('hwnd')
|
|
152
170
|
|
|
171
|
+
def ensure_hwnd(self, title, exe, frame_width=0, frame_height=0):
|
|
172
|
+
if self.hwnd is None:
|
|
173
|
+
self.hwnd = HwndWindow(title, exe, self.exit_event, frame_width, frame_height)
|
|
174
|
+
else:
|
|
175
|
+
self.hwnd.update_frame_size(frame_width, frame_height)
|
|
176
|
+
self.hwnd.update_title_and_exe(title, exe)
|
|
177
|
+
|
|
153
178
|
def start(self):
|
|
154
179
|
preferred = self.get_preferred_device()
|
|
180
|
+
if preferred is None:
|
|
181
|
+
return
|
|
155
182
|
if preferred['device'] == 'windows':
|
|
183
|
+
self.ensure_hwnd(self.hwnd_title, self.exe_name)
|
|
156
184
|
if not isinstance(self.capture_method, WindowsGraphicsCaptureMethod):
|
|
157
185
|
if self.capture_method is not None:
|
|
158
186
|
self.capture_method.close()
|
|
@@ -160,39 +188,32 @@ class DeviceManager:
|
|
|
160
188
|
self.interaction = Win32Interaction(self.capture_method)
|
|
161
189
|
self.capture_method.hwnd_window = self.hwnd
|
|
162
190
|
else:
|
|
191
|
+
self.connect_all()
|
|
163
192
|
for adb_device in self.adb.device_list():
|
|
164
193
|
if adb_device.serial == preferred.get('address'):
|
|
194
|
+
logger.debug(f"set device {adb_device}")
|
|
165
195
|
self._device = adb_device
|
|
166
196
|
hwnd_name = preferred.get('hwnd')
|
|
167
197
|
width = preferred.get('width', 0)
|
|
168
198
|
height = preferred.get('height', 0)
|
|
169
199
|
if preferred.get('capture') == "windows":
|
|
170
|
-
|
|
171
|
-
raise ValueError(f"adb device preferred hwnd is none, capture is windows {preferred}")
|
|
172
|
-
if self.hwnd is None:
|
|
173
|
-
self.hwnd = HwndWindow(hwnd_name, self.exit_event, width, height)
|
|
200
|
+
self.ensure_hwnd(hwnd_name, None, width, height)
|
|
174
201
|
if not isinstance(self.capture_method, WindowsGraphicsCaptureMethod):
|
|
175
202
|
if self.capture_method is not None:
|
|
176
203
|
self.capture_method.close()
|
|
177
204
|
self.capture_method = WindowsGraphicsCaptureMethod(self.hwnd)
|
|
178
|
-
self.hwnd.title = hwnd_name
|
|
179
205
|
self.capture_method.hwnd_window = self.hwnd
|
|
180
206
|
elif not isinstance(self.capture_method, ADBCaptureMethod):
|
|
181
207
|
if self.capture_method is not None:
|
|
182
208
|
self.capture_method.close()
|
|
183
209
|
self.capture_method = ADBCaptureMethod(self._device, self.exit_event, width=width,
|
|
184
210
|
height=height)
|
|
185
|
-
self.interaction = ADBBaseInteraction(self, self.capture_method)
|
|
186
|
-
if self.debug:
|
|
187
|
-
|
|
188
|
-
if self.hwnd is None:
|
|
189
|
-
self.hwnd = HwndWindow(hwnd_name, self.exit_event, width, height)
|
|
190
|
-
self.hwnd.title = hwnd_name
|
|
211
|
+
self.interaction = ADBBaseInteraction(self, self.capture_method, width, height)
|
|
212
|
+
if self.debug and hwnd_name:
|
|
213
|
+
self.ensure_hwnd(hwnd_name, None, width, height)
|
|
191
214
|
elif self.hwnd is not None:
|
|
192
215
|
self.hwnd.stop()
|
|
193
216
|
self.hwnd = None
|
|
194
|
-
if self.hwnd:
|
|
195
|
-
self.hwnd.update_frame_size(width, height)
|
|
196
217
|
|
|
197
218
|
@property
|
|
198
219
|
def device(self):
|
|
@@ -85,15 +85,16 @@ def installed_emulator():
|
|
|
85
85
|
|
|
86
86
|
logger.debug(f'running VirtualBox servers: {running_servers}')
|
|
87
87
|
results = []
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
try:
|
|
89
|
+
for server in running_servers:
|
|
90
|
+
logger.debug(f'checking {server}')
|
|
91
|
+
hproc = OpenProcess(PROCESS_QUERY_INFORMATION, False, server.pid)
|
|
92
|
+
if not hproc:
|
|
93
|
+
logger.debug(f'try OpenProcess failed for PID {server.pid}')
|
|
94
|
+
continue
|
|
95
|
+
CloseHandle(hproc)
|
|
96
|
+
logger.debug(f'checking hproc end {server}')
|
|
97
|
+
|
|
97
98
|
client = win32com.client.Dispatch(server.clsid)
|
|
98
99
|
for machine in client.Machines:
|
|
99
100
|
machine_name = machine.Name
|
|
@@ -118,8 +119,8 @@ def installed_emulator():
|
|
|
118
119
|
address, 2, 1, override_identifier=identifier,
|
|
119
120
|
preload_device_info={'emulator_hypervisor': 'vbox'}))
|
|
120
121
|
logger.debug(f'try checking {server}')
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error(f'failed to check server :{server} {e}', e)
|
|
123
124
|
return results
|
|
124
125
|
|
|
125
126
|
|
|
@@ -37,6 +37,8 @@ class WindowsGraphicsCaptureMethod(BaseCaptureMethod):
|
|
|
37
37
|
item = None
|
|
38
38
|
session = None
|
|
39
39
|
cputex = None
|
|
40
|
+
rtdevice = None
|
|
41
|
+
dxdevice = None
|
|
40
42
|
|
|
41
43
|
def __init__(self, hwnd_window: HwndWindow):
|
|
42
44
|
super().__init__()
|
|
@@ -105,6 +107,9 @@ class WindowsGraphicsCaptureMethod(BaseCaptureMethod):
|
|
|
105
107
|
return self.get_frame()
|
|
106
108
|
self.last_frame = img
|
|
107
109
|
|
|
110
|
+
def connected(self):
|
|
111
|
+
return self.hwnd_window is not None and self.hwnd_window.exists
|
|
112
|
+
|
|
108
113
|
def start_or_stop(self, capture_cursor=False):
|
|
109
114
|
if self.hwnd_window.exists and self.frame_pool is None:
|
|
110
115
|
logger.info('init windows capture')
|
|
@@ -205,7 +210,7 @@ class WindowsGraphicsCaptureMethod(BaseCaptureMethod):
|
|
|
205
210
|
return self.hwnd_window.get_abs_cords(x, y)
|
|
206
211
|
|
|
207
212
|
def clickable(self):
|
|
208
|
-
return self.hwnd_window.visible
|
|
213
|
+
return self.hwnd_window is not None and self.hwnd_window.visible
|
|
209
214
|
|
|
210
215
|
|
|
211
216
|
def crop_image(image, border, title_height):
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import ctypes
|
|
3
|
-
import ctypes.wintypes
|
|
4
2
|
import os
|
|
5
3
|
import sys
|
|
6
4
|
from collections.abc import Callable, Iterable
|
|
@@ -83,23 +81,6 @@ def try_delete_dc(dc: "PyCDC"):
|
|
|
83
81
|
pass
|
|
84
82
|
|
|
85
83
|
|
|
86
|
-
def get_window_bounds(hwnd: int) -> tuple[int, int, int, int]:
|
|
87
|
-
extended_frame_bounds = ctypes.wintypes.RECT()
|
|
88
|
-
ctypes.windll.dwmapi.DwmGetWindowAttribute(
|
|
89
|
-
hwnd,
|
|
90
|
-
DWMWA_EXTENDED_FRAME_BOUNDS,
|
|
91
|
-
ctypes.byref(extended_frame_bounds),
|
|
92
|
-
ctypes.sizeof(extended_frame_bounds),
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
window_rect = win32gui.GetWindowRect(hwnd)
|
|
96
|
-
window_left_bounds = extended_frame_bounds.left - window_rect[0]
|
|
97
|
-
window_top_bounds = extended_frame_bounds.top - window_rect[1]
|
|
98
|
-
window_width = extended_frame_bounds.right - extended_frame_bounds.left
|
|
99
|
-
window_height = extended_frame_bounds.bottom - extended_frame_bounds.top
|
|
100
|
-
return window_left_bounds, window_top_bounds, window_width, window_height
|
|
101
|
-
|
|
102
|
-
|
|
103
84
|
def open_file(file_path: str | bytes | os.PathLike[str] | os.PathLike[bytes]):
|
|
104
85
|
os.startfile(file_path) # noqa: S606
|
|
105
86
|
|
|
@@ -5,6 +5,7 @@ from ok.util.path import get_path_relative_to_exe
|
|
|
5
5
|
class Config:
|
|
6
6
|
def __init__(self, default, folder, name):
|
|
7
7
|
super().__init__()
|
|
8
|
+
self.default = default
|
|
8
9
|
self.config_file = get_path_relative_to_exe(folder, f"{name}.json")
|
|
9
10
|
self.config = read_json_file(self.config_file)
|
|
10
11
|
if self.config is None:
|
|
@@ -15,6 +16,11 @@ class Config:
|
|
|
15
16
|
def save_file(self):
|
|
16
17
|
write_json_file(self.config_file, self.config)
|
|
17
18
|
|
|
19
|
+
def reset_to_default(self):
|
|
20
|
+
self.config.clear()
|
|
21
|
+
self.update(self.default)
|
|
22
|
+
self.save_file()
|
|
23
|
+
|
|
18
24
|
def update(self, *args, **kwargs):
|
|
19
25
|
self.config.update(*args, **kwargs)
|
|
20
26
|
self.save_file()
|