ok-script 0.0.43__tar.gz → 0.0.45__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 (163) hide show
  1. {ok-script-0.0.43 → ok-script-0.0.45}/PKG-INFO +11 -9
  2. {ok-script-0.0.43 → ok-script-0.0.45}/ok/OK.py +2 -2
  3. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/BaseCaptureMethod.py +18 -1
  4. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/HwndWindow.py +29 -51
  5. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/adb/ADBCaptureMethod.py +5 -17
  6. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/adb/DeviceManager.py +74 -46
  7. ok-script-0.0.45/ok/capture/adb/WindowsCaptureFactory.py +39 -0
  8. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/adb/vbox.py +0 -18
  9. ok-script-0.0.45/ok/capture/windows/BaseWindowsCaptureMethod.py +36 -0
  10. ok-script-0.0.45/ok/capture/windows/BitBltCaptureMethod.py +97 -0
  11. ok-script-0.0.45/ok/capture/windows/DesktopDuplicationCaptureMethod.py +64 -0
  12. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/windows/WindowsGraphicsCaptureMethod.py +45 -41
  13. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/windows/window.py +6 -6
  14. {ok-script-0.0.43 → ok-script-0.0.45}/ok/config/Config.py +1 -1
  15. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/overlay/OverlayWindow.py +1 -1
  16. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/resources.py +394 -358
  17. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/start/StartCard.py +67 -11
  18. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/start/StartTab.py +2 -0
  19. {ok-script-0.0.43 → ok-script-0.0.45}/ok/logging/Logger.py +6 -4
  20. {ok-script-0.0.43 → ok-script-0.0.45}/ok/task/TaskExecutor.py +1 -1
  21. {ok-script-0.0.43 → ok-script-0.0.45}/ok_script.egg-info/PKG-INFO +11 -9
  22. {ok-script-0.0.43 → ok-script-0.0.45}/ok_script.egg-info/SOURCES.txt +4 -0
  23. ok-script-0.0.45/ok_script.egg-info/requires.txt +10 -0
  24. {ok-script-0.0.43 → ok-script-0.0.45}/setup.py +11 -9
  25. ok-script-0.0.43/ok_script.egg-info/requires.txt +0 -8
  26. {ok-script-0.0.43 → ok-script-0.0.45}/README.md +0 -0
  27. {ok-script-0.0.43 → ok-script-0.0.45}/ok/__init__.py +0 -0
  28. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/__init__.py +0 -0
  29. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/adb/__init__.py +0 -0
  30. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/adb/bin/__init__.py +0 -0
  31. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/adb/targets.py +0 -0
  32. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/windows/__init__.py +0 -0
  33. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/windows/d3d11.py +0 -0
  34. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/windows/dump.py +0 -0
  35. {ok-script-0.0.43 → ok-script-0.0.45}/ok/capture/windows/utils.py +0 -0
  36. {ok-script-0.0.43 → ok-script-0.0.45}/ok/color/Color.py +0 -0
  37. {ok-script-0.0.43 → ok-script-0.0.45}/ok/color/__init__.py +0 -0
  38. {ok-script-0.0.43 → ok-script-0.0.45}/ok/config/InfoDict.py +0 -0
  39. {ok-script-0.0.43 → ok-script-0.0.45}/ok/config/__init__.py +0 -0
  40. {ok-script-0.0.43 → ok-script-0.0.45}/ok/feature/Box.py +0 -0
  41. {ok-script-0.0.43 → ok-script-0.0.45}/ok/feature/Feature.py +0 -0
  42. {ok-script-0.0.43 → ok-script-0.0.45}/ok/feature/FeatureSet.py +0 -0
  43. {ok-script-0.0.43 → ok-script-0.0.45}/ok/feature/FindFeature.py +0 -0
  44. {ok-script-0.0.43 → ok-script-0.0.45}/ok/feature/__init__.py +0 -0
  45. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/App.py +0 -0
  46. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/Communicate.py +0 -0
  47. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/MainWindow.py +0 -0
  48. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/MessageWindow.py +0 -0
  49. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/TabTitles.py +0 -0
  50. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/__init__.py +0 -0
  51. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/about/AboutTab.py +0 -0
  52. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/about/__init__.py +0 -0
  53. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/common/OKIcon.py +0 -0
  54. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/common/__init__.py +0 -0
  55. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/common/config.py +0 -0
  56. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/common/style_sheet.py +0 -0
  57. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/debug/AspectRatioWidget.py +0 -0
  58. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/debug/DebugTab.py +0 -0
  59. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/debug/FrameWidget.py +0 -0
  60. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/debug/InfoWidget.py +0 -0
  61. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/debug/LoggerWidget.py +0 -0
  62. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/debug/Screenshot.py +0 -0
  63. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/debug/__init__.py +0 -0
  64. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/i18n/__init__.py +0 -0
  65. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/i18n/path.py +0 -0
  66. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/i18n/update.py +0 -0
  67. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/icon/__init__.py +0 -0
  68. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/icon/icon.py +0 -0
  69. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/overlay/__init__.py +0 -0
  70. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/qss/__init__.py +0 -0
  71. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/settings/SettingTab.py +0 -0
  72. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/settings/__init__.py +0 -0
  73. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/start/SelectCaptureListView.py +0 -0
  74. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/start/SelectHwndWindow.py +0 -0
  75. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/start/__init__.py +0 -0
  76. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/ConfigItemFactory.py +0 -0
  77. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/ConfigLabelAndWidget.py +0 -0
  78. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/LabelAndDoubleSpinBox.py +0 -0
  79. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/LabelAndDropDown.py +0 -0
  80. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/LabelAndLineEdit.py +0 -0
  81. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/LabelAndList.py +0 -0
  82. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/LabelAndSpinBox.py +0 -0
  83. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/LabelAndSwitchButton.py +0 -0
  84. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/LabelAndWidget.py +0 -0
  85. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/ModifyListDialog.py +0 -0
  86. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/ModifyListItem.py +0 -0
  87. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/OneTimeTaskTab.py +0 -0
  88. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/StartButton.py +0 -0
  89. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/TaskCard.py +0 -0
  90. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/TaskOpButton.py +0 -0
  91. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/TooltipTableWidget.py +0 -0
  92. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/TriggerTaskTab.py +0 -0
  93. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/tasks/__init__.py +0 -0
  94. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/util/Alert.py +0 -0
  95. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/util/InitWorker.py +0 -0
  96. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/util/__init__.py +0 -0
  97. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/Card.py +0 -0
  98. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/FramelessDialog.py +0 -0
  99. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/ListTableWidgetItem.py +0 -0
  100. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/NumericTableWidgetItem.py +0 -0
  101. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/StatusBar.py +0 -0
  102. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/Tab.py +0 -0
  103. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/TabWidget.py +0 -0
  104. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/UpdateConfigWidgetItem.py +0 -0
  105. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/YesNonWidgetItem.py +0 -0
  106. {ok-script-0.0.43 → ok-script-0.0.45}/ok/gui/widget/__init__.py +0 -0
  107. {ok-script-0.0.43 → ok-script-0.0.45}/ok/interaction/ADBInteraction.py +0 -0
  108. {ok-script-0.0.43 → ok-script-0.0.45}/ok/interaction/BaseInteraction.py +0 -0
  109. {ok-script-0.0.43 → ok-script-0.0.45}/ok/interaction/Win32Interaction.py +0 -0
  110. {ok-script-0.0.43 → ok-script-0.0.45}/ok/interaction/__init__.py +0 -0
  111. {ok-script-0.0.43 → ok-script-0.0.45}/ok/logging/__init__.py +0 -0
  112. {ok-script-0.0.43 → ok-script-0.0.45}/ok/ocr/OCR.py +0 -0
  113. {ok-script-0.0.43 → ok-script-0.0.45}/ok/ocr/__init__.py +0 -0
  114. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
  115. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
  116. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
  117. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
  118. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
  119. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
  120. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
  121. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
  122. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
  123. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Media/__init__.py +0 -0
  124. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
  125. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Security/__init__.py +0 -0
  126. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
  127. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/Storage/__init__.py +0 -0
  128. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/Windows/__init__.py +0 -0
  129. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/__init__.py +0 -0
  130. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/delegate.py +0 -0
  131. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/export.py +0 -0
  132. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/idldsl.py +0 -0
  133. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/inspectable.py +0 -0
  134. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/roapi.py +0 -0
  135. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/types.py +0 -0
  136. {ok-script-0.0.43 → ok-script-0.0.45}/ok/rotypes/winstring.py +0 -0
  137. {ok-script-0.0.43 → ok-script-0.0.45}/ok/save/BlackBarProcessor.py +0 -0
  138. {ok-script-0.0.43 → ok-script-0.0.45}/ok/save/PostProcessor.py +0 -0
  139. {ok-script-0.0.43 → ok-script-0.0.45}/ok/save/SaveByInterval.py +0 -0
  140. {ok-script-0.0.43 → ok-script-0.0.45}/ok/save/SaveByKeyPress.py +0 -0
  141. {ok-script-0.0.43 → ok-script-0.0.45}/ok/save/SaveMethodBase.py +0 -0
  142. {ok-script-0.0.43 → ok-script-0.0.45}/ok/save/__init__.py +0 -0
  143. {ok-script-0.0.43 → ok-script-0.0.45}/ok/scene/FeatureScene.py +0 -0
  144. {ok-script-0.0.43 → ok-script-0.0.45}/ok/scene/Scene.py +0 -0
  145. {ok-script-0.0.43 → ok-script-0.0.45}/ok/scene/__init__.py +0 -0
  146. {ok-script-0.0.43 → ok-script-0.0.45}/ok/stats/StreamStats.py +0 -0
  147. {ok-script-0.0.43 → ok-script-0.0.45}/ok/stats/__init__.py +0 -0
  148. {ok-script-0.0.43 → ok-script-0.0.45}/ok/task/BaseTask.py +0 -0
  149. {ok-script-0.0.43 → ok-script-0.0.45}/ok/task/ExecutorOperation.py +0 -0
  150. {ok-script-0.0.43 → ok-script-0.0.45}/ok/task/OneTimeTask.py +0 -0
  151. {ok-script-0.0.43 → ok-script-0.0.45}/ok/task/TriggerTask.py +0 -0
  152. {ok-script-0.0.43 → ok-script-0.0.45}/ok/task/__init__.py +0 -0
  153. {ok-script-0.0.43 → ok-script-0.0.45}/ok/util/__init__.py +0 -0
  154. {ok-script-0.0.43 → ok-script-0.0.45}/ok/util/exit_event.py +0 -0
  155. {ok-script-0.0.43 → ok-script-0.0.45}/ok/util/json.py +0 -0
  156. {ok-script-0.0.43 → ok-script-0.0.45}/ok/util/list.py +0 -0
  157. {ok-script-0.0.43 → ok-script-0.0.45}/ok/util/path.py +0 -0
  158. {ok-script-0.0.43 → ok-script-0.0.45}/ok/util/thread.py +0 -0
  159. {ok-script-0.0.43 → ok-script-0.0.45}/ok/util/win32_process.py +0 -0
  160. {ok-script-0.0.43 → ok-script-0.0.45}/ok/util/yaml.py +0 -0
  161. {ok-script-0.0.43 → ok-script-0.0.45}/ok_script.egg-info/dependency_links.txt +0 -0
  162. {ok-script-0.0.43 → ok-script-0.0.45}/ok_script.egg-info/top_level.txt +0 -0
  163. {ok-script-0.0.43 → ok-script-0.0.45}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ok-script
3
- Version: 0.0.43
3
+ Version: 0.0.45
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
@@ -11,14 +11,16 @@ Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: Microsoft :: Windows
12
12
  Requires-Python: >=3.6
13
13
  Description-Content-Type: text/markdown
14
- Requires-Dist: adbutils==2.7.1
15
- Requires-Dist: numpy==1.26.4
16
- Requires-Dist: PyDirectInput==1.0.4
17
- Requires-Dist: pywin32==306
18
- Requires-Dist: typing-extensions==4.11.0
19
- Requires-Dist: PySide6==6.7.0
20
- Requires-Dist: PySide6-Fluent-Widgets==1.5.6
21
- Requires-Dist: opencv-python==4.9.0.80
14
+ Requires-Dist: adbutils>=2.7.2
15
+ Requires-Dist: numpy>=1.26.4
16
+ Requires-Dist: PyDirectInput>=1.0.4
17
+ Requires-Dist: pywin32>=306
18
+ Requires-Dist: typing-extensions>=4.11.0
19
+ Requires-Dist: PySide6>=6.7.0
20
+ Requires-Dist: PySide6-Fluent-Widgets>=1.5.6
21
+ Requires-Dist: psutil>=5.9.8
22
+ Requires-Dist: opencv-python>=4.9.0.80
23
+ Requires-Dist: ok-d3dshot>=0.1.5
22
24
 
23
25
  # ok-script
24
26
 
@@ -132,8 +132,8 @@ class OK:
132
132
  if self.device_manager is None:
133
133
  from ok.capture.adb.DeviceManager import DeviceManager
134
134
  self.device_manager = DeviceManager(self.config.get("config_folder") or "config",
135
- self.config.get('capture_window_title'),
136
- self.config.get('capture_window_exe_name'),
135
+ self.config.get('windows_capture'),
136
+ self.config.get('adb_capture'),
137
137
  self.config.get("debug"),
138
138
  self.exit_event)
139
139
  ok.gui.device_manager = self.device_manager
@@ -18,9 +18,26 @@ class BaseCaptureMethod:
18
18
  # Some capture methods don't need an initialization process
19
19
  pass
20
20
 
21
+ @property
22
+ def width(self):
23
+ if self._size[0] == 0:
24
+ self.get_frame()
25
+ return self._size[0]
26
+
27
+ @property
28
+ def height(self):
29
+ if self._size[1] == 0:
30
+ self.get_frame()
31
+ return self._size[1]
32
+
21
33
  def get_frame(self) -> np.ndarray | None:
22
34
  try:
23
- return self.do_get_frame()
35
+ frame = self.do_get_frame()
36
+ if frame is not None:
37
+ self._size = (frame.shape[1], frame.shape[0])
38
+ if frame.shape[2] == 4:
39
+ frame = frame[:, :, :3]
40
+ return frame
24
41
  except Exception as e:
25
42
  raise CaptureException() from e
26
43
 
@@ -1,10 +1,9 @@
1
1
  # original https://github.com/dantmnf & https://github.com/hakaboom/winAuto
2
- import ctypes
3
- import os
4
2
  import re
5
3
  import threading
6
4
  import time
7
5
 
6
+ import psutil
8
7
  import win32process
9
8
  from win32 import win32gui
10
9
 
@@ -28,6 +27,8 @@ class HwndWindow:
28
27
  right_cut = 0
29
28
  bottom_cut = 0
30
29
  left_cut = 0
30
+ ext_left_bounds = 0 # for BitBlt
31
+ ext_top_bounds = 0 # for BitBlt
31
32
  window_change_listeners = []
32
33
  frame_aspect_ratio = 0
33
34
  hwnd = None
@@ -35,13 +36,14 @@ class HwndWindow:
35
36
  frame_height = 0
36
37
  exists = False
37
38
  title = None
39
+ exe_full_path = None
38
40
 
39
- def __init__(self, exit_event, title="", exe_name=None, frame_width=0, frame_height=0):
41
+ def __init__(self, exit_event, title, exe_name=None, frame_width=0, frame_height=0):
40
42
  super().__init__()
41
43
  self.app_exit_event = exit_event
42
44
  self.exe_name = exe_name
45
+ self.title = title
43
46
  self.stop_event = threading.Event()
44
- self.update_title_re(title)
45
47
  self.visible = False
46
48
  self.update_frame_size(frame_width, frame_height)
47
49
  self.do_update_window_size()
@@ -49,7 +51,7 @@ class HwndWindow:
49
51
  self.thread.start()
50
52
 
51
53
  def update_title_and_exe(self, title, exe):
52
- self.update_title_re(title)
54
+ self.title = title
53
55
  self.exe_name = exe
54
56
  self.hwnd = None
55
57
  self.visible = False
@@ -61,12 +63,6 @@ class HwndWindow:
61
63
  def stop(self):
62
64
  self.stop_event.set()
63
65
 
64
- def update_title_re(self, title):
65
- if not title:
66
- self.title = None
67
- else:
68
- self.title = re.compile(title)
69
-
70
66
  def update_frame_size(self, width, height):
71
67
  logger.debug(f"update_frame_size:{self.frame_width}x{self.frame_height} to {width}x{height}")
72
68
  if width != self.frame_width or height != self.frame_height:
@@ -82,24 +78,22 @@ class HwndWindow:
82
78
  time.sleep(0.2)
83
79
 
84
80
  def get_abs_cords(self, x, y):
85
- return int(self.x * self.scaling + (self.border * self.scaling + x)), int(
86
- self.y * self.scaling + (y + self.title_height * self.scaling))
81
+ return self.x + self.border + x, self.y + y + self.title_height
87
82
 
88
83
  def get_top_left_frame_offset(self):
89
- return int(self.border * self.scaling), int(
90
- self.title_height * self.scaling)
84
+ return self.border, self.title_height
91
85
 
92
86
  def do_update_window_size(self):
93
87
  try:
94
- 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
88
+ visible, x, y, border, title_height, width, height, scaling, ext_left_bounds, ext_top_bounds = self.visible, self.x, self.y, self.border, self.title_height, self.width, self.height, self.scaling, self.ext_left_bounds, self.ext_top_bounds
95
89
  if self.hwnd is None:
96
- self.hwnd = find_hwnd_by_title_and_exe(self.title, self.exe_name)
90
+ self.hwnd, self.exe_full_path = find_hwnd_by_title_and_exe(self.title, self.exe_name)
97
91
  self.exists = self.hwnd is not None
98
92
  if self.hwnd is not None:
99
93
  self.exists = win32gui.IsWindow(self.hwnd)
100
94
  if self.exists:
101
95
  visible = is_foreground_window(self.hwnd)
102
- x, y, border, title_height, width, height, scaling = get_window_bounds(
96
+ x, y, border, title_height, width, height, scaling, ext_left_bounds, ext_top_bounds = get_window_bounds(
103
97
  self.hwnd)
104
98
  if self.frame_aspect_ratio != 0 and height != 0:
105
99
  window_ratio = width / height
@@ -117,10 +111,10 @@ class HwndWindow:
117
111
  self.visible = visible
118
112
  self.scaling = scaling
119
113
  changed = True
120
- if (
121
- 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 (
122
- (x >= 0 and y >= 0) or self.visible):
123
- self.x, self.y, self.border, self.title_height, self.width, self.height = x, y, border, title_height, width, height
114
+ if (ext_left_bounds != self.ext_left_bounds or ext_top_bounds != self.ext_top_bounds or
115
+ 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 (
116
+ (x >= -1 and y >= -1) or self.visible):
117
+ self.x, self.y, self.border, self.title_height, self.width, self.height, self.ext_left_bounds, self.ext_top_bounds = x, y, border, title_height, width, height, ext_left_bounds, ext_top_bounds
124
118
  changed = True
125
119
  if changed:
126
120
  logger.debug(
@@ -148,7 +142,7 @@ class HwndWindow:
148
142
  def find_hwnd_by_title_and_exe(title, exe_name):
149
143
  result = []
150
144
  if title is None and exe_name is None:
151
- return None
145
+ return None, None
152
146
 
153
147
  def callback(hwnd, lParam):
154
148
  if len(result) == 0 and win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
@@ -159,42 +153,26 @@ def find_hwnd_by_title_and_exe(title, exe_name):
159
153
  return True
160
154
  elif not re.search(title, text):
161
155
  return True
156
+ name, full_path = get_exe_by_hwnd(hwnd)
162
157
  if exe_name:
163
- name = get_exe_name_by_hwnd(hwnd)
164
158
  if name == exe_name:
165
- result.append(hwnd)
159
+ result.append((hwnd, full_path))
166
160
  return True # Stop enumeration as we found the first match
167
161
  else:
168
- result.append(hwnd)
162
+ result.append((hwnd, full_path))
169
163
  return True
170
164
 
171
165
  win32gui.EnumWindows(callback, None)
172
- return result[0] if result else None
173
-
174
-
175
- OpenProcess = ctypes.windll.kernel32.OpenProcess
176
- CloseHandle = ctypes.windll.kernel32.CloseHandle
177
- GetModuleFileNameExW = ctypes.windll.psapi.GetModuleFileNameExW
166
+ if result:
167
+ return result[0][0], result[0][1]
168
+ else:
169
+ return None, None
178
170
 
179
- PROCESS_QUERY_INFORMATION = 0x0400
180
- PROCESS_VM_READ = 0x0010
181
171
 
182
-
183
- def get_exe_name_by_hwnd(hwnd):
184
- # Get the process ID of the window
172
+ def get_exe_by_hwnd(hwnd):
173
+ # Get the process ID associated with the window
185
174
  _, pid = win32process.GetWindowThreadProcessId(hwnd)
186
175
 
187
- # Open the process
188
- h_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid)
189
- if h_process:
190
- # Get the executable name
191
- exe_name = ctypes.create_unicode_buffer(1024)
192
- GetModuleFileNameExW(h_process, None, exe_name, 1024)
193
-
194
- # Extract the executable name from the full path
195
- exe_only_name = os.path.basename(exe_name.value)
196
-
197
- # Close the process handle
198
- CloseHandle(h_process)
199
-
200
- return exe_only_name
176
+ # Get the process name and executable path
177
+ process = psutil.Process(pid)
178
+ return process.name(), process.exe()
@@ -19,18 +19,6 @@ class ADBCaptureMethod(BaseCaptureMethod):
19
19
  self._connected = (width != 0 and height != 0)
20
20
  self.device_manager = device_manager
21
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
22
  @override
35
23
  def do_get_frame(self) -> np.ndarray | None:
36
24
  return self.screencap()
@@ -38,11 +26,9 @@ class ADBCaptureMethod(BaseCaptureMethod):
38
26
  def screencap(self):
39
27
  if self.exit_event.is_set():
40
28
  return None
41
- frame = do_screencap(self.device_manager)
29
+ frame = do_screencap(self.device_manager.device)
42
30
  if frame is not None:
43
- height, width, _ = frame.shape
44
31
  self._connected = True
45
- self._size = (width, height)
46
32
  else:
47
33
  self._connected = False
48
34
  return frame
@@ -53,8 +39,10 @@ class ADBCaptureMethod(BaseCaptureMethod):
53
39
  return self._connected and self.device_manager.device is not None
54
40
 
55
41
 
56
- def do_screencap(device_manager) -> np.ndarray | None:
57
- png_bytes = device_manager.shell("screencap -p", encoding=None)
42
+ def do_screencap(device) -> np.ndarray | None:
43
+ if device is None:
44
+ return None
45
+ png_bytes = device.shell("screencap -p", encoding=None)
58
46
  if png_bytes is not None and len(png_bytes) > 0:
59
47
  image_data = np.frombuffer(png_bytes, dtype=np.uint8)
60
48
  image = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
@@ -1,13 +1,12 @@
1
1
  import threading
2
2
 
3
- import adbutils
4
3
  import win32gui
5
4
  from adbutils import AdbError
6
5
 
7
6
  from ok.capture.HwndWindow import HwndWindow, find_hwnd_by_title_and_exe
8
7
  from ok.capture.adb.ADBCaptureMethod import ADBCaptureMethod, do_screencap
8
+ from ok.capture.adb.WindowsCaptureFactory import update_capture_method
9
9
  from ok.capture.adb.vbox import installed_emulator
10
- from ok.capture.windows.WindowsGraphicsCaptureMethod import WindowsGraphicsCaptureMethod
11
10
  from ok.capture.windows.window import get_window_bounds
12
11
  from ok.config.Config import Config
13
12
  from ok.gui.Communicate import communicate
@@ -20,22 +19,24 @@ logger = get_logger(__name__)
20
19
 
21
20
  class DeviceManager:
22
21
 
23
- def __init__(self, config_folder, hwnd_title=None, exe_name=None, debug=False, exit_event=None):
22
+ def __init__(self, config_folder, windows_capture_config, adb_capture_config, debug=False, exit_event=None):
24
23
  self._device = None
25
24
  self._connect_all = False
26
- self.adb = self.adb = adbutils.AdbClient(host="127.0.0.1")
27
- logger.debug(f'connect adb')
28
- self.hwnd_title = hwnd_title
25
+ self._adb = None
26
+ self.windows_capture_config = windows_capture_config
27
+ self.adb_capture_config = adb_capture_config
29
28
  self.pc_device = None
30
- self.exe_name = exe_name
31
29
  self.debug = debug
32
30
  self.interaction = None
33
31
  self.exit_event = exit_event
34
- if hwnd_title is not None:
35
- self.hwnd = HwndWindow(exit_event, hwnd_title, exe_name)
36
- self.config = Config({"devices": {}, "preferred": "none"}, config_folder, "DeviceManager")
32
+ if windows_capture_config is not None:
33
+ self.hwnd = HwndWindow(exit_event, windows_capture_config.get('title'),
34
+ windows_capture_config.get('exe'))
35
+ self.config = Config({"devices": {}, "preferred": "none", "pc_full_path": None}, config_folder,
36
+ "DeviceManager")
37
37
  self.thread = None
38
38
  self.capture_method = None
39
+ self.update_pc_device()
39
40
  self.start()
40
41
 
41
42
  def refresh(self):
@@ -55,6 +56,14 @@ class DeviceManager:
55
56
  else:
56
57
  return device_dict
57
58
 
59
+ @property
60
+ def adb(self):
61
+ if self._adb is None:
62
+ import adbutils
63
+ self._adb = adbutils.AdbClient(host="127.0.0.1")
64
+ logger.debug(f'connect adb')
65
+ return self._adb
66
+
58
67
  def connect_all(self):
59
68
  if not self._connect_all:
60
69
  for device in self.device_dict.values():
@@ -69,49 +78,62 @@ class DeviceManager:
69
78
  def get_devices(self):
70
79
  return list(self.device_dict.values())
71
80
 
72
- def do_refresh(self):
73
-
74
- if self.hwnd_title:
81
+ def update_pc_device(self):
82
+ if self.windows_capture_config is not None:
75
83
  nick = ""
76
84
  width = 0
77
85
  height = 0
78
- hwnd = find_hwnd_by_title_and_exe(self.hwnd_title, self.exe_name)
86
+ hwnd, full_path = find_hwnd_by_title_and_exe(self.windows_capture_config.get('title'),
87
+ self.windows_capture_config.get('exe'))
79
88
  if hwnd is not None:
80
89
  nick = win32gui.GetWindowText(hwnd)
81
- _, _, _, _, width, height, _ = get_window_bounds(
90
+ _, _, _, _, width, height, _, _, _ = get_window_bounds(
82
91
  hwnd)
83
92
  if not nick:
84
- nick = self.exe_name
93
+ nick = self.windows_capture_config.get('exe')
85
94
  self.pc_device = {"address": "", "imei": 'pc', "device": "windows",
86
95
  "model": "", "nick": nick, "width": width,
87
96
  "height": height,
88
97
  "hwnd": nick, "capture": "windows",
89
- "connected": hwnd is not None
98
+ "connected": hwnd is not None,
99
+ "full_path": full_path or self.config.get('pc_full_path')
90
100
  }
101
+ if full_path and full_path != self.config.get('pc_full_path'):
102
+ self.config['pc_full_path'] = full_path
103
+
91
104
  if width != 0:
92
105
  self.pc_device["resolution"] = f"{width}x{height}"
93
106
  communicate.adb_devices.emit(False)
94
107
 
95
- self.connect_all()
108
+ def do_refresh(self, fast=False):
109
+ self.update_pc_device()
110
+
111
+ self.refresh_adb_device(fast)
112
+
113
+ if self.exit_event.is_set():
114
+ return
115
+ self.start()
116
+ communicate.adb_devices.emit(True)
117
+ logger.debug(f'refresh {self.device_dict}')
96
118
 
97
- installed_emulators = installed_emulator()
119
+ def refresh_adb_device(self, fast):
120
+ if self.adb_capture_config is None:
121
+ return
122
+ if not fast:
123
+ self.connect_all()
124
+ installed_emulators = [] if fast else installed_emulator()
98
125
  for device in self.adb.list():
99
126
  logger.debug(f'adb.list() {device}')
100
-
101
127
  for emulator in installed_emulators:
102
128
  logger.debug(f"installed_emulator: {emulator}")
103
- self.adb.connect(emulator.adb_address)
104
-
129
+ self.adb.connect(emulator.adb_address, timeout=1)
105
130
  device_list = self.adb.device_list()
106
-
107
131
  device_list = sorted(device_list, key=lambda x: x.serial)
108
-
109
132
  adb_connected = []
110
-
111
133
  for device in device_list:
112
134
  imei = device.shell("settings get secure android_id") or device.shell(
113
135
  "service call iphonesubinfo 4") or device.prop.model
114
- frame = do_screencap(device)
136
+ frame = None if fast else do_screencap(device)
115
137
  width, height = 0, 0
116
138
  if frame is not None:
117
139
  height, width, _ = frame.shape
@@ -137,17 +159,10 @@ class DeviceManager:
137
159
  self.update_device(imei, adb_device)
138
160
  adb_connected.append(imei)
139
161
  for imei in self.device_dict:
140
- if imei not in adb_connected and self.device_dict[imei]['device'] == 'adb':
141
- self.update_device_value(imei, 'connected', False)
142
-
162
+ if self.device_dict[imei]['device'] == 'adb':
163
+ self.update_device_value(imei, 'connected', imei in adb_connected)
143
164
  self.config.save_file()
144
165
 
145
- if self.exit_event.is_set():
146
- return
147
- self.start()
148
- communicate.adb_devices.emit(True)
149
- logger.debug(f'refresh {self.device_dict}')
150
-
151
166
  def update_device(self, imei, device=None):
152
167
  devices = self.config.get("devices", {})
153
168
  devices[imei] = device
@@ -204,6 +219,16 @@ class DeviceManager:
204
219
  self.hwnd.update_frame_size(frame_width, frame_height)
205
220
  self.hwnd.update_title_and_exe(title, exe)
206
221
 
222
+ def use_windows_capture(self, override_config=None, require_bg=False):
223
+ if not override_config:
224
+ override_config = self.windows_capture_config
225
+ self.capture_method = update_capture_method(override_config, self.capture_method, self.hwnd,
226
+ require_bg)
227
+ if self.capture_method is None:
228
+ logger.error(f'can find a usable windows capture')
229
+ else:
230
+ logger.info(f'capture method {type(self.capture_method)}')
231
+
207
232
  def start(self):
208
233
  preferred = self.get_preferred_device()
209
234
  if preferred is None:
@@ -212,14 +237,11 @@ class DeviceManager:
212
237
  return
213
238
 
214
239
  if preferred['device'] == 'windows':
215
- self.ensure_hwnd(self.hwnd_title, self.exe_name)
216
- if not isinstance(self.capture_method, WindowsGraphicsCaptureMethod):
217
- if self.capture_method is not None:
218
- self.capture_method.close()
219
- self.capture_method = WindowsGraphicsCaptureMethod(self.hwnd)
240
+ self.ensure_hwnd(self.windows_capture_config.get('title'), self.windows_capture_config.get('exe'))
241
+ self.use_windows_capture()
220
242
  if not isinstance(self.interaction, Win32Interaction):
221
243
  self.interaction = Win32Interaction(self.capture_method)
222
- self.capture_method.hwnd_window = self.hwnd
244
+ preferred['connected'] = self.capture_method.connected()
223
245
  else:
224
246
  self.connect_all()
225
247
  for adb_device in self.adb.device_list():
@@ -231,11 +253,9 @@ class DeviceManager:
231
253
  height = preferred.get('height', 0)
232
254
  if preferred.get('capture') == "windows":
233
255
  self.ensure_hwnd(hwnd_name, None, width, height)
234
- if not isinstance(self.capture_method, WindowsGraphicsCaptureMethod):
235
- if self.capture_method is not None:
236
- self.capture_method.close()
237
- self.capture_method = WindowsGraphicsCaptureMethod(self.hwnd)
238
- self.capture_method.hwnd_window = self.hwnd
256
+ if self.hwnd.exe_full_path:
257
+ self.update_device_value(preferred['imei'], 'full_path', self.hwnd.exe_full_path)
258
+ self.use_windows_capture({'can_bit_blt': True}, require_bg=True)
239
259
  elif not isinstance(self.capture_method, ADBCaptureMethod):
240
260
  if self.capture_method is not None:
241
261
  self.capture_method.close()
@@ -284,3 +304,11 @@ class DeviceManager:
284
304
  return device.shell(*args, **kwargs)
285
305
  else:
286
306
  raise Exception('Device is none')
307
+
308
+ def get_exe_path(self, device):
309
+ path = device.get('full_path')
310
+ if device.get('device') == 'windows' and self.windows_capture_config and self.windows_capture_config.get(
311
+ 'calculate_pc_exe_path'):
312
+ return self.windows_capture_config.get('calculate_pc_exe_path')(path)
313
+ else:
314
+ return path
@@ -0,0 +1,39 @@
1
+ from ok.logging.Logger import get_logger
2
+
3
+ logger = get_logger(__name__)
4
+
5
+
6
+ def update_capture_method(config, capture_method, hwnd, require_bg=False):
7
+ if config.get('can_bit_blt'):
8
+ logger.debug(
9
+ f"try BitBlt method {config} current_type:{type(capture_method)}")
10
+ from ok.capture.windows.BitBltCaptureMethod import BitBltCaptureMethod
11
+ target_method = BitBltCaptureMethod
12
+ capture_method = get_capture(capture_method, target_method, hwnd)
13
+ if capture_method.test_exclusive_full_screen():
14
+ return capture_method
15
+ else:
16
+ logger.info("test_exclusive_full_screen failed, can't use BitBlt")
17
+ from ok.capture.windows.WindowsGraphicsCaptureMethod import windows_graphics_available
18
+ if windows_graphics_available():
19
+ from ok.capture.windows.WindowsGraphicsCaptureMethod import WindowsGraphicsCaptureMethod
20
+ target_method = WindowsGraphicsCaptureMethod
21
+ capture_method = get_capture(capture_method, target_method, hwnd)
22
+ return capture_method
23
+
24
+ if not require_bg:
25
+ from ok.capture.windows.DesktopDuplicationCaptureMethod import DesktopDuplicationCaptureMethod
26
+ target_method = DesktopDuplicationCaptureMethod
27
+ capture_method = get_capture(capture_method, target_method, hwnd)
28
+ return capture_method
29
+
30
+ return None
31
+
32
+
33
+ def get_capture(capture_method, target_method, hwnd):
34
+ if not isinstance(capture_method, target_method):
35
+ if capture_method is not None:
36
+ capture_method.close()
37
+ capture_method = target_method(hwnd)
38
+ capture_method.hwnd_window = hwnd
39
+ return capture_method
@@ -3,7 +3,6 @@
3
3
  import winreg
4
4
  from dataclasses import dataclass
5
5
 
6
- import adbutils
7
6
  import win32com.client
8
7
 
9
8
  from ok.capture.adb.targets import ADBControllerTarget
@@ -122,20 +121,3 @@ def installed_emulator():
122
121
  except Exception as e:
123
122
  logger.error(f'failed to check server :{server} {e}', e)
124
123
  return results
125
-
126
-
127
- def _main():
128
- logger.config({'debug': True})
129
- adb = adbutils.AdbClient(host="127.0.0.1", port=5037)
130
- for x in installed_emulator():
131
- logger.debug(f'installed emulators {x}')
132
- adb.connect(x.adb_address)
133
-
134
- logger.debug(adb.device_list())
135
- for device in adb.device_list():
136
- logger.debug(device.shell('settings get secure android_id'))
137
- logger.debug(f'{device.info}')
138
-
139
-
140
- if __name__ == '__main__':
141
- _main()
@@ -0,0 +1,36 @@
1
+ # original https://github.com/dantmnf & https://github.com/hakaboom/winAuto
2
+ import ctypes.wintypes
3
+
4
+ from ok.capture.BaseCaptureMethod import BaseCaptureMethod
5
+ from ok.capture.HwndWindow import HwndWindow
6
+ from ok.logging.Logger import get_logger
7
+
8
+ PBYTE = ctypes.POINTER(ctypes.c_ubyte)
9
+ WGC_NO_BORDER_MIN_BUILD = 20348
10
+
11
+ logger = get_logger(__name__)
12
+
13
+
14
+ class BaseWindowsCaptureMethod(BaseCaptureMethod):
15
+ _hwnd_window: HwndWindow = None
16
+
17
+ def __init__(self, hwnd_window: HwndWindow):
18
+ super().__init__()
19
+ self._hwnd_window = hwnd_window
20
+
21
+ @property
22
+ def hwnd_window(self):
23
+ return self._hwnd_window
24
+
25
+ @hwnd_window.setter
26
+ def hwnd_window(self, hwnd_window):
27
+ self._hwnd_window = hwnd_window
28
+
29
+ def connected(self):
30
+ return self.hwnd_window is not None and self.hwnd_window.exists
31
+
32
+ def get_abs_cords(self, x, y):
33
+ return self.hwnd_window.get_abs_cords(x, y)
34
+
35
+ def clickable(self):
36
+ return self.hwnd_window is not None and self.hwnd_window.visible