ok-script 0.0.49__tar.gz → 0.0.50__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.

Potentially problematic release.


This version of ok-script might be problematic. Click here for more details.

Files changed (170) hide show
  1. {ok-script-0.0.49 → ok-script-0.0.50}/PKG-INFO +2 -1
  2. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/adb/DeviceManager.py +3 -4
  3. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/App.py +6 -2
  4. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/Communicate.py +2 -0
  5. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/MainWindow.py +3 -3
  6. ok-script-0.0.50/ok/gui/about/AboutTab.py +135 -0
  7. ok-script-0.0.50/ok/gui/about/VersionCard.py +40 -0
  8. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/start/SelectCaptureListView.py +5 -7
  9. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/Tab.py +7 -3
  10. {ok-script-0.0.49 → ok-script-0.0.50}/ok/task/TaskExecutor.py +8 -2
  11. ok-script-0.0.50/ok/update/Updater.py +251 -0
  12. ok-script-0.0.50/ok/update/gen_md5.py +23 -0
  13. ok-script-0.0.50/ok/util/__init__.py +0 -0
  14. {ok-script-0.0.49 → ok-script-0.0.50}/ok/util/path.py +46 -0
  15. {ok-script-0.0.49 → ok-script-0.0.50}/ok_script.egg-info/PKG-INFO +2 -1
  16. {ok-script-0.0.49 → ok-script-0.0.50}/ok_script.egg-info/SOURCES.txt +7 -1
  17. {ok-script-0.0.49 → ok-script-0.0.50}/ok_script.egg-info/requires.txt +1 -0
  18. ok-script-0.0.50/ok_script.egg-info/top_level.txt +2 -0
  19. {ok-script-0.0.49 → ok-script-0.0.50}/setup.py +3 -2
  20. ok-script-0.0.50/tests/__init__.py +3 -0
  21. ok-script-0.0.50/tests/test_updater.py +25 -0
  22. ok-script-0.0.49/ok/gui/about/AboutTab.py +0 -24
  23. ok-script-0.0.49/ok_script.egg-info/top_level.txt +0 -1
  24. {ok-script-0.0.49 → ok-script-0.0.50}/README.md +0 -0
  25. {ok-script-0.0.49 → ok-script-0.0.50}/ok/OK.py +0 -0
  26. {ok-script-0.0.49 → ok-script-0.0.50}/ok/__init__.py +0 -0
  27. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/BaseCaptureMethod.py +0 -0
  28. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/HwndWindow.py +0 -0
  29. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/__init__.py +0 -0
  30. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/adb/ADBCaptureMethod.py +0 -0
  31. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/adb/WindowsCaptureFactory.py +0 -0
  32. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/adb/__init__.py +0 -0
  33. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/adb/bin/__init__.py +0 -0
  34. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/adb/targets.py +0 -0
  35. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/adb/vbox.py +0 -0
  36. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/windows/BaseWindowsCaptureMethod.py +0 -0
  37. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/windows/BitBltCaptureMethod.py +0 -0
  38. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/windows/DesktopDuplicationCaptureMethod.py +0 -0
  39. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/windows/WindowsGraphicsCaptureMethod.py +0 -0
  40. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/windows/__init__.py +0 -0
  41. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/windows/d3d11.py +0 -0
  42. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/windows/dump.py +0 -0
  43. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/windows/utils.py +0 -0
  44. {ok-script-0.0.49 → ok-script-0.0.50}/ok/capture/windows/window.py +0 -0
  45. {ok-script-0.0.49 → ok-script-0.0.50}/ok/color/Color.py +0 -0
  46. {ok-script-0.0.49 → ok-script-0.0.50}/ok/color/__init__.py +0 -0
  47. {ok-script-0.0.49 → ok-script-0.0.50}/ok/config/Config.py +0 -0
  48. {ok-script-0.0.49 → ok-script-0.0.50}/ok/config/InfoDict.py +0 -0
  49. {ok-script-0.0.49 → ok-script-0.0.50}/ok/config/__init__.py +0 -0
  50. {ok-script-0.0.49 → ok-script-0.0.50}/ok/feature/Box.py +0 -0
  51. {ok-script-0.0.49 → ok-script-0.0.50}/ok/feature/Feature.py +0 -0
  52. {ok-script-0.0.49 → ok-script-0.0.50}/ok/feature/FeatureSet.py +0 -0
  53. {ok-script-0.0.49 → ok-script-0.0.50}/ok/feature/FindFeature.py +0 -0
  54. {ok-script-0.0.49 → ok-script-0.0.50}/ok/feature/__init__.py +0 -0
  55. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/MessageWindow.py +0 -0
  56. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/TabTitles.py +0 -0
  57. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/__init__.py +0 -0
  58. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/about/__init__.py +0 -0
  59. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/common/OKIcon.py +0 -0
  60. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/common/__init__.py +0 -0
  61. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/common/config.py +0 -0
  62. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/common/style_sheet.py +0 -0
  63. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/debug/AspectRatioWidget.py +0 -0
  64. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/debug/DebugTab.py +0 -0
  65. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/debug/FrameWidget.py +0 -0
  66. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/debug/InfoWidget.py +0 -0
  67. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/debug/LoggerWidget.py +0 -0
  68. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/debug/Screenshot.py +0 -0
  69. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/debug/__init__.py +0 -0
  70. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/i18n/__init__.py +0 -0
  71. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/i18n/path.py +0 -0
  72. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/i18n/update.py +0 -0
  73. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/icon/__init__.py +0 -0
  74. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/icon/icon.py +0 -0
  75. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/overlay/OverlayWindow.py +0 -0
  76. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/overlay/__init__.py +0 -0
  77. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/qss/__init__.py +0 -0
  78. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/resources.py +0 -0
  79. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/settings/SettingTab.py +0 -0
  80. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/settings/__init__.py +0 -0
  81. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/start/SelectHwndWindow.py +0 -0
  82. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/start/StartCard.py +0 -0
  83. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/start/StartTab.py +0 -0
  84. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/start/__init__.py +0 -0
  85. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/ConfigItemFactory.py +0 -0
  86. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/ConfigLabelAndWidget.py +0 -0
  87. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/LabelAndDoubleSpinBox.py +0 -0
  88. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/LabelAndDropDown.py +0 -0
  89. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/LabelAndLineEdit.py +0 -0
  90. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/LabelAndList.py +0 -0
  91. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/LabelAndSpinBox.py +0 -0
  92. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/LabelAndSwitchButton.py +0 -0
  93. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/LabelAndWidget.py +0 -0
  94. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/ModifyListDialog.py +0 -0
  95. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/ModifyListItem.py +0 -0
  96. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/OneTimeTaskTab.py +0 -0
  97. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/StartButton.py +0 -0
  98. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/TaskCard.py +0 -0
  99. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/TaskOpButton.py +0 -0
  100. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/TooltipTableWidget.py +0 -0
  101. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/TriggerTaskTab.py +0 -0
  102. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/tasks/__init__.py +0 -0
  103. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/util/Alert.py +0 -0
  104. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/util/InitWorker.py +0 -0
  105. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/util/__init__.py +0 -0
  106. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/Card.py +0 -0
  107. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/FramelessDialog.py +0 -0
  108. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/ListTableWidgetItem.py +0 -0
  109. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/NumericTableWidgetItem.py +0 -0
  110. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/StatusBar.py +0 -0
  111. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/TabWidget.py +0 -0
  112. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/UpdateConfigWidgetItem.py +0 -0
  113. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/YesNonWidgetItem.py +0 -0
  114. {ok-script-0.0.49 → ok-script-0.0.50}/ok/gui/widget/__init__.py +0 -0
  115. {ok-script-0.0.49 → ok-script-0.0.50}/ok/interaction/ADBInteraction.py +0 -0
  116. {ok-script-0.0.49 → ok-script-0.0.50}/ok/interaction/BaseInteraction.py +0 -0
  117. {ok-script-0.0.49 → ok-script-0.0.50}/ok/interaction/Win32Interaction.py +0 -0
  118. {ok-script-0.0.49 → ok-script-0.0.50}/ok/interaction/__init__.py +0 -0
  119. {ok-script-0.0.49 → ok-script-0.0.50}/ok/logging/Logger.py +0 -0
  120. {ok-script-0.0.49 → ok-script-0.0.50}/ok/logging/__init__.py +0 -0
  121. {ok-script-0.0.49 → ok-script-0.0.50}/ok/ocr/OCR.py +0 -0
  122. {ok-script-0.0.49 → ok-script-0.0.50}/ok/ocr/__init__.py +0 -0
  123. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
  124. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
  125. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
  126. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
  127. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
  128. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
  129. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
  130. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
  131. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
  132. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Media/__init__.py +0 -0
  133. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
  134. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Security/__init__.py +0 -0
  135. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
  136. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/Storage/__init__.py +0 -0
  137. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/Windows/__init__.py +0 -0
  138. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/__init__.py +0 -0
  139. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/delegate.py +0 -0
  140. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/export.py +0 -0
  141. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/idldsl.py +0 -0
  142. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/inspectable.py +0 -0
  143. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/roapi.py +0 -0
  144. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/types.py +0 -0
  145. {ok-script-0.0.49 → ok-script-0.0.50}/ok/rotypes/winstring.py +0 -0
  146. {ok-script-0.0.49 → ok-script-0.0.50}/ok/save/BlackBarProcessor.py +0 -0
  147. {ok-script-0.0.49 → ok-script-0.0.50}/ok/save/PostProcessor.py +0 -0
  148. {ok-script-0.0.49 → ok-script-0.0.50}/ok/save/SaveByInterval.py +0 -0
  149. {ok-script-0.0.49 → ok-script-0.0.50}/ok/save/SaveByKeyPress.py +0 -0
  150. {ok-script-0.0.49 → ok-script-0.0.50}/ok/save/SaveMethodBase.py +0 -0
  151. {ok-script-0.0.49 → ok-script-0.0.50}/ok/save/__init__.py +0 -0
  152. {ok-script-0.0.49 → ok-script-0.0.50}/ok/scene/FeatureScene.py +0 -0
  153. {ok-script-0.0.49 → ok-script-0.0.50}/ok/scene/Scene.py +0 -0
  154. {ok-script-0.0.49 → ok-script-0.0.50}/ok/scene/__init__.py +0 -0
  155. {ok-script-0.0.49 → ok-script-0.0.50}/ok/stats/StreamStats.py +0 -0
  156. {ok-script-0.0.49 → ok-script-0.0.50}/ok/stats/__init__.py +0 -0
  157. {ok-script-0.0.49 → ok-script-0.0.50}/ok/task/BaseTask.py +0 -0
  158. {ok-script-0.0.49 → ok-script-0.0.50}/ok/task/ExecutorOperation.py +0 -0
  159. {ok-script-0.0.49 → ok-script-0.0.50}/ok/task/OneTimeTask.py +0 -0
  160. {ok-script-0.0.49 → ok-script-0.0.50}/ok/task/TriggerTask.py +0 -0
  161. {ok-script-0.0.49 → ok-script-0.0.50}/ok/task/__init__.py +0 -0
  162. {ok-script-0.0.49/ok/util → ok-script-0.0.50/ok/update}/__init__.py +0 -0
  163. {ok-script-0.0.49 → ok-script-0.0.50}/ok/util/exit_event.py +0 -0
  164. {ok-script-0.0.49 → ok-script-0.0.50}/ok/util/json.py +0 -0
  165. {ok-script-0.0.49 → ok-script-0.0.50}/ok/util/list.py +0 -0
  166. {ok-script-0.0.49 → ok-script-0.0.50}/ok/util/thread.py +0 -0
  167. {ok-script-0.0.49 → ok-script-0.0.50}/ok/util/win32_process.py +0 -0
  168. {ok-script-0.0.49 → ok-script-0.0.50}/ok/util/yaml.py +0 -0
  169. {ok-script-0.0.49 → ok-script-0.0.50}/ok_script.egg-info/dependency_links.txt +0 -0
  170. {ok-script-0.0.49 → ok-script-0.0.50}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ok-script
3
- Version: 0.0.49
3
+ Version: 0.0.50
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
@@ -21,6 +21,7 @@ Requires-Dist: PySide6-Fluent-Widgets>=1.5.6
21
21
  Requires-Dist: psutil>=5.9.8
22
22
  Requires-Dist: opencv-python>=4.9.0.80
23
23
  Requires-Dist: ok-d3dshot>=0.1.5
24
+ Requires-Dist: py7zr>=0.21.0
24
25
 
25
26
  # ok-script
26
27
 
@@ -1,12 +1,10 @@
1
1
  import threading
2
2
 
3
3
  import win32gui
4
- from adbutils import AdbError
5
4
 
6
5
  from ok.capture.HwndWindow import HwndWindow, find_hwnd_by_title_and_exe
7
6
  from ok.capture.adb.ADBCaptureMethod import ADBCaptureMethod, do_screencap
8
7
  from ok.capture.adb.WindowsCaptureFactory import update_capture_method
9
- from ok.capture.adb.vbox import installed_emulator
10
8
  from ok.capture.windows.window import get_window_bounds
11
9
  from ok.config.Config import Config
12
10
  from ok.gui.Communicate import communicate
@@ -36,8 +34,7 @@ class DeviceManager:
36
34
  "DeviceManager")
37
35
  self.thread = None
38
36
  self.capture_method = None
39
- self.update_pc_device()
40
- self.start()
37
+ self.refresh()
41
38
 
42
39
  def refresh(self):
43
40
  if self.thread is None or not self.thread.is_alive():
@@ -121,6 +118,7 @@ class DeviceManager:
121
118
  return
122
119
  if not fast:
123
120
  self.connect_all()
121
+ from ok.capture.adb.vbox import installed_emulator
124
122
  installed_emulators = [] if fast else installed_emulator()
125
123
  for device in self.adb.list():
126
124
  logger.debug(f'adb.list() {device}')
@@ -295,6 +293,7 @@ class DeviceManager:
295
293
  def shell(self, *args, **kwargs):
296
294
  device = self.device
297
295
  if device is not None:
296
+ from adbutils import AdbError
298
297
  try:
299
298
  return device.shell(*args, **kwargs)
300
299
  except AdbError as e:
@@ -13,6 +13,7 @@ from ok.gui.MessageWindow import MessageWindow
13
13
  from ok.gui.i18n.path import i18n_path
14
14
  from ok.gui.overlay.OverlayWindow import OverlayWindow
15
15
  from ok.logging.Logger import get_logger
16
+ from ok.update.Updater import Updater
16
17
 
17
18
  logger = get_logger(__name__)
18
19
 
@@ -32,6 +33,8 @@ class App:
32
33
  self.app.setAttribute(Qt.AA_DontCreateNativeWidgetSiblings)
33
34
  qconfig.theme = Theme.AUTO
34
35
 
36
+ self.updater = Updater(self.config, exit_event)
37
+
35
38
  self.about = self.config.get('about')
36
39
  self.title = self.config.get('gui_title')
37
40
  self.version = self.config.get('version')
@@ -75,8 +78,9 @@ class App:
75
78
  self.show_message_window(title, content)
76
79
 
77
80
  def show_main_window(self):
78
- self.main_window = MainWindow(self.icon, self.overlay, self.about, exit_event=self.exit_event)
79
- self.main_window.setWindowTitle(f'{self.title} {self.version}') # Set the window title here
81
+ self.main_window = MainWindow(self.icon, self.title, self.version, self.overlay, self.about,
82
+ exit_event=self.exit_event)
83
+ # Set the window title here
80
84
  self.main_window.setWindowIcon(self.icon)
81
85
  if self.overlay and ok.gui.device_manager.hwnd is not None:
82
86
  self.overlay_window = OverlayWindow(ok.gui.device_manager.hwnd)
@@ -20,6 +20,8 @@ class Communicate(QObject):
20
20
  config_validation: Signal = Signal(str)
21
21
  tab = Signal(str)
22
22
  capture_error = Signal()
23
+ check_update = Signal()
24
+ download_update = Signal(float, str, str, bool)
23
25
 
24
26
  def emit_draw_box(self, key: str = None, boxes=None, color=None, frame=None):
25
27
  self.draw_box.emit(key, boxes, color, frame)
@@ -20,7 +20,7 @@ class Communicate(QObject):
20
20
 
21
21
 
22
22
  class MainWindow(MSFluentWindow):
23
- def __init__(self, icon, debug=False, about=None, exit_event=None):
23
+ def __init__(self, icon, title, version, debug=False, about=None, exit_event=None):
24
24
  super().__init__()
25
25
  self.exit_event = exit_event
26
26
  self.start_tab = StartTab()
@@ -42,11 +42,11 @@ class MainWindow(MSFluentWindow):
42
42
  # self.addSubInterface(debug_tab, FluentIcon.COMMAND_PROMPT, self.tr('Debug'))
43
43
  # ... Add other tabs similarly
44
44
  if about:
45
- about_tab = AboutTab(about)
45
+ about_tab = AboutTab(icon, title, version, about)
46
46
  self.addSubInterface(about_tab, FluentIcon.QUESTION, self.tr('About'),
47
47
  position=NavigationItemPosition.BOTTOM)
48
48
  # Styling the tabs and content if needed, for example:
49
- self.setWindowTitle("Close Event Example")
49
+ self.setWindowTitle(f'{title} {version}')
50
50
 
51
51
  communicate.executor_paused.connect(self.executor_paused)
52
52
  communicate.tab.connect(self.navigate_tab)
@@ -0,0 +1,135 @@
1
+ from PySide6.QtCore import Qt
2
+ from PySide6.QtWidgets import QHBoxLayout, QLabel
3
+ from qfluentwidgets import TextEdit, PushButton, InfoBar, InfoBarPosition, ProgressBar
4
+
5
+ import ok.gui
6
+ from ok.capture.windows.dump import dump_threads
7
+ from ok.gui.Communicate import communicate
8
+ from ok.gui.about.VersionCard import VersionCard
9
+ from ok.gui.widget.Tab import Tab
10
+
11
+
12
+ class AboutTab(Tab):
13
+ def __init__(self, icon, title, version, about):
14
+ super().__init__()
15
+ self.version_card = VersionCard(icon, title, version, self)
16
+ # Create a QTextEdit instance
17
+ self.addWidget(self.version_card, align=Qt.AlignCenter)
18
+ if ok.gui.app.updater.enabled():
19
+ self.update_hbox = QHBoxLayout()
20
+ self.update_hbox.addStretch()
21
+ self.check_update_button = PushButton(self.tr("Check for updates"))
22
+ self.check_update_button.clicked.connect(self.check_update)
23
+ self.update_hbox.addWidget(self.check_update_button)
24
+ self.download_latest_button = PushButton()
25
+ self.download_latest_button.clicked.connect(self.download_latest)
26
+
27
+ self.update_hbox.addWidget(self.download_latest_button)
28
+ self.download_stable_button = PushButton(self.tr("Download stable version {version}").format(version=1))
29
+ self.download_stable_button.clicked.connect(self.download_stable)
30
+
31
+ self.update_hbox.addWidget(self.download_stable_button)
32
+
33
+ self.download_bar = ProgressBar(self)
34
+ self.download_bar.setFixedWidth(200)
35
+ self.update_hbox.addWidget(self.download_bar)
36
+
37
+ self.downloading_text = QLabel()
38
+ self.update_hbox.addWidget(self.downloading_text)
39
+
40
+ self.update_hbox.addStretch()
41
+ self.addLayout(self.update_hbox)
42
+ communicate.download_update.connect(self.download_update)
43
+ communicate.check_update.connect(self.update_update)
44
+ self.update_update_buttons(0)
45
+ text_edit = TextEdit()
46
+ text_edit.setHtml(about)
47
+ text_edit.setReadOnly(True)
48
+
49
+ # Set the layout on the widget
50
+ self.addWidget(text_edit)
51
+ if ok.gui.ok.debug:
52
+ dump_button = PushButton(self.tr("Dump Threads(HotKey:Ctrl+Alt+D)"))
53
+ dump_button.clicked.connect(dump_threads)
54
+ self.addWidget(dump_button)
55
+
56
+ def update_update_buttons(self, percent, progress=""):
57
+ if ok.gui.app.updater.latest_release and not ok.gui.app.updater.downloading:
58
+ self.download_latest_button.setText(self.tr("Download latest version {version} ({size})").format(
59
+ version=ok.gui.app.updater.latest_release.get('version'),
60
+ size=ok.gui.app.updater.latest_release.get('readable_size')))
61
+ self.download_latest_button.show()
62
+ else:
63
+ self.download_latest_button.hide()
64
+ if ok.gui.app.updater.stable_release and not ok.gui.app.updater.downloading:
65
+ self.download_stable_button.setText(self.tr("Download stable version {version} ({size})").format(
66
+ version=ok.gui.app.updater.stable_release.get('version'),
67
+ size=ok.gui.app.updater.stable_release.get('readable_size')))
68
+ self.download_stable_button.show()
69
+ else:
70
+ self.download_stable_button.hide()
71
+ if ok.gui.app.updater.downloading:
72
+ self.downloading_text.show()
73
+ self.downloading_text.setText(
74
+ self.tr('Downloading {progress} {percent}%').format(progress=progress, percent=format(percent, '.1f')))
75
+ self.download_bar.show()
76
+ self.download_bar.setValue(percent)
77
+ else:
78
+ self.downloading_text.hide()
79
+ self.download_bar.hide()
80
+
81
+ def download_update(self, percent, progress, done, error):
82
+ message = ""
83
+ if done:
84
+ if error:
85
+ bar = InfoBar.error
86
+ title = self.tr('Download Error:')
87
+ message = error
88
+ else:
89
+ bar = InfoBar.info
90
+ title = self.tr('Download Complete!')
91
+ bar(
92
+ title=title,
93
+ content=message,
94
+ orient=Qt.Horizontal,
95
+ isClosable=True,
96
+ position=InfoBarPosition.TOP,
97
+ duration=5000, # won't disappear automatically
98
+ parent=self
99
+ )
100
+ self.update_update_buttons(percent, progress)
101
+
102
+ def download_latest(self):
103
+ ok.gui.app.updater.async_run(lambda: ok.gui.app.updater.download(ok.gui.app.updater.latest_release))
104
+
105
+ def download_stable(self):
106
+ ok.gui.app.updater.async_run(lambda: ok.gui.app.updater.download(ok.gui.app.updater.stable_release))
107
+
108
+ def update_update(self):
109
+ self.check_update_button.setText(
110
+ self.tr("Check for update"))
111
+ self.check_update_button.setEnabled(True)
112
+ if ok.gui.app.updater.latest_release or ok.gui.app.updater.stable_release:
113
+ bar = InfoBar.info
114
+ message = self.tr('Found new version!')
115
+ elif ok.gui.app.updater.error:
116
+ bar = InfoBar.error
117
+ message = self.tr('Found new version!')
118
+ else:
119
+ bar = InfoBar.info
120
+ message = self.tr("This is the newest version!")
121
+ bar(
122
+ title="",
123
+ content=message,
124
+ orient=Qt.Horizontal,
125
+ isClosable=True,
126
+ position=InfoBarPosition.TOP,
127
+ duration=5000, # won't disappear automatically
128
+ parent=self
129
+ )
130
+ self.update_update_buttons(0)
131
+
132
+ def check_update(self):
133
+ ok.gui.app.updater.async_run(lambda: ok.gui.app.updater.check_for_updates())
134
+ self.check_update_button.setText(self.tr("Checking for updates"))
135
+ self.check_update_button.setEnabled(False)
@@ -0,0 +1,40 @@
1
+ from PySide6.QtCore import Qt
2
+ from PySide6.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout
3
+ from qfluentwidgets import IconWidget, TextWrap, CardWidget
4
+
5
+
6
+ class VersionCard(CardWidget):
7
+ """ Sample card """
8
+
9
+ def __init__(self, icon, title, version, parent=None):
10
+ super().__init__(parent=parent)
11
+
12
+ self.iconWidget = IconWidget(icon, self)
13
+ self.titleLabel = QLabel(title, self)
14
+ self.contentLabel = QLabel(TextWrap.wrap(version, 45, False)[0], self)
15
+
16
+ self.hBoxLayout = QHBoxLayout(self)
17
+ self.vBoxLayout = QVBoxLayout()
18
+
19
+ self.setFixedSize(360, 90)
20
+ self.iconWidget.setFixedSize(48, 48)
21
+
22
+ self.hBoxLayout.setSpacing(28)
23
+ self.hBoxLayout.setContentsMargins(20, 0, 0, 0)
24
+ self.vBoxLayout.setSpacing(2)
25
+ self.vBoxLayout.setContentsMargins(0, 0, 0, 0)
26
+ self.vBoxLayout.setAlignment(Qt.AlignVCenter)
27
+
28
+ self.hBoxLayout.setAlignment(Qt.AlignVCenter)
29
+ self.hBoxLayout.addWidget(self.iconWidget)
30
+ self.hBoxLayout.addLayout(self.vBoxLayout)
31
+ self.vBoxLayout.addStretch(1)
32
+ self.vBoxLayout.addWidget(self.titleLabel)
33
+ self.vBoxLayout.addWidget(self.contentLabel)
34
+ self.vBoxLayout.addStretch(1)
35
+
36
+ self.titleLabel.setObjectName('titleLabel')
37
+ self.contentLabel.setObjectName('contentLabel')
38
+
39
+ def mouseReleaseEvent(self, e):
40
+ super().mouseReleaseEvent(e)
@@ -9,20 +9,18 @@ class SelectCaptureListView(ListWidget):
9
9
 
10
10
  def update_for_device(self, device, hwnd, capture):
11
11
  if self.count() == 0:
12
- item = QListWidgetItem(self.tr(f"Game Window(Supports Background, Low Compatibility, Low Latency)"))
13
-
14
- # Add the QListWidgetItem to the QListWidget
12
+ item = QListWidgetItem(self.tr(f"Game Window"))
15
13
  self.addItem(item)
16
- tips = hwnd or self.tr("Supports Background, Low Compatibility, Low Latency")
14
+ tips = hwnd or ""
17
15
  if device == "windows":
18
16
  title = self.tr("Game Window")
19
17
  if self.count() == 2:
20
18
  self.takeItem(1)
21
19
  else:
22
- title = self.tr("Emulator Window")
20
+ title = self.tr("Emulator Window(Supports Background, Fast, Low Latency)")
23
21
  if self.count() == 1:
24
- item = QListWidgetItem(self.tr("ADB (Supports Background, High Compatibility, High Latency)"))
22
+ item = QListWidgetItem(self.tr("ADB(Supports Background, Slow, High Compatibility, High Latency)"))
25
23
  self.addItem(item)
26
- self.item(0).setText(f"{title}({tips})")
24
+ self.item(0).setText(f"{title} - {tips}")
27
25
  selected = 0 if capture == "windows" else 1
28
26
  self.setCurrentRow(selected)
@@ -29,9 +29,13 @@ class Tab(ScrollArea):
29
29
 
30
30
  def addCard(self, title, widget, stretch=0, parent=None):
31
31
  container = Card(title, widget)
32
- self.vBoxLayout.addWidget(container, stretch, Qt.AlignTop)
32
+ self.addWidget(container, stretch)
33
33
  return container
34
34
 
35
- def addWidget(self, widget, stretch=0):
36
- self.vBoxLayout.addWidget(widget, stretch, Qt.AlignTop)
35
+ def addWidget(self, widget, stretch=0, align=Qt.AlignTop):
36
+ self.vBoxLayout.addWidget(widget, stretch, align)
37
37
  return widget
38
+
39
+ def addLayout(self, layout, stretch=0):
40
+ self.vBoxLayout.addLayout(layout, stretch)
41
+ return layout
@@ -3,6 +3,7 @@ import time
3
3
  import traceback
4
4
 
5
5
  from ok.capture.BaseCaptureMethod import CaptureException
6
+ from ok.capture.adb.ADBCaptureMethod import ADBCaptureMethod
6
7
  from ok.capture.adb.DeviceManager import DeviceManager
7
8
  from ok.gui.Communicate import communicate
8
9
  from ok.logging.Logger import get_logger
@@ -73,6 +74,7 @@ class TaskExecutor:
73
74
  return self._frame
74
75
 
75
76
  def supports_screen_ratio(self, supported_ratio):
77
+ frame = self.method.get_frame() if not isinstance(self.method, ADBCaptureMethod) else None
76
78
  if supported_ratio is None:
77
79
  return True, None
78
80
  width = self.method.width
@@ -88,9 +90,13 @@ class TaskExecutor:
88
90
 
89
91
  # Calculate the difference between the actual and supported ratios
90
92
  difference = abs(actual_ratio - supported_ratio)
91
-
93
+ support = difference <= 0.01 * supported_ratio
94
+ if not support:
95
+ logger.error(f'resolution error {width}x{height} {frame is None}')
96
+ if not support and frame:
97
+ communicate.screenshot.emit(frame, "resolution_error")
92
98
  # Check if the difference is within 1%
93
- return difference <= 0.01 * supported_ratio, f"{width}x{height}"
99
+ return support, f"{width}x{height}"
94
100
 
95
101
  def can_capture(self):
96
102
  return self.method is not None and self.interaction is not None and self.interaction.should_capture()
@@ -0,0 +1,251 @@
1
+ import math
2
+ import os
3
+ import queue
4
+ import threading
5
+ import zipfile
6
+ from datetime import datetime
7
+
8
+ import py7zr
9
+ import requests
10
+
11
+ from ok.gui.Communicate import communicate
12
+ from ok.logging.Logger import get_logger
13
+ from ok.util.path import get_path_relative_to_exe, ensure_dir, dir_checksum, find_folder_with_file
14
+
15
+ logger = get_logger(__name__)
16
+
17
+ updater_bat = r'''@echo off
18
+ set "source_folder=%~1"
19
+ set "target_folder=%~2"
20
+
21
+ for /R "%source_folder%" %%F in (*) do (
22
+ if exist "%target_folder%\%%~nxF" (
23
+ echo Deleting "%target_folder%\%%~nxF"
24
+ del /F /Q "%target_folder%\%%~nxF"
25
+ )
26
+ echo Copying "%%F"
27
+ copy /Y "%%F" "%target_folder%\%%~nxF"
28
+ )
29
+ '''
30
+
31
+
32
+ class Updater:
33
+ def __init__(self, app_config, exit_event):
34
+ self.config = app_config.get('update')
35
+ self.version = app_config.get('version')
36
+ self.debug = app_config.get('debug')
37
+ self.latest_release = None
38
+ self.stable_release = None
39
+ self.error = None
40
+ self.downloading = False
41
+ if self.config is None:
42
+ return
43
+ self.update_url = self.config.get('releases_url')
44
+ self.proxy_url = self.config.get('proxy_url')
45
+ self.use_proxy = self.config.get('use_proxy')
46
+ self.update_dir = get_path_relative_to_exe('updates')
47
+ self.exit_event = exit_event
48
+ self.task_queue = queue.Queue()
49
+ self.exit_event.bind_queue(self.task_queue)
50
+ t = threading.Thread(target=self._run_tasks, name='run_tasks')
51
+ t.start()
52
+
53
+ def _run_tasks(self):
54
+ while True and not self.exit_event.is_set():
55
+ task = self.task_queue.get()
56
+ if task is None:
57
+ logger.debug("Task queue get is None quit")
58
+ break
59
+ task()
60
+ self.task_queue.task_done()
61
+
62
+ def get_url(self, url):
63
+ if self.use_proxy:
64
+ url = self.proxy_url + url
65
+ logger.info(f'get_url: {url}')
66
+ return url
67
+
68
+ def check_for_updates(self):
69
+ try:
70
+ # Send GET request to the API endpoint
71
+ response = requests.get(self.get_url(self.update_url))
72
+
73
+ # Raise an exception if the request was unsuccessful
74
+ response.raise_for_status()
75
+
76
+ # Parse the JSON response to a dictionary
77
+ releases = response.json()
78
+
79
+ # Extract useful information from each release
80
+ self.latest_release = None
81
+ self.stable_release = None
82
+ self.error = None
83
+ for release in releases:
84
+ release = self.parse_data(release)
85
+
86
+ if not release.get('draft') and is_newer_version(self.version, release.get('version')):
87
+ if self.latest_release is None:
88
+ self.latest_release = release
89
+ if not release.get('prerelease') and self.stable_release is None:
90
+ self.stable_release = release
91
+
92
+ if self.latest_release and self.stable_release:
93
+ break
94
+
95
+ if self.latest_release == self.stable_release:
96
+ self.latest_release = None
97
+
98
+ except Exception as e:
99
+ print(f"HTTP error occurred: {e}")
100
+ communicate.check_update.emit()
101
+
102
+ def async_run(self, task):
103
+ self.task_queue.put(task)
104
+
105
+ def download(self, release):
106
+ try:
107
+ ensure_dir(self.update_dir)
108
+ file = os.path.join(self.update_dir, release.get('version') + '.' + release.get('type'))
109
+ downloaded = 0
110
+ size = release.get('size')
111
+ if os.path.exists(file):
112
+ downloaded = os.path.getsize(file)
113
+ if downloaded == size:
114
+ logger.info(f'File {file} already downloaded')
115
+ self.extract(file)
116
+ return
117
+ elif downloaded > size:
118
+ logger.warning(f'File {file} already downloaded greater than target')
119
+ os.remove(file)
120
+ downloaded = 0
121
+
122
+ headers = {'Range': f'bytes={downloaded}-'}
123
+ response = requests.get(self.get_url(release.get('url')), headers=headers, stream=True)
124
+ response.raise_for_status()
125
+ logger.info(f'download update to: {file} downloaded {convert_size(downloaded)}')
126
+ self.downloading = True
127
+ communicate.download_update.emit(0, "", False, None)
128
+ with open(file, 'ab') as f:
129
+ for chunk in response.iter_content(chunk_size=1024): # 1 KB chunks
130
+ if self.exit_event.is_set():
131
+ return
132
+ if chunk:
133
+ downloaded += len(chunk)
134
+ percent = downloaded / size * 100
135
+ # Every 1%
136
+ communicate.download_update.emit(percent,
137
+ convert_size(downloaded) + '/' + convert_size(size), False,
138
+ None
139
+ )
140
+ f.write(chunk) # Write chunk to file
141
+ self.downloading = False
142
+ logger.info(f'download success: {file}')
143
+ self.extract(file)
144
+ return
145
+ except Exception as e:
146
+ logger.error('download error occurred', e)
147
+ communicate.download_update.emit(0, "", True, e.args[0])
148
+
149
+ def extract(self, file):
150
+ communicate.download_update.emit(100, "Extracting", False, None)
151
+ folder, extension = file.rsplit('.', 1)
152
+ ensure_dir(folder)
153
+ if extension == '7z':
154
+ with py7zr.SevenZipFile(file, mode='r') as z:
155
+ z.extractall(folder)
156
+ elif extension == 'zip':
157
+ with zipfile.ZipFile(file, 'r') as z:
158
+ z.extractall(folder)
159
+
160
+ updater_root_folder = find_folder_with_file(folder, 'md5.txt')
161
+
162
+ if updater_root_folder is None:
163
+ logger.error('check_package_error no md5.txt found')
164
+ self.check_package_error()
165
+ return
166
+ correct_md5_file = os.path.join(updater_root_folder, 'md5.txt')
167
+ try:
168
+ with open(correct_md5_file, 'r') as file:
169
+ # Read the content of the file
170
+ correct_md5 = file.read()
171
+ except Exception as e:
172
+ logger.error(f'check_package_error md5.txt not found {correct_md5_file}', e)
173
+ self.check_package_error()
174
+ return
175
+
176
+ md5 = dir_checksum(updater_root_folder, ['md5.txt'])
177
+ if md5 != correct_md5:
178
+ logger.error(f'check_package_error md5.txt mismatch {correct_md5_file} != {md5}')
179
+ self.check_package_error()
180
+ return
181
+ logger.info(f'extract {file} md5: {md5}')
182
+
183
+ # Open the file in write mode ('w')
184
+ try:
185
+ with open(get_path_relative_to_exe('update.bat'), 'w') as file:
186
+ # Write a string to the file
187
+ file.write(updater_bat)
188
+ logger.info('write update .bat')
189
+ except Exception as e:
190
+ logger.error(f'write updater_bat error', e)
191
+ self.check_package_error()
192
+ return
193
+
194
+ # Now, 'Hello, World!' is written to 'filename.txt'
195
+
196
+ communicate.download_update.emit(100, "", True, None)
197
+
198
+ def check_package_error(self):
199
+ # clear_folder(self.update_dir)
200
+ communicate.download_update.emit(100, "", True, "Update Package CheckSum Error!")
201
+
202
+ def enabled(self):
203
+ return self.config is not None
204
+
205
+ def parse_data(self, release):
206
+ version = release.get('tag_name')
207
+ time = datetime.strptime(release.get('published_at'), "%Y-%m-%dT%H:%M:%SZ").strftime('%Y-%m-%d %H:%M:%S')
208
+ target_asset = None
209
+ for asset in release.get('assets'):
210
+ asset = {
211
+ 'name': asset.get('name'),
212
+ 'url': asset.get('browser_download_url'),
213
+ 'size': asset.get('size'),
214
+ 'readable_size': convert_size(asset.get('size'))
215
+ }
216
+ asset['type'] = asset['url'].rsplit('.', 1)[1]
217
+ url = asset.get('url')
218
+ if version in url:
219
+ if 'debug' in url and self.debug:
220
+ target_asset = asset
221
+ elif 'release' in url and not self.debug:
222
+ target_asset = asset
223
+ if not target_asset:
224
+ raise ValueError(f'No asset found for version: {version}')
225
+ release = {
226
+ 'version': version,
227
+ 'notes': release.get('body'),
228
+ 'time': time,
229
+ 'draft': release.get('draft'),
230
+ 'prerelease': release.get('prerelease'),
231
+ }
232
+ release.update(target_asset)
233
+ return release
234
+
235
+
236
+ def is_newer_version(base_version, target_version):
237
+ base_version = list(map(int, base_version[1:].split('.')))
238
+ target_version = list(map(int, target_version[1:].split('.')))
239
+
240
+ # Compare the versions
241
+ return base_version < target_version
242
+
243
+
244
+ def convert_size(size_bytes):
245
+ if size_bytes == 0:
246
+ return "0B"
247
+ size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
248
+ i = int(math.floor(math.log(size_bytes, 1024)))
249
+ p = math.pow(1024, i)
250
+ s = round(size_bytes / p, 2)
251
+ return f"{s} {size_name[i]}"
@@ -0,0 +1,23 @@
1
+ import os.path
2
+ import sys
3
+
4
+ from ok.util.path import dir_checksum
5
+
6
+
7
+ def write_checksum_to_file(folder_path):
8
+ # Call the dir_checksum function
9
+ checksum = dir_checksum(folder_path)
10
+
11
+ # Write the checksum to a file named 'md5.txt' in the same folder
12
+ file = os.path.join(folder_path, 'md5.txt')
13
+ with open(file, 'w') as file:
14
+ file.write(checksum)
15
+ print(f'write checksum {checksum} to {file}')
16
+
17
+
18
+ if __name__ == "__main__":
19
+ # Get the folder path from the command line arguments
20
+ folder_path = sys.argv[1]
21
+
22
+ # Call the function
23
+ write_checksum_to_file(folder_path)
File without changes