ok-script 0.0.67__tar.gz → 0.0.69__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 (183) hide show
  1. {ok-script-0.0.67 → ok-script-0.0.69}/PKG-INFO +1 -1
  2. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/HwndWindow.py +69 -16
  3. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/adb/DeviceManager.py +25 -14
  4. {ok-script-0.0.67 → ok-script-0.0.69}/ok/feature/Box.py +10 -0
  5. {ok-script-0.0.67 → ok-script-0.0.69}/ok/feature/FeatureSet.py +18 -8
  6. {ok-script-0.0.67 → ok-script-0.0.69}/ok/feature/FindFeature.py +5 -5
  7. {ok-script-0.0.67 → ok-script-0.0.69}/ok/firebase/FireBaseAnalytics.py +82 -4
  8. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/App.py +1 -1
  9. {ok-script-0.0.67/ok/capture/adb → ok-script-0.0.69/ok/gui}/StartController.py +2 -1
  10. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/debug/DebugTab.py +24 -0
  11. ok-script-0.0.69/ok/gui/debug/FrameWidget.py +93 -0
  12. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/overlay/OverlayWindow.py +9 -4
  13. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/resources.py +588 -517
  14. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/OneTimeTaskTab.py +1 -0
  15. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/StartLoadingDialog.py +1 -1
  16. {ok-script-0.0.67 → ok-script-0.0.69}/ok/ocr/OCR.py +27 -7
  17. {ok-script-0.0.67 → ok-script-0.0.69}/ok/task/ExecutorOperation.py +6 -11
  18. {ok-script-0.0.67 → ok-script-0.0.69}/ok_script.egg-info/PKG-INFO +1 -1
  19. {ok-script-0.0.67 → ok-script-0.0.69}/ok_script.egg-info/SOURCES.txt +1 -1
  20. {ok-script-0.0.67 → ok-script-0.0.69}/setup.py +1 -1
  21. ok-script-0.0.67/ok/gui/debug/FrameWidget.py +0 -58
  22. {ok-script-0.0.67 → ok-script-0.0.69}/README.md +0 -0
  23. {ok-script-0.0.67 → ok-script-0.0.69}/ok/OK.py +0 -0
  24. {ok-script-0.0.67 → ok-script-0.0.69}/ok/__init__.py +0 -0
  25. {ok-script-0.0.67 → ok-script-0.0.69}/ok/alas/__init__.py +0 -0
  26. {ok-script-0.0.67 → ok-script-0.0.69}/ok/alas/emulator_base.py +0 -0
  27. {ok-script-0.0.67 → ok-script-0.0.69}/ok/alas/emulator_windows.py +0 -0
  28. {ok-script-0.0.67 → ok-script-0.0.69}/ok/alas/platform_base.py +0 -0
  29. {ok-script-0.0.67 → ok-script-0.0.69}/ok/alas/platform_windows.py +0 -0
  30. {ok-script-0.0.67 → ok-script-0.0.69}/ok/alas/utils.py +0 -0
  31. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/BaseCaptureMethod.py +0 -0
  32. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/__init__.py +0 -0
  33. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/adb/ADBCaptureMethod.py +0 -0
  34. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/adb/WindowsCaptureFactory.py +0 -0
  35. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/adb/__init__.py +0 -0
  36. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/adb/bin/__init__.py +0 -0
  37. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/image/ImageCaptureMethod.py +0 -0
  38. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/image/__init__.py +0 -0
  39. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/windows/BaseWindowsCaptureMethod.py +0 -0
  40. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/windows/BitBltCaptureMethod.py +0 -0
  41. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/windows/DesktopDuplicationCaptureMethod.py +0 -0
  42. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/windows/WindowsGraphicsCaptureMethod.py +0 -0
  43. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/windows/__init__.py +0 -0
  44. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/windows/d3d11.py +0 -0
  45. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/windows/dump.py +0 -0
  46. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/windows/utils.py +0 -0
  47. {ok-script-0.0.67 → ok-script-0.0.69}/ok/capture/windows/window.py +0 -0
  48. {ok-script-0.0.67 → ok-script-0.0.69}/ok/color/Color.py +0 -0
  49. {ok-script-0.0.67 → ok-script-0.0.69}/ok/color/__init__.py +0 -0
  50. {ok-script-0.0.67 → ok-script-0.0.69}/ok/config/Config.py +0 -0
  51. {ok-script-0.0.67 → ok-script-0.0.69}/ok/config/InfoDict.py +0 -0
  52. {ok-script-0.0.67 → ok-script-0.0.69}/ok/config/__init__.py +0 -0
  53. {ok-script-0.0.67 → ok-script-0.0.69}/ok/feature/Feature.py +0 -0
  54. {ok-script-0.0.67 → ok-script-0.0.69}/ok/feature/__init__.py +0 -0
  55. {ok-script-0.0.67 → ok-script-0.0.69}/ok/firebase/__init__.py +0 -0
  56. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/Communicate.py +0 -0
  57. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/MainWindow.py +0 -0
  58. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/MessageWindow.py +0 -0
  59. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/TabTitles.py +0 -0
  60. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/__init__.py +0 -0
  61. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/about/AboutTab.py +0 -0
  62. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/about/HttpTextEdit.py +0 -0
  63. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/about/VersionCard.py +0 -0
  64. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/about/__init__.py +0 -0
  65. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/common/OKIcon.py +0 -0
  66. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/common/__init__.py +0 -0
  67. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/common/config.py +0 -0
  68. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/common/style_sheet.py +0 -0
  69. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/debug/AspectRatioWidget.py +0 -0
  70. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/debug/InfoWidget.py +0 -0
  71. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/debug/LoggerWidget.py +0 -0
  72. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/debug/Screenshot.py +0 -0
  73. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/debug/__init__.py +0 -0
  74. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/i18n/__init__.py +0 -0
  75. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/i18n/path.py +0 -0
  76. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/i18n/update.py +0 -0
  77. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/icon/__init__.py +0 -0
  78. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/icon/icon.py +0 -0
  79. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/overlay/__init__.py +0 -0
  80. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/qss/__init__.py +0 -0
  81. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/settings/SettingTab.py +0 -0
  82. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/settings/__init__.py +0 -0
  83. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/start/SelectCaptureListView.py +0 -0
  84. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/start/StartCard.py +0 -0
  85. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/start/StartTab.py +0 -0
  86. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/start/__init__.py +0 -0
  87. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/ConfigItemFactory.py +0 -0
  88. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/ConfigLabelAndWidget.py +0 -0
  89. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/LabelAndDoubleSpinBox.py +0 -0
  90. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/LabelAndDropDown.py +0 -0
  91. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/LabelAndLineEdit.py +0 -0
  92. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/LabelAndList.py +0 -0
  93. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/LabelAndSpinBox.py +0 -0
  94. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/LabelAndSwitchButton.py +0 -0
  95. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/LabelAndWidget.py +0 -0
  96. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/ModifyListDialog.py +0 -0
  97. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/ModifyListItem.py +0 -0
  98. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/StartButton.py +0 -0
  99. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/TaskCard.py +0 -0
  100. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/TaskOpButton.py +0 -0
  101. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/TooltipTableWidget.py +0 -0
  102. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/TriggerTaskTab.py +0 -0
  103. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/tasks/__init__.py +0 -0
  104. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/util/Alert.py +0 -0
  105. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/util/InitWorker.py +0 -0
  106. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/util/__init__.py +0 -0
  107. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/Card.py +0 -0
  108. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/EmptyCard.py +0 -0
  109. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/FramelessDialog.py +0 -0
  110. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/ListTableWidgetItem.py +0 -0
  111. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/NumericTableWidgetItem.py +0 -0
  112. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/StatusBar.py +0 -0
  113. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/Tab.py +0 -0
  114. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/TabWidget.py +0 -0
  115. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/UpdateConfigWidgetItem.py +0 -0
  116. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/YesNonWidgetItem.py +0 -0
  117. {ok-script-0.0.67 → ok-script-0.0.69}/ok/gui/widget/__init__.py +0 -0
  118. {ok-script-0.0.67 → ok-script-0.0.69}/ok/interaction/ADBInteraction.py +0 -0
  119. {ok-script-0.0.67 → ok-script-0.0.69}/ok/interaction/BaseInteraction.py +0 -0
  120. {ok-script-0.0.67 → ok-script-0.0.69}/ok/interaction/DoNothingInteraction.py +0 -0
  121. {ok-script-0.0.67 → ok-script-0.0.69}/ok/interaction/Win32Interaction.py +0 -0
  122. {ok-script-0.0.67 → ok-script-0.0.69}/ok/interaction/__init__.py +0 -0
  123. {ok-script-0.0.67 → ok-script-0.0.69}/ok/logging/Logger.py +0 -0
  124. {ok-script-0.0.67 → ok-script-0.0.69}/ok/logging/__init__.py +0 -0
  125. {ok-script-0.0.67 → ok-script-0.0.69}/ok/ocr/__init__.py +0 -0
  126. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
  127. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
  128. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
  129. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
  130. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
  131. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
  132. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
  133. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
  134. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
  135. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Media/__init__.py +0 -0
  136. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
  137. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Security/__init__.py +0 -0
  138. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
  139. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/Storage/__init__.py +0 -0
  140. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/Windows/__init__.py +0 -0
  141. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/__init__.py +0 -0
  142. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/delegate.py +0 -0
  143. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/export.py +0 -0
  144. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/idldsl.py +0 -0
  145. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/inspectable.py +0 -0
  146. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/roapi.py +0 -0
  147. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/types.py +0 -0
  148. {ok-script-0.0.67 → ok-script-0.0.69}/ok/rotypes/winstring.py +0 -0
  149. {ok-script-0.0.67 → ok-script-0.0.69}/ok/save/BlackBarProcessor.py +0 -0
  150. {ok-script-0.0.67 → ok-script-0.0.69}/ok/save/PostProcessor.py +0 -0
  151. {ok-script-0.0.67 → ok-script-0.0.69}/ok/save/SaveByInterval.py +0 -0
  152. {ok-script-0.0.67 → ok-script-0.0.69}/ok/save/SaveByKeyPress.py +0 -0
  153. {ok-script-0.0.67 → ok-script-0.0.69}/ok/save/SaveMethodBase.py +0 -0
  154. {ok-script-0.0.67 → ok-script-0.0.69}/ok/save/__init__.py +0 -0
  155. {ok-script-0.0.67 → ok-script-0.0.69}/ok/scene/FeatureScene.py +0 -0
  156. {ok-script-0.0.67 → ok-script-0.0.69}/ok/scene/Scene.py +0 -0
  157. {ok-script-0.0.67 → ok-script-0.0.69}/ok/scene/__init__.py +0 -0
  158. {ok-script-0.0.67 → ok-script-0.0.69}/ok/stats/StreamStats.py +0 -0
  159. {ok-script-0.0.67 → ok-script-0.0.69}/ok/stats/__init__.py +0 -0
  160. {ok-script-0.0.67 → ok-script-0.0.69}/ok/task/BaseTask.py +0 -0
  161. {ok-script-0.0.67 → ok-script-0.0.69}/ok/task/OneTimeTask.py +0 -0
  162. {ok-script-0.0.67 → ok-script-0.0.69}/ok/task/TaskExecutor.py +0 -0
  163. {ok-script-0.0.67 → ok-script-0.0.69}/ok/task/TriggerTask.py +0 -0
  164. {ok-script-0.0.67 → ok-script-0.0.69}/ok/task/__init__.py +0 -0
  165. {ok-script-0.0.67 → ok-script-0.0.69}/ok/update/GithubMultiDownloader.py +0 -0
  166. {ok-script-0.0.67 → ok-script-0.0.69}/ok/update/Updater.py +0 -0
  167. {ok-script-0.0.67 → ok-script-0.0.69}/ok/update/__init__.py +0 -0
  168. {ok-script-0.0.67 → ok-script-0.0.69}/ok/update/gen_md5.py +0 -0
  169. {ok-script-0.0.67 → ok-script-0.0.69}/ok/util/Handler.py +0 -0
  170. {ok-script-0.0.67 → ok-script-0.0.69}/ok/util/__init__.py +0 -0
  171. {ok-script-0.0.67 → ok-script-0.0.69}/ok/util/exit_event.py +0 -0
  172. {ok-script-0.0.67 → ok-script-0.0.69}/ok/util/json.py +0 -0
  173. {ok-script-0.0.67 → ok-script-0.0.69}/ok/util/list.py +0 -0
  174. {ok-script-0.0.67 → ok-script-0.0.69}/ok/util/path.py +0 -0
  175. {ok-script-0.0.67 → ok-script-0.0.69}/ok/util/thread.py +0 -0
  176. {ok-script-0.0.67 → ok-script-0.0.69}/ok/util/win32_process.py +0 -0
  177. {ok-script-0.0.67 → ok-script-0.0.69}/ok/util/yaml.py +0 -0
  178. {ok-script-0.0.67 → ok-script-0.0.69}/ok_script.egg-info/dependency_links.txt +0 -0
  179. {ok-script-0.0.67 → ok-script-0.0.69}/ok_script.egg-info/requires.txt +0 -0
  180. {ok-script-0.0.67 → ok-script-0.0.69}/ok_script.egg-info/top_level.txt +0 -0
  181. {ok-script-0.0.67 → ok-script-0.0.69}/setup.cfg +0 -0
  182. {ok-script-0.0.67 → ok-script-0.0.69}/tests/__init__.py +0 -0
  183. {ok-script-0.0.67 → ok-script-0.0.69}/tests/test_updater.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ok-script
3
- Version: 0.0.67
3
+ Version: 0.0.69
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
@@ -50,8 +50,7 @@ class HwndWindow:
50
50
  self.title = title
51
51
  self.stop_event = threading.Event()
52
52
  self.visible = False
53
- self.update_frame_size(frame_width, frame_height)
54
- self.do_update_window_size()
53
+ self.update_window(title, exe_name, frame_width, frame_height)
55
54
  self.thread = threading.Thread(target=self.update_window_size, name="update_window_size")
56
55
  self.thread.start()
57
56
 
@@ -68,14 +67,21 @@ class HwndWindow:
68
67
  def stop(self):
69
68
  self.stop_event.set()
70
69
 
70
+ def update_window(self, title, exe_name, frame_width, frame_height):
71
+ self.title = title
72
+ self.exe_name = exe_name
73
+ self.update_frame_size(frame_width, frame_height)
74
+
71
75
  def update_frame_size(self, width, height):
72
76
  logger.debug(f"update_frame_size:{self.frame_width}x{self.frame_height} to {width}x{height}")
73
77
  if width != self.frame_width or height != self.frame_height:
78
+ self.hwnd = None
74
79
  self.frame_width = width
75
80
  self.frame_height = height
76
81
  if width > 0 and height > 0:
77
82
  self.frame_aspect_ratio = width / height
78
- logger.debug(f"HwndWindow: frame ratio:{self.frame_aspect_ratio} width: {width}, height: {height}")
83
+ logger.debug(f"HwndWindow: frame ratio: width: {width}, height: {height}")
84
+ self.do_update_window_size()
79
85
 
80
86
  def update_window_size(self):
81
87
  while not self.app_exit_event.is_set() and not self.stop_event.is_set():
@@ -95,7 +101,7 @@ class HwndWindow:
95
101
  name, self.hwnd, self.exe_full_path, self.real_x_offset, self.real_y_offset, self.real_width, self.real_height = find_hwnd(
96
102
  self.title,
97
103
  self.exe_name,
98
- self.frame_aspect_ratio)
104
+ self.frame_width, self.frame_height)
99
105
  self.exists = self.hwnd is not None
100
106
  if self.hwnd is not None:
101
107
  self.exists = win32gui.IsWindow(self.hwnd)
@@ -147,10 +153,11 @@ class HwndWindow:
147
153
  return f"title:{self.title}_{self.exe_name}_{self.width}x{self.height}_{self.hwnd}_{self.exists}_{self.visible}"
148
154
 
149
155
 
150
- def find_hwnd(title, exe_name, frame_aspect_ratio=0):
156
+ def find_hwnd(title, exe_name, frame_width, frame_height):
151
157
  results = []
152
158
  if exe_name is None and title is None:
153
159
  return None, None, None, 0, 0, 0, 0
160
+ frame_aspect_ratio = frame_width / frame_height if frame_height != 0 else 0
154
161
 
155
162
  def callback(hwnd, lParam):
156
163
  if win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
@@ -173,7 +180,6 @@ def find_hwnd(title, exe_name, frame_aspect_ratio=0):
173
180
 
174
181
  win32gui.EnumWindows(callback, None)
175
182
  biggest = None
176
- ratio_match = None
177
183
  if len(results) > 0:
178
184
  for result in results:
179
185
  from ok.capture.windows.BitBltCaptureMethod import bit_blt_test_hwnd
@@ -189,17 +195,46 @@ def find_hwnd(title, exe_name, frame_aspect_ratio=0):
189
195
  x_offset = 0
190
196
  y_offset = 0
191
197
  real_width, real_height = biggest[2], biggest[3]
192
- if ratio_match is not None and ratio_match[4] - biggest[4] >= 0 and ratio_match[5] - biggest[5] >= 0:
193
- x_offset = ratio_match[4] - biggest[4]
194
- y_offset = ratio_match[5] - biggest[5]
195
- real_width = ratio_match[2]
196
- real_height = ratio_match[3]
198
+ if biggest is not None and frame_aspect_ratio != 0:
199
+ matching_child = enum_child_windows(biggest, frame_aspect_ratio)
200
+ if matching_child is not None:
201
+ x_offset, y_offset, real_width, real_height = matching_child
197
202
  logger.debug(f'find_hwnd {results} {biggest} {x_offset, y_offset, real_width, real_height}')
198
203
  return biggest[6], biggest[0], biggest[1], x_offset, y_offset, real_width, real_height,
199
204
  else:
200
205
  return None, None, None, 0, 0, 0, 0
201
206
 
202
207
 
208
+ def enum_child_windows(biggest, frame_aspect_ratio):
209
+ ratio_match = []
210
+ """
211
+ A function to enumerate all child windows of the given parent window handle
212
+ and print their handle and window title.
213
+ """
214
+
215
+ def child_callback(hwnd, _):
216
+ visible = win32gui.IsWindowVisible(hwnd)
217
+ parent = win32gui.GetParent(hwnd)
218
+ rect = win32gui.GetWindowRect(hwnd)
219
+ logger.debug(f'find_hwnd child_callback {visible} {biggest[0]} {parent} {rect}')
220
+ if visible and parent == biggest[0]:
221
+ real_width = rect[2] - rect[0]
222
+ real_height = rect[3] - rect[1]
223
+ ratio = real_width / real_height
224
+ difference = abs(ratio - frame_aspect_ratio)
225
+ support = difference <= 0.01 * frame_aspect_ratio
226
+ percent = (real_width * real_height) / (biggest[2] * biggest[3])
227
+ if support and percent >= 0.7:
228
+ x_offset = rect[0] - biggest[4]
229
+ y_offset = rect[1] - biggest[5]
230
+ ratio_match.append((x_offset, y_offset, real_width, real_height))
231
+ return True
232
+
233
+ win32gui.EnumChildWindows(biggest[0], child_callback, None)
234
+ if len(ratio_match) > 0:
235
+ return ratio_match[0]
236
+
237
+
203
238
  def get_exe_by_hwnd(hwnd):
204
239
  # Get the process ID associated with the window
205
240
  _, pid = win32process.GetWindowThreadProcessId(hwnd)
@@ -230,10 +265,28 @@ def enum_windows(emulator_path=None):
230
265
  return windows
231
266
 
232
267
 
268
+ #
269
+ # def enum_child_windows(parent_handle):
270
+ # """
271
+ # A function to enumerate all child windows of the given parent window handle
272
+ # and print their handle and window title.
273
+ # """
274
+ #
275
+ # def callback(hwnd, _):
276
+ # if win32gui.IsWindowVisible(hwnd) and win32gui.GetParent(hwnd) == parent_handle:
277
+ # rect = win32gui.GetWindowRect(hwnd)
278
+ # width = rect[2] - rect[0]
279
+ # height = rect[3] - rect[1]
280
+ # print(f"Child HWND: {hwnd} - Window Text: {win32gui.GetWindowText(hwnd)} {rect} {width} {height}")
281
+ # return True
282
+ #
283
+ # win32gui.EnumChildWindows(parent_handle, callback, None)
284
+
285
+
233
286
  if __name__ == '__main__':
234
287
  print(find_hwnd("MuMu模拟器12", None))
235
- hwnd_window = HwndWindow(threading.Event(), None, 'D:\\MuMuPlayer-12.0\\shell\\MuMuPlayer.exe', 16, 9)
236
- from ok.capture.windows.BitBltCaptureMethod import BitBltCaptureMethod
237
-
238
- method = BitBltCaptureMethod(hwnd_window)
239
- method.get_frame()
288
+ # hwnd_window = HwndWindow(threading.Event(), None, 'D:\\MuMuPlayer-12.0\\shell\\MuMuPlayer.exe', 16, 9)
289
+ # from ok.capture.windows.BitBltCaptureMethod import BitBltCaptureMethod
290
+ #
291
+ # method = BitBltCaptureMethod(hwnd_window)
292
+ # method.get_frame()
@@ -25,6 +25,7 @@ class DeviceManager:
25
25
  self.interaction = None
26
26
  self.device_dict = {}
27
27
  self.exit_event = exit_event
28
+ self.resolution_dict = {}
28
29
  if windows_capture_config is not None:
29
30
  self.hwnd = HwndWindow(exit_event, windows_capture_config.get('title'),
30
31
  windows_capture_config.get('exe'))
@@ -73,7 +74,7 @@ class DeviceManager:
73
74
  def update_pc_device(self):
74
75
  if self.windows_capture_config is not None:
75
76
  name, hwnd, full_path, x, y, width, height = find_hwnd(self.windows_capture_config.get('title'),
76
- self.windows_capture_config.get('exe'))
77
+ self.windows_capture_config.get('exe'), 0, 0)
77
78
  nick = name or self.windows_capture_config.get('exe')
78
79
  pc_device = {"address": "", "imei": 'pc', "device": "windows",
79
80
  "model": "", "nick": nick, "width": width,
@@ -98,7 +99,7 @@ class DeviceManager:
98
99
 
99
100
  if self.exit_event.is_set():
100
101
  return
101
- self.start()
102
+ self.do_start()
102
103
 
103
104
  logger.debug(f'refresh {self.device_dict}')
104
105
 
@@ -112,10 +113,11 @@ class DeviceManager:
112
113
  found = True
113
114
  break
114
115
  if not found:
116
+ height, width = self.get_resolution(adb_device)
115
117
  logger.debug(f'refresh_phones found an phone {adb_device}')
116
118
  phone_device = {"address": adb_device.serial, "device": "adb", "connected": True, "imei": imei,
117
119
  "nick": adb_device.prop.model or imei,
118
- "resolution": self.get_resolution(adb_device)}
120
+ "resolution": f'{width}x{height}'}
119
121
  self.device_dict[imei] = phone_device
120
122
 
121
123
  def refresh_emulators(self):
@@ -124,8 +126,9 @@ class DeviceManager:
124
126
  installed_emulators = manager.all_emulator_instances
125
127
  for emulator in installed_emulators:
126
128
  adb_device = self.adb_connect(emulator.serial)
129
+ width, height = self.get_resolution(adb_device)
127
130
  name, hwnd, full_path, x, y, width, height = find_hwnd(None,
128
- emulator.path)
131
+ emulator.path, width, height)
129
132
  connected = adb_device is not None and name is not None
130
133
  emulator_device = {"address": emulator.serial, "device": "adb",
131
134
  "full_path": emulator.path, "connected": connected,
@@ -142,9 +145,12 @@ class DeviceManager:
142
145
  device = self.device
143
146
  width, height = 0, 0
144
147
  if device is not None:
148
+ if resolution := self.resolution_dict.get(device.serial):
149
+ return resolution
145
150
  frame = do_screencap(device)
146
151
  if frame is not None:
147
152
  height, width, _ = frame.shape
153
+ self.resolution_dict[device.serial] = (width, height)
148
154
  return height, width
149
155
 
150
156
  def set_preferred_device(self, imei=None, index=-1):
@@ -156,7 +162,12 @@ class DeviceManager:
156
162
  preferred = self.device_dict.get(imei)
157
163
  if preferred is None:
158
164
  if len(self.device_dict) > 0:
159
- preferred = next(iter(self.device_dict.values()))
165
+ connected_device = None
166
+ for device in self.device_dict.values():
167
+ if device.get('connected') or connected_device is None:
168
+ connected_device = device
169
+ logger.info(f'first start use first or connected device {connected_device}')
170
+ preferred = connected_device
160
171
  imei = preferred['imei']
161
172
  else:
162
173
  logger.warning(f'no devices')
@@ -204,8 +215,7 @@ class DeviceManager:
204
215
  if self.hwnd is None:
205
216
  self.hwnd = HwndWindow(self.exit_event, title, exe, frame_width, frame_height)
206
217
  else:
207
- self.hwnd.update_frame_size(frame_width, frame_height)
208
- self.hwnd.update_title_and_exe(title, exe)
218
+ self.hwnd.update_window(title, exe, frame_width, frame_height)
209
219
 
210
220
  def use_windows_capture(self, override_config=None, require_bg=False, use_bit_blt_only=False):
211
221
  if not override_config:
@@ -238,13 +248,14 @@ class DeviceManager:
238
248
  if self.config.get('capture') == "windows":
239
249
  self.ensure_hwnd(None, preferred.get('full_path'), width, height)
240
250
  self.use_windows_capture({'can_bit_blt': True}, require_bg=True, use_bit_blt_only=True)
241
- elif not isinstance(self.capture_method, ADBCaptureMethod):
242
- logger.debug(f'use adb capture')
243
- if self.capture_method is not None:
244
- self.capture_method.close()
245
- self.capture_method = ADBCaptureMethod(self, self.exit_event, width=width,
246
- height=height)
247
- if self.debug:
251
+ else:
252
+ if not isinstance(self.capture_method, ADBCaptureMethod):
253
+ logger.debug(f'use adb capture')
254
+ if self.capture_method is not None:
255
+ self.capture_method.close()
256
+ self.capture_method = ADBCaptureMethod(self, self.exit_event, width=width,
257
+ height=height)
258
+ if self.debug and preferred.get('full_path'):
248
259
  self.ensure_hwnd(None, preferred.get('full_path'), width, height)
249
260
  elif self.hwnd is not None:
250
261
  self.hwnd.stop()
@@ -250,3 +250,13 @@ def boxes_to_map_by_list_of_names(boxes: list[Box], name_list: list[str]) -> dic
250
250
  box_dict[box.name] = box
251
251
  if len(box_dict) == len(name_list):
252
252
  return box_dict
253
+
254
+
255
+ def relative_box(frame_width, frame_height, x, y, to_x=1, to_y=1, width=0, height=0, name=None):
256
+ if width == 0:
257
+ width = to_x - x
258
+ if height == 0:
259
+ height = to_y - y
260
+ return Box(int(x * frame_width), int(y * frame_height),
261
+ int(width * frame_width), int(height * frame_height),
262
+ name=name)
@@ -143,7 +143,8 @@ class FeatureSet:
143
143
  return boxes[0]
144
144
 
145
145
  def find_feature(self, mat: np.ndarray, category_name: str, horizontal_variance: float = 0,
146
- vertical_variance: float = 0, threshold: float = 0, use_gray_scale: bool = False) -> List[Box]:
146
+ vertical_variance: float = 0, threshold: float = 0, use_gray_scale: bool = False, x=-1, y=-1,
147
+ to_x=-1, to_y=-1, width=-1, height=-1) -> List[Box]:
147
148
  """
148
149
  Find a feature within a given variance.
149
150
 
@@ -168,15 +169,24 @@ class FeatureSet:
168
169
  vertical_variance = self.default_vertical_variance
169
170
  if category_name not in self.feature_dict:
170
171
  raise ValueError(f"FeatureSet: {category_name} not found in featureDict")
171
-
172
172
  feature = self.feature_dict[category_name]
173
173
  feature_width, feature_height = feature.width, feature.height
174
-
175
- # Define search area using variance
176
- search_x1 = max(0, int(feature.x - self.width * horizontal_variance))
177
- search_y1 = max(0, int(feature.y - self.height * vertical_variance))
178
- search_x2 = min(self.width, int(feature.x + feature_width + self.width * horizontal_variance))
179
- search_y2 = min(self.height, int(feature.y + feature_height + self.height * vertical_variance))
174
+ if x != -1 and y != -1:
175
+ frame_height, frame_width, *_ = mat.shape
176
+ if width == -1:
177
+ width = to_x - x
178
+ if height == -1:
179
+ height = to_y - y
180
+ search_x1 = int(x * frame_width)
181
+ search_y1 = int(y * frame_height)
182
+ search_x2 = int((x + width) * frame_width)
183
+ search_y2 = int((y + height) * frame_height)
184
+ else:
185
+ # Define search area using variance
186
+ search_x1 = max(0, int(feature.x - self.width * horizontal_variance))
187
+ search_y1 = max(0, int(feature.y - self.height * vertical_variance))
188
+ search_x2 = min(self.width, int(feature.x + feature_width + self.width * horizontal_variance))
189
+ search_y2 = min(self.height, int(feature.y + feature_height + self.height * vertical_variance))
180
190
 
181
191
  search_area = mat[search_y1:search_y2, search_x1:search_x2, :3]
182
192
  # Crop the search area from the image
@@ -11,11 +11,11 @@ class FindFeature:
11
11
  self.executor = None
12
12
 
13
13
  def find_feature(self, feature_name, horizontal_variance=0, vertical_variance=0, threshold=0,
14
- use_gray_scale=False) -> List[Box]:
14
+ use_gray_scale=False, x=-1, y=-1, to_x=-1, to_y=-1, width=-1, height=-1) -> List[Box]:
15
15
  if self.executor.frame is None:
16
16
  return list()
17
17
  return self.feature_set.find_feature(self.executor.frame, feature_name, horizontal_variance, vertical_variance,
18
- threshold, use_gray_scale)
18
+ threshold, use_gray_scale, x, y, to_x, to_y, width, height)
19
19
 
20
20
  def get_box_by_name(self, name):
21
21
  return self.feature_set.get_box_by_name(name)
@@ -33,9 +33,9 @@ class FindFeature:
33
33
  setattr(self, feature, result)
34
34
  return ret
35
35
 
36
- def wait_and_click(self, feature, horizontal_variance=0, vertical_variance=0, threshold=0, relative_x=0.5,
37
- relative_y=0.5,
38
- time_out=0, pre_action=None, post_action=None):
36
+ def wait_click_feature(self, feature, horizontal_variance=0, vertical_variance=0, threshold=0, relative_x=0.5,
37
+ relative_y=0.5,
38
+ time_out=0, pre_action=None, post_action=None):
39
39
  box = self.wait_until(lambda: self.find_one(feature, horizontal_variance, vertical_variance, threshold),
40
40
  time_out,
41
41
  pre_action,
@@ -1,5 +1,10 @@
1
+ import ctypes
1
2
  import json
3
+ import locale
4
+ import random
5
+ import time
2
6
  import uuid
7
+ from urllib.parse import urlencode
3
8
 
4
9
  import requests
5
10
  import wmi
@@ -10,6 +15,8 @@ from ok.util.Handler import Handler
10
15
 
11
16
  FIREBASE_ENDPOINT = 'https://www.google-analytics.com/mp/collect'
12
17
 
18
+ GTAG_ENDPOINT = "https://www.google-analytics.com/g/collect"
19
+
13
20
  logger = get_logger(__name__)
14
21
 
15
22
 
@@ -20,8 +27,9 @@ class FireBaseAnalytics:
20
27
  self.app_config = app_config
21
28
  self._config = None
22
29
  self._handler = Handler(exit_event, __name__)
23
- self._handler.post(self.report_open, 10)
30
+ self._handler.post(self.send_gtag, 0)
24
31
  self._user_properties = None
32
+ self._fv = 1
25
33
 
26
34
  @property
27
35
  def user_properties(self):
@@ -64,8 +72,10 @@ class FireBaseAnalytics:
64
72
  }
65
73
  return self._user_properties
66
74
 
67
- def report_open(self):
68
- self.send_event('app_open', {'version': self.app_config.get('version'), 'debug': self.app_config.get('debug')})
75
+ #
76
+ # def report_open(self):
77
+ # self._handler.post(self.send_gtag, 5)
78
+ # # self.send_event('app_open', {'version': self.app_config.get('version'), 'debug': self.app_config.get('debug')})
69
79
 
70
80
  @property
71
81
  def client_id(self):
@@ -73,8 +83,66 @@ class FireBaseAnalytics:
73
83
  self._config = Config({'client_id': ''}, self.app_config.get("config_folder") or "config", 'statistics')
74
84
  if not self._config.get('client_id'):
75
85
  self._config['client_id'] = get_unique_client_id()
86
+ else:
87
+ self._fv = 0
76
88
  return self._config.get('client_id')
77
89
 
90
+ def send_gtag(self):
91
+ # Define the parameters in a dictionary
92
+ params = {
93
+ 'v': '2',
94
+ 'tid': self.measurement_id,
95
+ 'gtm': '45je4630v9186643593za200',
96
+ '_p': random_number(),
97
+ # 'gcd': '13l3l3l3l1',
98
+ 'npa': '0',
99
+ 'dma': '0',
100
+ 'tag_exp': '0',
101
+ 'cid': self.client_id,
102
+ 'ul': locale.getdefaultlocale()[0],
103
+ 'sr': get_screen_resolution(),
104
+ 'uaa': 'x86',
105
+ 'uab': '64',
106
+ # 'uafvl': 'Microsoft Edge;125.0.2535.85|Chromium;125.0.6422.142|Not.A/Brand;24.0.0.0',
107
+ 'uamb': '0',
108
+ 'uam': '',
109
+ 'uap': 'Windows',
110
+ 'uapv': '15.0.0',
111
+ 'uaw': '0',
112
+ 'are': '1',
113
+ 'frm': '0',
114
+ 'pscdl': 'noapi',
115
+ '_s': '1',
116
+ 'sid': int(time.time()),
117
+ 'sct': '1',
118
+ 'seg': '1',
119
+ 'dl': 'https://ok-script.com/',
120
+ 'dt': 'AppStartGTAG2',
121
+ 'en': 'page_view',
122
+ '_ee': '1',
123
+ 'ep.version': self.app_config.get('version')
124
+ # 'tfd': '5133'
125
+ }
126
+ if self._fv:
127
+ params['_fv'] = self._fv
128
+
129
+ # Send the GET request
130
+
131
+ headers = {
132
+ 'Content-Type': 'application/json',
133
+ 'Origin': 'https://ok-script.com',
134
+ 'Referer': 'https://ok-script.com/'
135
+ }
136
+ query_string = urlencode(params)
137
+
138
+ response = requests.post(f'{GTAG_ENDPOINT}', data=params)
139
+
140
+ logger.debug(f'send gtag {GTAG_ENDPOINT}?{query_string}')
141
+ if response.status_code == 204:
142
+ logger.debug(f'Successfully sent gtag')
143
+ else:
144
+ logger.debug(f'Failed to send event: {response.status_code} - {response.text}')
145
+
78
146
  def send_event(self, event_name, params=None):
79
147
  if params is None:
80
148
  params = dict()
@@ -107,7 +175,17 @@ class FireBaseAnalytics:
107
175
 
108
176
 
109
177
  def get_unique_client_id():
110
- return str(uuid.uuid4())
178
+ return f'{random_number()}.{random_number()}'
179
+
180
+
181
+ def random_number():
182
+ return random.randint(0, 2147483647)
183
+
184
+
185
+ def get_screen_resolution():
186
+ user32 = ctypes.windll.user32
187
+ screensize = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)
188
+ return f"{screensize[0]}x{screensize[1]}"
111
189
 
112
190
 
113
191
  if __name__ == '__main__':
@@ -8,11 +8,11 @@ from qfluentwidgets import FluentTranslator, qconfig, Theme
8
8
 
9
9
  import ok
10
10
  import ok.gui.resources
11
- from ok.capture.adb.StartController import StartController
12
11
  from ok.firebase.FireBaseAnalytics import FireBaseAnalytics
13
12
  from ok.gui.Communicate import communicate
14
13
  from ok.gui.MainWindow import MainWindow
15
14
  from ok.gui.MessageWindow import MessageWindow
15
+ from ok.gui.StartController import StartController
16
16
  from ok.gui.i18n.path import i18n_path
17
17
  from ok.gui.overlay.OverlayWindow import OverlayWindow
18
18
  from ok.logging.Logger import get_logger
@@ -26,7 +26,7 @@ class StartController(QObject):
26
26
 
27
27
  def do_start(self, task=None):
28
28
  communicate.starting_emulator.emit(False, None, 30)
29
- self.device_manager.do_refresh(fast=True)
29
+ self.device_manager.do_refresh()
30
30
 
31
31
  device = self.device_manager.get_preferred_device()
32
32
 
@@ -37,6 +37,7 @@ class StartController(QObject):
37
37
  execute(path)
38
38
  wait_until = time.time() + 30
39
39
  while not self.exit_event.is_set():
40
+ self.device_manager.do_refresh()
40
41
  error = self.check_device_error()
41
42
  if error is None:
42
43
  break
@@ -12,6 +12,7 @@ from ok.gui.util.Alert import alert_info, alert_error
12
12
  from ok.gui.widget.Tab import Tab
13
13
  from ok.interaction.DoNothingInteraction import DoNothingInteraction
14
14
  from ok.logging.Logger import get_logger, exception_to_str
15
+ from ok.ocr.OCR import OCR
15
16
  from ok.util.Handler import Handler
16
17
 
17
18
  logger = get_logger(__name__)
@@ -44,6 +45,10 @@ class DebugTab(Tab):
44
45
  capture_button.clicked.connect(lambda: self.handler.post(self.capture))
45
46
  layout.addWidget(capture_button)
46
47
 
48
+ ocr_button = PushButton("OCR")
49
+ ocr_button.clicked.connect(lambda: self.handler.post(self.ocr))
50
+ layout.addWidget(ocr_button)
51
+
47
52
  open_log_folder = PushButton(self.tr("Open Logs"))
48
53
 
49
54
  call_task_widget = QWidget()
@@ -116,6 +121,25 @@ class DebugTab(Tab):
116
121
  self.config['target_function'] = func_name
117
122
  alert_info(self.tr(f"call success: {result}"))
118
123
 
124
+ def ocr(self):
125
+ if not ok.gui.executor.ocr:
126
+ alert_error(self.tr('No OCR configured'))
127
+ return
128
+ if ok.gui.device_manager.capture_method is not None:
129
+ logger.info(f'ok.gui.device_manager.capture_method {ok.gui.device_manager.capture_method}')
130
+ capture = str(ok.gui.device_manager.capture_method)
131
+ frame = ok.gui.device_manager.capture_method.do_get_frame()
132
+ if frame is not None:
133
+ ocr = OCR()
134
+ ocr.executor = ok.gui.executor
135
+ result = ocr.ocr(frame=frame)
136
+ self.update_result_text.emit(str(result))
137
+ alert_info(self.tr(f"OCR success: {result}"))
138
+ else:
139
+ alert_error(self.tr('Capture returned None'))
140
+ else:
141
+ alert_error(self.tr('No Capture Available or Selected'))
142
+
119
143
  def task_changed(self, text):
120
144
  self.config['target_task'] = text
121
145
  task = ok.gui.executor.get_task_by_class_name(text)
@@ -0,0 +1,93 @@
1
+ import os
2
+
3
+ import win32api
4
+ from PySide6.QtCore import Qt, QPoint, QTimer
5
+ from PySide6.QtGui import QPainter, QColor, QPen, QFont, QGuiApplication
6
+ from PySide6.QtWidgets import QWidget
7
+
8
+ import ok.gui
9
+ from ok.gui.Communicate import communicate
10
+ from ok.logging.Logger import get_logger
11
+
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ class FrameWidget(QWidget):
16
+ def __init__(self):
17
+ super(FrameWidget, self).__init__()
18
+ self._visible = True
19
+ self._mouse_position = QPoint(0, 0)
20
+ self.setMouseTracking(True)
21
+ communicate.update_overlay.connect(self.update)
22
+ # Start a timer to update mouse position using Windows API
23
+ self.timer = QTimer(self)
24
+ self.timer.timeout.connect(self.update_mouse_position)
25
+ self.timer.start(1000) # Update every 50 milliseconds
26
+ self.mouse_font = QFont()
27
+ self.mouse_font.setPointSize(10) # Adjust the size as needed
28
+ screen = QGuiApplication.primaryScreen()
29
+ self.scaling = screen.devicePixelRatio()
30
+ communicate.update_overlay.connect(self.update)
31
+
32
+ def update_mouse_position(self):
33
+ try:
34
+ x, y = win32api.GetCursorPos()
35
+ relative = self.mapFromGlobal(QPoint(x / self.scaling, y / self.scaling))
36
+ if self._mouse_position != relative and relative.x() > 0 and relative.y() > 0 and self._visible:
37
+ self._mouse_position = relative
38
+ self.update()
39
+ except Exception as e:
40
+ logger.warning(f'GetCursorPos exception {e}')
41
+
42
+ def frame_ratio(self):
43
+ if ok.gui.device_manager.width == 0:
44
+ return 1
45
+ return self.width() / ok.gui.device_manager.width
46
+
47
+ def paintEvent(self, event):
48
+ if not self._visible:
49
+ return
50
+ painter = QPainter(self)
51
+ self.paint_border(painter)
52
+ self.paint_boxes(painter)
53
+ self.paint_mouse_position(painter)
54
+
55
+ def paint_boxes(self, painter):
56
+ pen = QPen() # Set the brush to red color
57
+ pen.setWidth(2) # Set the width of the pen (border thickness)
58
+ painter.setPen(pen) # Apply the pen to the painter
59
+ painter.setBrush(Qt.NoBrush) # Ensure no fill
60
+
61
+ frame_ratio = self.frame_ratio()
62
+ for key, value in ok.gui.ok.screenshot.ui_dict.items():
63
+ boxes = value[0]
64
+ pen.setColor(value[2])
65
+ painter.setPen(pen)
66
+ for box in boxes:
67
+ width = box.width * frame_ratio
68
+ height = box.height * frame_ratio
69
+ x = box.x * frame_ratio
70
+ y = box.y * frame_ratio
71
+ painter.drawRect(x, y, width, height)
72
+ painter.drawText(x, y + height + 12, f"{box.name or key}_{round(box.confidence * 100)}")
73
+
74
+ def paint_border(self, painter):
75
+ pen = QPen(QColor(255, 0, 0, 255)) # Solid red color for the border
76
+ pen.setWidth(1) # Set the border width
77
+ painter.setPen(pen)
78
+ # Draw the border around the widget
79
+ painter.drawRect(0, 0, self.width() - 1, self.height() - 1)
80
+
81
+ def paint_mouse_position(self, painter):
82
+ x_percent = self._mouse_position.x() / self.width()
83
+ y_percent = self._mouse_position.y() / self.height()
84
+ x, y = self._mouse_position.x() * 2, self._mouse_position.y() * 2
85
+ text = f"({x}, {y}, {x_percent:.2f}, {y_percent:.2f})"
86
+ painter.setFont(self.mouse_font)
87
+
88
+ painter.setPen(QPen(QColor(255, 0, 0, 255), 1))
89
+ painter.drawText(20, 20, text)
90
+ #
91
+ # # Draw the black text
92
+ # painter.setPen(QPen(QColor(0, 0, 0), 2))
93
+ # painter.drawText(10, 10, text)