ok-script 0.0.91__tar.gz → 0.0.94__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 (184) hide show
  1. {ok-script-0.0.91 → ok-script-0.0.94}/PKG-INFO +1 -1
  2. {ok-script-0.0.91 → ok-script-0.0.94}/ok/OK.py +24 -0
  3. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/adb/DeviceManager.py +3 -3
  4. ok-script-0.0.94/ok/capture/windows/dump.py +83 -0
  5. ok-script-0.0.94/ok/feature/CompressCoco.py +61 -0
  6. {ok-script-0.0.91 → ok-script-0.0.94}/ok/feature/Feature.py +10 -4
  7. {ok-script-0.0.91 → ok-script-0.0.94}/ok/feature/FeatureSet.py +73 -58
  8. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/App.py +10 -9
  9. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/MainWindow.py +4 -16
  10. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/StartController.py +1 -1
  11. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/debug/DebugTab.py +19 -17
  12. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/debug/LogWindow.py +23 -24
  13. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/resources.py +433 -432
  14. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/start/StartCard.py +9 -0
  15. {ok-script-0.0.91 → ok-script-0.0.94}/ok/task/TaskExecutor.py +10 -7
  16. {ok-script-0.0.91 → ok-script-0.0.94}/ok/update/GithubMultiDownloader.py +48 -11
  17. {ok-script-0.0.91 → ok-script-0.0.94}/ok/update/Updater.py +14 -5
  18. {ok-script-0.0.91 → ok-script-0.0.94}/ok/util/Handler.py +1 -0
  19. {ok-script-0.0.91 → ok-script-0.0.94}/ok_script.egg-info/PKG-INFO +1 -1
  20. {ok-script-0.0.91 → ok-script-0.0.94}/setup.py +1 -1
  21. ok-script-0.0.91/ok/capture/windows/dump.py +0 -31
  22. ok-script-0.0.91/ok/feature/CompressCoco.py +0 -57
  23. {ok-script-0.0.91 → ok-script-0.0.94}/README.md +0 -0
  24. {ok-script-0.0.91 → ok-script-0.0.94}/ok/__init__.py +0 -0
  25. {ok-script-0.0.91 → ok-script-0.0.94}/ok/alas/__init__.py +0 -0
  26. {ok-script-0.0.91 → ok-script-0.0.94}/ok/alas/emulator_base.py +0 -0
  27. {ok-script-0.0.91 → ok-script-0.0.94}/ok/alas/emulator_windows.py +0 -0
  28. {ok-script-0.0.91 → ok-script-0.0.94}/ok/alas/platform_base.py +0 -0
  29. {ok-script-0.0.91 → ok-script-0.0.94}/ok/alas/platform_windows.py +0 -0
  30. {ok-script-0.0.91 → ok-script-0.0.94}/ok/alas/utils.py +0 -0
  31. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/BaseCaptureMethod.py +0 -0
  32. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/HwndWindow.py +0 -0
  33. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/__init__.py +0 -0
  34. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/adb/ADBCaptureMethod.py +0 -0
  35. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/adb/WindowsCaptureFactory.py +0 -0
  36. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/adb/__init__.py +0 -0
  37. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/adb/bin/__init__.py +0 -0
  38. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/image/ImageCaptureMethod.py +0 -0
  39. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/image/__init__.py +0 -0
  40. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/windows/BaseWindowsCaptureMethod.py +0 -0
  41. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/windows/BitBltCaptureMethod.py +0 -0
  42. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/windows/DesktopDuplicationCaptureMethod.py +0 -0
  43. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/windows/WindowsGraphicsCaptureMethod.py +0 -0
  44. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/windows/__init__.py +0 -0
  45. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/windows/d3d11.py +0 -0
  46. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/windows/utils.py +0 -0
  47. {ok-script-0.0.91 → ok-script-0.0.94}/ok/capture/windows/window.py +0 -0
  48. {ok-script-0.0.91 → ok-script-0.0.94}/ok/color/Color.py +0 -0
  49. {ok-script-0.0.91 → ok-script-0.0.94}/ok/color/__init__.py +0 -0
  50. {ok-script-0.0.91 → ok-script-0.0.94}/ok/config/Config.py +0 -0
  51. {ok-script-0.0.91 → ok-script-0.0.94}/ok/config/InfoDict.py +0 -0
  52. {ok-script-0.0.91 → ok-script-0.0.94}/ok/config/__init__.py +0 -0
  53. {ok-script-0.0.91 → ok-script-0.0.94}/ok/feature/Box.py +0 -0
  54. {ok-script-0.0.91 → ok-script-0.0.94}/ok/feature/FindFeature.py +0 -0
  55. {ok-script-0.0.91 → ok-script-0.0.94}/ok/feature/__init__.py +0 -0
  56. {ok-script-0.0.91 → ok-script-0.0.94}/ok/firebase/Analytics.py +0 -0
  57. {ok-script-0.0.91 → ok-script-0.0.94}/ok/firebase/__init__.py +0 -0
  58. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/Communicate.py +0 -0
  59. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/MessageWindow.py +0 -0
  60. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/TabTitles.py +0 -0
  61. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/__init__.py +0 -0
  62. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/about/AboutTab.py +0 -0
  63. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/about/HttpTextEdit.py +0 -0
  64. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/about/VersionCard.py +0 -0
  65. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/about/__init__.py +0 -0
  66. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/common/OKIcon.py +0 -0
  67. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/common/__init__.py +0 -0
  68. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/common/config.py +0 -0
  69. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/common/style_sheet.py +0 -0
  70. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/debug/AspectRatioWidget.py +0 -0
  71. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/debug/FrameWidget.py +0 -0
  72. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/debug/InfoWidget.py +0 -0
  73. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/debug/Screenshot.py +0 -0
  74. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/debug/__init__.py +0 -0
  75. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/i18n/__init__.py +0 -0
  76. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/i18n/path.py +0 -0
  77. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/i18n/update.py +0 -0
  78. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/icon/__init__.py +0 -0
  79. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/icon/icon.py +0 -0
  80. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/overlay/OverlayWindow.py +0 -0
  81. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/overlay/__init__.py +0 -0
  82. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/qss/__init__.py +0 -0
  83. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/settings/SettingTab.py +0 -0
  84. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/settings/__init__.py +0 -0
  85. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/start/SelectCaptureListView.py +0 -0
  86. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/start/StartTab.py +0 -0
  87. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/start/__init__.py +0 -0
  88. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/ConfigItemFactory.py +0 -0
  89. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/ConfigLabelAndWidget.py +0 -0
  90. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/LabelAndDoubleSpinBox.py +0 -0
  91. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/LabelAndDropDown.py +0 -0
  92. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/LabelAndLineEdit.py +0 -0
  93. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/LabelAndList.py +0 -0
  94. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/LabelAndSpinBox.py +0 -0
  95. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/LabelAndSwitchButton.py +0 -0
  96. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/LabelAndWidget.py +0 -0
  97. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/ModifyListDialog.py +0 -0
  98. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/ModifyListItem.py +0 -0
  99. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/OneTimeTaskTab.py +0 -0
  100. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/StartButton.py +0 -0
  101. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/TaskCard.py +0 -0
  102. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/TaskOpButton.py +0 -0
  103. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/TooltipTableWidget.py +0 -0
  104. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/TriggerTaskTab.py +0 -0
  105. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/tasks/__init__.py +0 -0
  106. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/util/Alert.py +0 -0
  107. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/util/InitWorker.py +0 -0
  108. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/util/__init__.py +0 -0
  109. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/Card.py +0 -0
  110. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/EmptyCard.py +0 -0
  111. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/FramelessDialog.py +0 -0
  112. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/ListTableWidgetItem.py +0 -0
  113. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/NumericTableWidgetItem.py +0 -0
  114. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/StartLoadingDialog.py +0 -0
  115. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/StatusBar.py +0 -0
  116. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/Tab.py +0 -0
  117. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/TabWidget.py +0 -0
  118. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/UpdateConfigWidgetItem.py +0 -0
  119. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/YesNonWidgetItem.py +0 -0
  120. {ok-script-0.0.91 → ok-script-0.0.94}/ok/gui/widget/__init__.py +0 -0
  121. {ok-script-0.0.91 → ok-script-0.0.94}/ok/interaction/ADBInteraction.py +0 -0
  122. {ok-script-0.0.91 → ok-script-0.0.94}/ok/interaction/BaseInteraction.py +0 -0
  123. {ok-script-0.0.91 → ok-script-0.0.94}/ok/interaction/DoNothingInteraction.py +0 -0
  124. {ok-script-0.0.91 → ok-script-0.0.94}/ok/interaction/Win32Interaction.py +0 -0
  125. {ok-script-0.0.91 → ok-script-0.0.94}/ok/interaction/__init__.py +0 -0
  126. {ok-script-0.0.91 → ok-script-0.0.94}/ok/logging/Logger.py +0 -0
  127. {ok-script-0.0.91 → ok-script-0.0.94}/ok/logging/__init__.py +0 -0
  128. {ok-script-0.0.91 → ok-script-0.0.94}/ok/ocr/OCR.py +0 -0
  129. {ok-script-0.0.91 → ok-script-0.0.94}/ok/ocr/__init__.py +0 -0
  130. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
  131. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
  132. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
  133. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
  134. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
  135. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
  136. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
  137. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
  138. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
  139. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Media/__init__.py +0 -0
  140. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
  141. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Security/__init__.py +0 -0
  142. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
  143. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/Storage/__init__.py +0 -0
  144. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/Windows/__init__.py +0 -0
  145. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/__init__.py +0 -0
  146. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/delegate.py +0 -0
  147. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/export.py +0 -0
  148. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/idldsl.py +0 -0
  149. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/inspectable.py +0 -0
  150. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/roapi.py +0 -0
  151. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/types.py +0 -0
  152. {ok-script-0.0.91 → ok-script-0.0.94}/ok/rotypes/winstring.py +0 -0
  153. {ok-script-0.0.91 → ok-script-0.0.94}/ok/save/BlackBarProcessor.py +0 -0
  154. {ok-script-0.0.91 → ok-script-0.0.94}/ok/save/PostProcessor.py +0 -0
  155. {ok-script-0.0.91 → ok-script-0.0.94}/ok/save/SaveByInterval.py +0 -0
  156. {ok-script-0.0.91 → ok-script-0.0.94}/ok/save/SaveByKeyPress.py +0 -0
  157. {ok-script-0.0.91 → ok-script-0.0.94}/ok/save/SaveMethodBase.py +0 -0
  158. {ok-script-0.0.91 → ok-script-0.0.94}/ok/save/__init__.py +0 -0
  159. {ok-script-0.0.91 → ok-script-0.0.94}/ok/scene/FeatureScene.py +0 -0
  160. {ok-script-0.0.91 → ok-script-0.0.94}/ok/scene/Scene.py +0 -0
  161. {ok-script-0.0.91 → ok-script-0.0.94}/ok/scene/__init__.py +0 -0
  162. {ok-script-0.0.91 → ok-script-0.0.94}/ok/stats/StreamStats.py +0 -0
  163. {ok-script-0.0.91 → ok-script-0.0.94}/ok/stats/__init__.py +0 -0
  164. {ok-script-0.0.91 → ok-script-0.0.94}/ok/task/BaseTask.py +0 -0
  165. {ok-script-0.0.91 → ok-script-0.0.94}/ok/task/ExecutorOperation.py +0 -0
  166. {ok-script-0.0.91 → ok-script-0.0.94}/ok/task/TriggerTask.py +0 -0
  167. {ok-script-0.0.91 → ok-script-0.0.94}/ok/task/__init__.py +0 -0
  168. {ok-script-0.0.91 → ok-script-0.0.94}/ok/update/__init__.py +0 -0
  169. {ok-script-0.0.91 → ok-script-0.0.94}/ok/update/gen_md5.py +0 -0
  170. {ok-script-0.0.91 → ok-script-0.0.94}/ok/util/__init__.py +0 -0
  171. {ok-script-0.0.91 → ok-script-0.0.94}/ok/util/exit_event.py +0 -0
  172. {ok-script-0.0.91 → ok-script-0.0.94}/ok/util/json.py +0 -0
  173. {ok-script-0.0.91 → ok-script-0.0.94}/ok/util/list.py +0 -0
  174. {ok-script-0.0.91 → ok-script-0.0.94}/ok/util/path.py +0 -0
  175. {ok-script-0.0.91 → ok-script-0.0.94}/ok/util/thread.py +0 -0
  176. {ok-script-0.0.91 → ok-script-0.0.94}/ok/util/win32_process.py +0 -0
  177. {ok-script-0.0.91 → ok-script-0.0.94}/ok/util/yaml.py +0 -0
  178. {ok-script-0.0.91 → ok-script-0.0.94}/ok_script.egg-info/SOURCES.txt +0 -0
  179. {ok-script-0.0.91 → ok-script-0.0.94}/ok_script.egg-info/dependency_links.txt +0 -0
  180. {ok-script-0.0.91 → ok-script-0.0.94}/ok_script.egg-info/requires.txt +0 -0
  181. {ok-script-0.0.91 → ok-script-0.0.94}/ok_script.egg-info/top_level.txt +0 -0
  182. {ok-script-0.0.91 → ok-script-0.0.94}/setup.cfg +0 -0
  183. {ok-script-0.0.91 → ok-script-0.0.94}/tests/__init__.py +0 -0
  184. {ok-script-0.0.91 → ok-script-0.0.94}/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.91
3
+ Version: 0.0.94
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
@@ -37,6 +37,9 @@ class OK:
37
37
  config['debug'] = config.get("debug", False)
38
38
  self.debug = config['debug']
39
39
  try:
40
+ if self.debug:
41
+ import win32api
42
+ win32api.SetConsoleCtrlHandler(self.console_handler, True)
40
43
  self.config = config
41
44
  self.init_device_manager()
42
45
  from ok.gui.debug.Screenshot import Screenshot
@@ -59,6 +62,7 @@ class OK:
59
62
  if self.config.get("use_gui"):
60
63
  if not self.init_error:
61
64
  self.app.show_main_window()
65
+ logger.debug('start app.exec()')
62
66
  self.app.exec()
63
67
  else:
64
68
  self.task_executor.start()
@@ -135,6 +139,26 @@ class OK:
135
139
  while not self.exit_event.is_set():
136
140
  time.sleep(1)
137
141
 
142
+ def console_handler(self, event):
143
+ import win32con
144
+ if event == win32con.CTRL_C_EVENT:
145
+ logger.info("CTRL+C event dump threads")
146
+ from ok.capture.windows.dump import dump_threads
147
+ dump_threads()
148
+ self.quit()
149
+ elif event == win32con.CTRL_CLOSE_EVENT:
150
+ logger.info("Close event quit")
151
+ self.quit()
152
+ elif event == win32con.CTRL_LOGOFF_EVENT:
153
+ logger.info("Logoff event quit")
154
+ self.quit()
155
+ elif event == win32con.CTRL_SHUTDOWN_EVENT:
156
+ logger.info("Shutdown event quit")
157
+ self.quit()
158
+ else: # Perform clean-up tasks here
159
+ print("Performing clean-up...")
160
+ return True
161
+
138
162
  def quit(self):
139
163
  logger.debug('quit app')
140
164
  self.exit_event.set()
@@ -67,7 +67,7 @@ class DeviceManager:
67
67
  logger.debug(f'adb_connect already connected {addr}')
68
68
  return device
69
69
  if try_connect:
70
- self.adb.connect(addr, timeout=5)
70
+ self.adb.connect(addr, timeout=3)
71
71
  logger.debug(f'adb_connect {addr}')
72
72
  return self.adb_connect(addr, try_connect=False)
73
73
  except AdbTimeout as e:
@@ -116,8 +116,8 @@ class DeviceManager:
116
116
  if imei is not None:
117
117
  preferred = self.get_preferred_device()
118
118
  if current and preferred is not None and preferred['imei'] != imei:
119
- logger.debug(f"refresh current only skip others {preferred['imei']} != imei")
120
- break
119
+ logger.debug(f"refresh current only skip others {preferred['imei']} != {imei}")
120
+ continue
121
121
  found = False
122
122
  for device in self.device_dict.values():
123
123
  if device.get('adb_imei') == imei:
@@ -0,0 +1,83 @@
1
+ import os
2
+ import signal
3
+ import sys
4
+ import threading
5
+ import traceback
6
+
7
+ from ok.util.path import get_path_relative_to_exe
8
+
9
+
10
+ def get_thread_name(thread_id):
11
+ for thread in threading.enumerate():
12
+ if thread.ident == thread_id:
13
+ return thread.name
14
+ return "" # Return empty string if the thread name is not found
15
+
16
+
17
+ def dump_threads():
18
+ # Get the stack trace for each thread
19
+ thread_dumps = []
20
+ for thread_id, frame in sys._current_frames().items():
21
+ thread_name = get_thread_name(thread_id)
22
+ thread_dump = f"Stack for thread {thread_id} (Name: {thread_name}):\n"
23
+ thread_dump += "".join(traceback.format_stack(frame))
24
+ thread_dumps.append(thread_dump)
25
+
26
+ # Write the thread dumps to a file
27
+ output_file = get_path_relative_to_exe(os.path.join('logs', "thread_dumps.txt"))
28
+ print(f'Dumping threads to {output_file}')
29
+
30
+ with open(output_file, "w") as file:
31
+ # Open the file with UTF-8 encoding
32
+ with open('file.txt', 'w', encoding='utf-8') as f:
33
+ f.write("\n\n".join(thread_dumps))
34
+
35
+ # subprocess.Popen(r'explorer /select,"{}"'.format(output_file))
36
+
37
+
38
+ def kill_dump():
39
+ print("Killing dump")
40
+ dump_threads()
41
+ sys.exit(0)
42
+
43
+
44
+ def console_handler(event):
45
+ import win32con
46
+ if event == win32con.CTRL_C_EVENT:
47
+ print("CTRL+C event")
48
+ dump_threads()
49
+ sys.exit(0)
50
+ elif event == win32con.CTRL_CLOSE_EVENT:
51
+ print("Close event")
52
+ elif event == win32con.CTRL_LOGOFF_EVENT:
53
+ print("Logoff event")
54
+ elif event == win32con.CTRL_SHUTDOWN_EVENT:
55
+ print("Shutdown event")
56
+ # Perform clean-up tasks here
57
+ print("Performing clean-up...")
58
+ # sys.exit(0) # Exit the program
59
+ return True
60
+
61
+
62
+ if __name__ == '__main__':
63
+
64
+ import psutil
65
+ import faulthandler
66
+
67
+ # Enable the fault handler
68
+ faulthandler.enable()
69
+
70
+ # Iterate over all running processes
71
+ for proc in psutil.process_iter(['pid', 'name', 'status', 'exe']):
72
+ # Check if the process is unresponsive
73
+ # print(proc.info['exe'])
74
+ if proc.info['name'] == 'python.exe':
75
+ # Dump the traceback
76
+ print(f'dump {proc.pid} {proc.cmdline()} {proc.info["pid"] == os.getpid()}')
77
+ # faulthandler.dump_traceback(file=sys.stderr, all_threads=True)
78
+ # Kill the process
79
+ # os.kill(proc.info['pid'], signal.SIGTERM)
80
+
81
+ # faulthandler.dump_traceback(file=sys.stderr, all_threads=True)
82
+ # Kill the process
83
+ os.kill(16916, signal.SIGTERM)
@@ -0,0 +1,61 @@
1
+ import json
2
+ import os
3
+ import sys
4
+
5
+ import cv2
6
+ from PIL import Image
7
+
8
+ from ok.feature.FeatureSet import read_from_json
9
+
10
+
11
+ def compress_coco(coco_json) -> None:
12
+ feature_dict, *_ = read_from_json(coco_json)
13
+ with open(coco_json, 'r') as file:
14
+ image_dict = {}
15
+ data = json.load(file)
16
+ coco_folder = os.path.dirname(coco_json)
17
+ image_map = {image['id']: image['file_name'] for image in data['images']}
18
+ category_map = {category['id']: category['name'] for category in data['categories']}
19
+
20
+ for annotation in data['annotations']:
21
+ image_id = annotation['image_id']
22
+ category_id = annotation['category_id']
23
+
24
+ feature = feature_dict.get(category_map[category_id])
25
+ if feature:
26
+ # Load and scale the image
27
+ image_path = str(os.path.join(coco_folder, image_map[image_id]))
28
+ image_features = image_dict.get(image_path, [])
29
+ image_features.append(feature)
30
+ image_dict[image_path] = image_features
31
+
32
+ # Loop through the image_dict and write all the image_feature associated with it in a new PNG
33
+ for image_path, features in image_dict.items():
34
+ background = None
35
+ for feature in features:
36
+ # Create a white background
37
+ if not background:
38
+ original_image = cv2.imread(image_path)
39
+ background = Image.new('RGBA', (original_image.shape[1], original_image.shape[0]),
40
+ (255, 255, 255, 255))
41
+ # Convert the OpenCV image (numpy array) to a PIL image
42
+ image = Image.fromarray(cv2.cvtColor(feature.mat, cv2.COLOR_BGR2RGBA))
43
+ background.paste(image, (feature.x, feature.y), image)
44
+ # Save the image with compression level 9
45
+ # background.save(image_path, 'PNG', optimize=True, compress_level=9)
46
+
47
+ # Add metadata
48
+ metadata = {
49
+ "ok_compressed": "1"
50
+ }
51
+
52
+ for key, value in metadata.items():
53
+ background.info[key] = value
54
+
55
+ # Save the image with metadata
56
+ background.save(image_path, 'PNG', optimize=True)
57
+
58
+
59
+ if __name__ == '__main__':
60
+ json_file = sys.argv[1]
61
+ compress_coco(json_file)
@@ -14,10 +14,16 @@ class Feature:
14
14
  height (int): The height of the bounding box.
15
15
  """
16
16
  self.mat = mat
17
- self.x = x
18
- self.y = y
19
- self.width = width
20
- self.height = height
17
+ self.x = round(x)
18
+ self.y = round(y)
19
+
20
+ @property
21
+ def width(self):
22
+ return self.mat.shape[1]
23
+
24
+ @property
25
+ def height(self):
26
+ return self.mat.shape[0]
21
27
 
22
28
  def __str__(self) -> str:
23
29
  return f'self.x: {self.x}, self.y: {self.y}, width: {self.width}, height: {self.height}'
@@ -7,6 +7,7 @@ from typing import List
7
7
 
8
8
  import cv2
9
9
  import numpy as np
10
+ from PIL import Image
10
11
 
11
12
  from ok.feature.Box import Box, sort_boxes
12
13
  from ok.feature.Feature import Feature
@@ -33,7 +34,6 @@ class FeatureSet:
33
34
  height (int): Scale images to this height.
34
35
  """
35
36
  self.coco_json = resource_path(coco_json)
36
- self.coco_folder = os.path.dirname(self.coco_json)
37
37
  self.debug = debug
38
38
 
39
39
  logger.debug(f'Loading features from {self.coco_json}')
@@ -67,59 +67,12 @@ class FeatureSet:
67
67
  width (int): Target width for scaling images.
68
68
  height (int): Target height for scaling images.
69
69
  """
70
- if self.debug:
71
- folder_size = sum(os.path.getsize(os.path.join(dirpath, filename))
72
- for dirpath, dirnames, filenames in os.walk(self.coco_folder)
73
- for filename in filenames)
74
- # Convert size to MB
75
- folder_size_mb = folder_size / (1024 * 1024)
76
- if folder_size_mb > 5:
77
- from ok.feature.CompressCoco import compress_coco
78
- logger.info(f'template folder greater than 5MB try to compress the COCO dataset')
79
- compress_coco(self.coco_json)
80
- self.feature_dict.clear()
81
- self.box_dict.clear()
82
- with open(self.coco_json, 'r') as file:
83
- data = json.load(file)
84
-
85
- # Create a map from image ID to file name
86
- image_map = {image['id']: image['file_name'] for image in data['images']}
87
-
88
- # Create a map from category ID to category name
89
- category_map = {category['id']: category['name'] for category in data['categories']}
90
-
91
- for annotation in data['annotations']:
92
- image_id = annotation['image_id']
93
- category_id = annotation['category_id']
94
- bbox = annotation['bbox']
95
-
96
- # Load and scale the image
97
- image_path = str(os.path.join(self.coco_folder, image_map[image_id]))
98
- image = cv2.imread(image_path)
99
- _, original_width = image.shape[:2]
100
- if image is None:
101
- logger.error(f'Could not read image {image_path}')
102
- continue
103
- x, y, w, h = bbox
104
- if self.width != image.shape[1] or self.height != image.shape[0]:
105
- scale_x, scale_y = self.width / image.shape[1], self.height / image.shape[0]
106
- logger.debug(f'scaling images {scale_x}, {scale_y}')
107
- image = cv2.resize(image, (self.width, self.height))
108
- # Calculate the scaled bounding box
109
- x, y, w, h = x * scale_x, y * scale_y, w * scale_x, h * scale_y
110
-
111
- # Crop the image to the bounding box
112
- image = image[round(y):round(y + h), round(x):round(x + w), :3]
113
-
114
- # Store in featureDict using the category name
115
- category_name = category_map[category_id]
116
- logger.debug(
117
- f"loaded {category_name} resized width {self.width} / original_width:{original_width},scale_x:{self.width / original_width}")
118
- if category_name in self.feature_dict:
119
- raise ValueError(f"Multiple boxes found for category {category_name}")
120
- if not category_name.startswith('box_'):
121
- self.feature_dict[category_name] = Feature(image, x, y, w, h)
122
- self.box_dict[category_name] = Box(x, y, w, h, name=category_name)
70
+ self.feature_dict, self.box_dict, compressed = read_from_json(self.coco_json, self.width, self.height)
71
+ if self.debug and not compressed:
72
+ from ok.feature.CompressCoco import compress_coco
73
+ logger.info(f'coco not compressed try to compress the COCO dataset')
74
+ compress_coco(self.coco_json)
75
+ self.feature_dict, self.box_dict, compressed = read_from_json(self.coco_json, self.width, self.height)
123
76
 
124
77
  def get_box_by_name(self, mat, category_name: str) -> Box:
125
78
  self.check_size(mat)
@@ -235,6 +188,66 @@ class FeatureSet:
235
188
  return result
236
189
 
237
190
 
191
+ def read_from_json(coco_json, width=-1, height=-1):
192
+ feature_dict = {}
193
+ box_dict = {}
194
+ ok_compressed = None
195
+ with open(coco_json, 'r') as file:
196
+ data = json.load(file)
197
+ coco_folder = os.path.dirname(coco_json)
198
+
199
+ # Create a map from image ID to file name
200
+ image_map = {image['id']: image['file_name'] for image in data['images']}
201
+
202
+ # Create a map from category ID to category name
203
+ category_map = {category['id']: category['name'] for category in data['categories']}
204
+
205
+ for image_id, file_name in image_map.items():
206
+ # Load and scale the image
207
+ image_path = str(os.path.join(coco_folder, file_name))
208
+ if ok_compressed is None:
209
+ image = Image.open(image_path)
210
+ ok_compressed = 'ok_compressed' in image.info.items()
211
+ whole_image = cv2.imread(image_path)
212
+ if whole_image is None:
213
+ logger.error(f'Could not read image {image_path}')
214
+ continue
215
+ _, original_width = whole_image.shape[:2]
216
+
217
+ if width != -1 and height != -1 and (width != whole_image.shape[1] or height != whole_image.shape[0]):
218
+ scale_x, scale_y = width / whole_image.shape[1], height / whole_image.shape[0]
219
+ logger.debug(f'scaling images {scale_x}, {scale_y} to {width}x{height}')
220
+ whole_image = cv2.resize(whole_image, (width, height))
221
+ else:
222
+ scale_x, scale_y = 1, 1
223
+
224
+ for annotation in data['annotations']:
225
+ if image_id != annotation['image_id']:
226
+ continue
227
+
228
+ category_id = annotation['category_id']
229
+ bbox = annotation['bbox']
230
+ x, y, w, h = bbox
231
+
232
+ # Calculate the scaled bounding box
233
+ x, y, w, h = x * scale_x, y * scale_y, w * scale_x, h * scale_y
234
+
235
+ # Crop the image to the bounding box
236
+ image = whole_image[round(y):round(y + h), round(x):round(x + w), :3]
237
+
238
+ # Store in featureDict using the category name
239
+ category_name = category_map[category_id]
240
+ logger.debug(
241
+ f"loaded {category_name} resized width {width} / original_width:{original_width},scale_x:{width / original_width}")
242
+ if category_name in feature_dict:
243
+ raise ValueError(f"Multiple boxes found for category {category_name}")
244
+ if not category_name.startswith('box_'):
245
+ feature_dict[category_name] = Feature(image, x, y, w, h)
246
+ box_dict[category_name] = Box(x, y, w, h, name=category_name)
247
+
248
+ return feature_dict, box_dict, ok_compressed
249
+
250
+
238
251
  def filter_and_sort_matches(result, threshold, width, height):
239
252
  # Filter matches based on the threshold
240
253
  loc = np.where(result >= threshold)
@@ -242,13 +255,15 @@ def filter_and_sort_matches(result, threshold, width, height):
242
255
  # Zip the locations into a list of tuples and sort by threshold in descending order
243
256
  matches = sorted(zip(*loc[::-1]), key=lambda p: result[p[::-1]], reverse=True)
244
257
 
258
+ # Sort matches based on their location
259
+ matches.sort(key=lambda pt: (pt[0], pt[1]))
260
+
245
261
  # Filter out overlapping matches
246
262
  unique_matches = []
247
263
  for pt in matches:
248
- if all(not (pt[0] >= m[0] - width and pt[0] <= m[0] + width and
249
- pt[1] >= m[1] - height and pt[1] <= m[1] + height)
250
- for m in unique_matches):
264
+ # Only compare with the last match in unique_matches
265
+ if not unique_matches or not (unique_matches[-1][0] <= pt[0] <= unique_matches[-1][0] + width and
266
+ unique_matches[-1][1] <= pt[1] <= unique_matches[-1][1] + height):
251
267
  unique_matches.append(pt)
252
268
 
253
- # print(f"result {len(result)} loc {len(loc)} matches {len(matches)} unique_matches {unique_matches}")
254
269
  return unique_matches
@@ -13,7 +13,6 @@ from ok.gui.Communicate import communicate
13
13
  from ok.gui.MainWindow import MainWindow
14
14
  from ok.gui.MessageWindow import MessageWindow
15
15
  from ok.gui.StartController import StartController
16
- from ok.gui.debug.LogWindow import LogWindow
17
16
  from ok.gui.i18n.path import i18n_path
18
17
  from ok.gui.overlay.OverlayWindow import OverlayWindow
19
18
  from ok.logging.Logger import get_logger
@@ -95,26 +94,28 @@ class App:
95
94
  self.show_message_window(title, content)
96
95
 
97
96
  def show_main_window(self):
97
+
98
+ if self.overlay and ok.gui.device_manager.hwnd is not None:
99
+ self.overlay_window = OverlayWindow(ok.gui.device_manager.hwnd)
100
+
101
+ if self.config.get('debug'):
102
+ from ok.gui.debug.LogWindow import LogWindow
103
+ self.log_window = LogWindow(self.config)
104
+ self.log_window.show()
105
+ logger.debug('showing log_window')
106
+
98
107
  self.main_window = MainWindow(self.config, self.icon, self.title, self.version, self.overlay, self.about,
99
108
  self.exit_event)
100
109
  # Set the window title here
101
110
  self.main_window.setWindowIcon(self.icon)
102
- if self.overlay and ok.gui.device_manager.hwnd is not None:
103
- self.overlay_window = OverlayWindow(ok.gui.device_manager.hwnd)
104
111
 
105
112
  size = self.size_relative_to_screen(width=0.5, height=0.6)
106
113
  self.main_window.resize(size)
107
114
  self.main_window.setMinimumSize(size)
108
115
 
109
- # Optional: Move the window to the center of the screen
110
-
111
116
  self.main_window.show()
112
117
  self.main_window.raise_()
113
118
  self.main_window.activateWindow()
114
- if self.config.get('debug'):
115
- self.log_window = LogWindow(self.config)
116
- self.log_window.show()
117
- logger.debug('showing log_window')
118
119
 
119
120
  def size_relative_to_screen(self, width, height):
120
121
  screen = self.app.primaryScreen()
@@ -1,9 +1,8 @@
1
1
  import os
2
- import sys
3
2
 
4
3
  from PySide6.QtCore import QObject, Signal, Qt
5
4
  from PySide6.QtWidgets import QMenu, QSystemTrayIcon
6
- from qfluentwidgets import FluentIcon, NavigationItemPosition, MSFluentWindow, InfoBar, InfoBarPosition, MessageBox
5
+ from qfluentwidgets import FluentIcon, NavigationItemPosition, MSFluentWindow, InfoBar, InfoBarPosition
7
6
 
8
7
  import ok.gui
9
8
  from ok.gui.Communicate import communicate
@@ -158,22 +157,11 @@ class MainWindow(MSFluentWindow):
158
157
  self.comm.speak.emit("Hello, PySide6 with parameters!")
159
158
 
160
159
  def closeEvent(self, event):
161
- # Create a message box that asks the user if they really want to close the window
162
- if sys.platform == 'win32' and 'shutdown' in os.environ.get('SESSIONNAME', '').lower():
163
- logger.info("system shutting down")
164
- ok.gui.ok.quit()
165
- event.accept()
166
- return
167
-
168
160
  if ok.gui.ok.exit_event.is_set():
161
+ logger.info("Window closed exit_event.is_set")
169
162
  event.accept()
170
163
  return
171
- title = self.tr('Exit')
172
- content = self.tr("Are you sure you want to exit the app?")
173
- w = MessageBox(title, content, self.window())
174
- if w.exec():
175
- logger.info("Window closed")
164
+ else:
165
+ logger.info("Window closed exit_event.is not set")
176
166
  ok.gui.ok.quit()
177
167
  event.accept()
178
- else:
179
- event.ignore()
@@ -39,7 +39,7 @@ class StartController(QObject):
39
39
  error = self.check_device_error()
40
40
  if error is None:
41
41
  break
42
- logger.debug(f'waiting for game to start error {error}')
42
+ logger.error(f'waiting for game to start error {error}')
43
43
  remaining_time = wait_until - time.time()
44
44
  if remaining_time <= 0:
45
45
  communicate.starting_emulator.emit(True, self.tr('Start game timeout!'), 0)
@@ -1,7 +1,7 @@
1
1
  import subprocess
2
2
  from ctypes import windll, wintypes
3
3
 
4
- from PySide6.QtCore import Qt, Signal
4
+ from PySide6.QtCore import Qt, Signal, QCoreApplication
5
5
  from PySide6.QtWidgets import QWidget, QFileDialog, QCompleter, QVBoxLayout, QHBoxLayout
6
6
  from _ctypes import byref
7
7
  from qfluentwidgets import PushButton, FlowLayout, ComboBox, SearchLineEdit, TextEdit
@@ -46,7 +46,7 @@ class DebugTab(Tab):
46
46
  # self.dump_shortcut.activated.connect(dump_threads)
47
47
 
48
48
  capture_button = PushButton(self.tr("Capture Screenshot"))
49
- capture_button.clicked.connect(lambda: self.handler.post(self.capture))
49
+ capture_button.clicked.connect(lambda: self.handler.post(capture))
50
50
  layout.addWidget(capture_button)
51
51
 
52
52
  ocr_button = PushButton("OCR")
@@ -202,20 +202,22 @@ class DebugTab(Tab):
202
202
  self.select_screenshot_button.setText(self.tr("Drop or Select Screenshot"))
203
203
  self.config['target_images'] = None
204
204
 
205
- def capture(self):
206
- if ok.gui.device_manager.capture_method is not None:
207
- logger.info(f'ok.gui.device_manager.capture_method {ok.gui.device_manager.capture_method}')
208
- capture = str(ok.gui.device_manager.capture_method)
209
- frame = ok.gui.device_manager.capture_method.do_get_frame()
210
- if frame is not None:
211
- file_path = ok.gui.ok.screenshot.generate_screen_shot(frame, ok.gui.ok.screenshot.ui_dict,
212
- ok.gui.ok.screenshot.screenshot_folder, capture)
213
205
 
214
- # Use subprocess.Popen to open the file explorer and select the file
215
- subprocess.Popen(r'explorer /select,"{}"'.format(file_path))
216
- logger.info(f'captured screenshot: {capture}')
217
- alert_info(self.tr('Capture Success'), True)
218
- else:
219
- alert_error(self.tr('Capture returned None'))
206
+ def capture():
207
+ if ok.gui.device_manager.capture_method is not None:
208
+ logger.info(f'ok.gui.device_manager.capture_method {ok.gui.device_manager.capture_method}')
209
+ current_capture = str(ok.gui.device_manager.capture_method)
210
+ frame = ok.gui.device_manager.capture_method.do_get_frame()
211
+ if frame is not None:
212
+ file_path = ok.gui.ok.screenshot.generate_screen_shot(frame, ok.gui.ok.screenshot.ui_dict,
213
+ ok.gui.ok.screenshot.screenshot_folder,
214
+ current_capture)
215
+
216
+ # Use subprocess.Popen to open the file explorer and select the file
217
+ subprocess.Popen(r'explorer /select,"{}"'.format(file_path))
218
+ logger.info(f'captured screenshot: {current_capture}')
219
+ alert_info(QCoreApplication.translate('DebugTab', 'Capture Success'), True)
220
220
  else:
221
- alert_error(self.tr('No Capture Available or Selected'))
221
+ alert_error(QCoreApplication.translate('DebugTab', 'Capture returned None'))
222
+ else:
223
+ alert_error(QCoreApplication.translate('DebugTab', 'No Capture Available or Selected'))
@@ -1,7 +1,8 @@
1
- from PySide6.QtCore import Qt, QAbstractListModel, QModelIndex, QPoint, Signal
1
+ from PySide6.QtCore import Qt, QAbstractListModel, QPoint, QModelIndex
2
2
  from PySide6.QtGui import QColor
3
- from PySide6.QtWidgets import QVBoxLayout, QListView, QLineEdit, QHBoxLayout, QComboBox, QAbstractItemView, QWidget, \
4
- QPushButton, QLabel, QStyledItemDelegate
3
+ from PySide6.QtWidgets import QWidget, \
4
+ QStyledItemDelegate, QListView, QVBoxLayout, QHBoxLayout, QAbstractItemView, QComboBox, QLineEdit, QLabel, \
5
+ QPushButton
5
6
 
6
7
  from ok.config.Config import Config
7
8
  from ok.gui.Communicate import communicate
@@ -39,19 +40,19 @@ class ColorDelegate(QStyledItemDelegate):
39
40
 
40
41
 
41
42
  class LogModel(QAbstractListModel):
42
- def __init__(self, log_list):
43
+ def __init__(self):
43
44
  super(LogModel, self).__init__()
44
- self.log_list = log_list
45
45
  self.logs = []
46
- self.filtered_logs = self.logs[:]
46
+ self.filtered_logs = []
47
47
  self.current_level = "ALL"
48
48
  self.current_keyword = ""
49
49
 
50
50
  def data(self, index, role):
51
- if role == Qt.DisplayRole:
52
- return self.filtered_logs[index.row()].text
53
- elif role == Qt.ForegroundRole:
54
- return self.filtered_logs[index.row()].format
51
+ if 0 <= index.row() < len(self.filtered_logs):
52
+ if role == Qt.DisplayRole:
53
+ return self.filtered_logs[index.row()].text
54
+ elif role == Qt.ForegroundRole:
55
+ return self.filtered_logs[index.row()].format
55
56
 
56
57
  def rowCount(self, index):
57
58
  return len(self.filtered_logs)
@@ -62,15 +63,15 @@ class LogModel(QAbstractListModel):
62
63
  colored_text = ColoredText(message, color_format, level)
63
64
  self.logs.append(colored_text)
64
65
  if len(self.logs) >= 500:
65
- self.beginRemoveRows(QModelIndex(), 0, 0)
66
+ # self.beginRemoveRows(QModelIndex(), 0, 0)
66
67
  self.logs.pop(0)
67
- self.endRemoveRows()
68
-
68
+ # self.endRemoveRows()
69
69
  self.beginInsertRows(QModelIndex(), self.rowCount(QModelIndex()), self.rowCount(QModelIndex()))
70
70
  self.do_filter_logs()
71
71
  self.endInsertRows()
72
- self.layoutChanged.emit()
73
- self.log_list.scrollToBottom()
72
+
73
+ # self.layoutChanged.emit()
74
+ # self.log_list.scrollToBottom()
74
75
 
75
76
  def do_filter_logs(self):
76
77
 
@@ -92,9 +93,10 @@ class LogModel(QAbstractListModel):
92
93
  def filter_logs(self, level, keyword):
93
94
  self.current_level = level
94
95
  self.current_keyword = keyword
96
+ self.beginRemoveRows(QModelIndex(), 0, self.rowCount(QModelIndex()) - 1)
97
+ self.filtered_logs.clear()
98
+ self.endRemoveRows()
95
99
  self.do_filter_logs()
96
- self.layoutChanged.emit()
97
- self.log_list.scrollToBottom()
98
100
 
99
101
  def get_color_format(self, level):
100
102
  # Define color codes for different levels
@@ -118,8 +120,6 @@ class LogWindow(QWidget):
118
120
  app_config.get('config_folder'), 'log_window')
119
121
  self.setGeometry(self.config['x'], self.config['y'], self.config['width'], self.config['height'])
120
122
 
121
- self.refresh_signal = Signal()
122
-
123
123
  self.old_pos = None
124
124
 
125
125
  # Layouts
@@ -158,16 +158,15 @@ class LogWindow(QWidget):
158
158
 
159
159
  self.setLayout(self.layout)
160
160
 
161
- self.log_model = LogModel(self.log_list)
161
+ self.log_model = LogModel()
162
162
  self.log_list.setModel(self.log_model)
163
- communicate.log.connect(self.add_log)
164
163
 
165
- self.logs = []
166
- self.filter_logs()
164
+ communicate.log.connect(self.add_log)
167
165
 
168
166
  def add_log(self, level_no, message):
169
- self.logs.append(message)
167
+ # print('add_log', level_no, message)
170
168
  self.log_model.add_log(log_levels.get(level_no, 'DEBUG'), message)
169
+ self.log_list.scrollToBottom()
171
170
 
172
171
  def filter_logs(self):
173
172
  level = self.level_filter.currentText()