ok-script 0.0.7__py3-none-any.whl
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.
- autohelper/AutoHelper.py +116 -0
- autohelper/__init__.py +0 -0
- autohelper/capture/BaseCaptureMethod.py +30 -0
- autohelper/capture/HwndWindow.py +138 -0
- autohelper/capture/__init__.py +0 -0
- autohelper/capture/adb/ADBCaptureMethod.py +29 -0
- autohelper/capture/adb/DeviceManager.py +209 -0
- autohelper/capture/adb/__init__.py +0 -0
- autohelper/capture/adb/bin/__init__.py +0 -0
- autohelper/capture/adb/targets.py +61 -0
- autohelper/capture/adb/vbox.py +140 -0
- autohelper/capture/windows/WindowsGraphicsCaptureMethod.py +228 -0
- autohelper/capture/windows/__init__.py +0 -0
- autohelper/capture/windows/d3d11.py +74 -0
- autohelper/capture/windows/utils.py +149 -0
- autohelper/capture/windows/window.py +36 -0
- autohelper/color/Color.py +41 -0
- autohelper/color/__init__.py +0 -0
- autohelper/config/Config.py +79 -0
- autohelper/config/InfoDict.py +16 -0
- autohelper/config/__init__.py +0 -0
- autohelper/feature/Box.py +199 -0
- autohelper/feature/Feature.py +20 -0
- autohelper/feature/FeatureSet.py +207 -0
- autohelper/feature/FindFeature.py +52 -0
- autohelper/feature/__init__.py +0 -0
- autohelper/gui/App.py +120 -0
- autohelper/gui/Communicate.py +22 -0
- autohelper/gui/MainWindow.py +69 -0
- autohelper/gui/TabTitles.py +56 -0
- autohelper/gui/__init__.py +5 -0
- autohelper/gui/debug/AspectRatioWidget.py +31 -0
- autohelper/gui/debug/DebugTab.py +37 -0
- autohelper/gui/debug/FrameWidget.py +125 -0
- autohelper/gui/debug/InfoWidget.py +40 -0
- autohelper/gui/debug/LoggerWidget.py +74 -0
- autohelper/gui/debug/__init__.py +0 -0
- autohelper/gui/i18n/__init__.py +0 -0
- autohelper/gui/i18n/path.py +3 -0
- autohelper/gui/icon/__init__.py +0 -0
- autohelper/gui/icon/icon.py +6 -0
- autohelper/gui/loading/LoadingWindow.py +142 -0
- autohelper/gui/loading/__init__.py +0 -0
- autohelper/gui/overlay/OverlayWindow.py +34 -0
- autohelper/gui/overlay/__init__.py +0 -0
- autohelper/gui/resources.py +485 -0
- autohelper/gui/tasks/ConfigItemFactory.py +14 -0
- autohelper/gui/tasks/StartButton.py +47 -0
- autohelper/gui/tasks/TaskOpButton.py +33 -0
- autohelper/gui/tasks/TaskTab.py +121 -0
- autohelper/gui/tasks/TooltipTableWidget.py +39 -0
- autohelper/gui/tasks/__init__.py +0 -0
- autohelper/gui/util/Alert.py +20 -0
- autohelper/gui/util/InitWorker.py +17 -0
- autohelper/gui/util/__init__.py +0 -0
- autohelper/gui/widget/ListTableWidgetItem.py +27 -0
- autohelper/gui/widget/NumericTableWidgetItem.py +33 -0
- autohelper/gui/widget/RoundCornerContainer.py +59 -0
- autohelper/gui/widget/TabWidget.py +30 -0
- autohelper/gui/widget/UpdateConfigWidgetItem.py +16 -0
- autohelper/gui/widget/YesNonWidgetItem.py +23 -0
- autohelper/gui/widget/__init__.py +0 -0
- autohelper/interaction/ADBInteraction.py +33 -0
- autohelper/interaction/BaseInteraction.py +36 -0
- autohelper/interaction/Win32Interaction.py +56 -0
- autohelper/interaction/__init__.py +0 -0
- autohelper/logging/Logger.py +103 -0
- autohelper/logging/__init__.py +0 -0
- autohelper/ocr/OCR.py +50 -0
- autohelper/ocr/__init__.py +0 -0
- autohelper/predict/__init__.py +0 -0
- autohelper/predict/predict.py +45 -0
- autohelper/rotypes/Windows/Foundation/Collections.py +60 -0
- autohelper/rotypes/Windows/Foundation/__init__.py +153 -0
- autohelper/rotypes/Windows/Globalization/__init__.py +25 -0
- autohelper/rotypes/Windows/Graphics/Capture/__init__.py +110 -0
- autohelper/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +41 -0
- autohelper/rotypes/Windows/Graphics/DirectX/__init__.py +167 -0
- autohelper/rotypes/Windows/Graphics/Imaging/__init__.py +34 -0
- autohelper/rotypes/Windows/Graphics/__init__.py +5 -0
- autohelper/rotypes/Windows/Media/Ocr/__init__.py +76 -0
- autohelper/rotypes/Windows/Media/__init__.py +0 -0
- autohelper/rotypes/Windows/Security/Cryptography/__init__.py +18 -0
- autohelper/rotypes/Windows/Security/__init__.py +0 -0
- autohelper/rotypes/Windows/Storage/Streams/__init__.py +27 -0
- autohelper/rotypes/Windows/Storage/__init__.py +0 -0
- autohelper/rotypes/Windows/__init__.py +0 -0
- autohelper/rotypes/__init__.py +4 -0
- autohelper/rotypes/delegate.py +105 -0
- autohelper/rotypes/export.py +21 -0
- autohelper/rotypes/idldsl.py +272 -0
- autohelper/rotypes/inspectable.py +79 -0
- autohelper/rotypes/roapi.py +30 -0
- autohelper/rotypes/types.py +77 -0
- autohelper/rotypes/winstring.py +44 -0
- autohelper/save/BlackBarProcessor.py +25 -0
- autohelper/save/PostProcessor.py +6 -0
- autohelper/save/SaveByInterval.py +30 -0
- autohelper/save/SaveByKeyPress.py +27 -0
- autohelper/save/SaveMethodBase.py +46 -0
- autohelper/save/__init__.py +0 -0
- autohelper/scene/FeatureScene.py +14 -0
- autohelper/scene/Scene.py +21 -0
- autohelper/scene/__init__.py +0 -0
- autohelper/stats/StreamStats.py +34 -0
- autohelper/stats/__init__.py +0 -0
- autohelper/task/BaseTask.py +109 -0
- autohelper/task/ExecutorOperation.py +119 -0
- autohelper/task/FindFeatureTask.py +6 -0
- autohelper/task/TaskExecutor.py +249 -0
- autohelper/task/__init__.py +0 -0
- autohelper/util/__init__.py +0 -0
- autohelper/util/json.py +27 -0
- autohelper/util/list.py +2 -0
- autohelper/util/path.py +37 -0
- autohelper/util/thread.py +12 -0
- autohelper/util/win32_process.py +86 -0
- autohelper/util/yaml.py +28 -0
- ok_script-0.0.7.dist-info/METADATA +64 -0
- ok_script-0.0.7.dist-info/RECORD +122 -0
- ok_script-0.0.7.dist-info/WHEEL +5 -0
- ok_script-0.0.7.dist-info/top_level.txt +1 -0
autohelper/gui/App.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from PySide6.QtCore import QSize, QCoreApplication, QLocale, QTranslator
|
|
5
|
+
from PySide6.QtGui import QIcon
|
|
6
|
+
from PySide6.QtWidgets import QApplication, QStyleFactory, QMenu, QSystemTrayIcon
|
|
7
|
+
|
|
8
|
+
import autohelper
|
|
9
|
+
import autohelper.gui.resources
|
|
10
|
+
from autohelper.gui.Communicate import communicate
|
|
11
|
+
from autohelper.gui.MainWindow import MainWindow
|
|
12
|
+
from autohelper.gui.i18n.path import i18n_path
|
|
13
|
+
from autohelper.gui.loading.LoadingWindow import LoadingWindow
|
|
14
|
+
from autohelper.gui.overlay.OverlayWindow import OverlayWindow
|
|
15
|
+
from autohelper.logging.Logger import get_logger
|
|
16
|
+
|
|
17
|
+
logger = get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class App:
|
|
21
|
+
def __init__(self, icon=None, overlay=False, title="AutoUI", tasks=None,
|
|
22
|
+
exit_event=None):
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.app = QApplication(sys.argv)
|
|
25
|
+
self.app.setStyle(QStyleFactory.create("Fusion"))
|
|
26
|
+
self.tasks = tasks
|
|
27
|
+
self.title = title
|
|
28
|
+
self.overlay = overlay
|
|
29
|
+
self.loading_window = None
|
|
30
|
+
self.overlay_window = None
|
|
31
|
+
self.main_window = None
|
|
32
|
+
self.exit_event = exit_event
|
|
33
|
+
self.icon = QIcon(icon or ":/icon/icon.ico")
|
|
34
|
+
self.tray = QSystemTrayIcon(self.icon)
|
|
35
|
+
|
|
36
|
+
locale = QLocale.system().name()
|
|
37
|
+
translator = QTranslator(self.app)
|
|
38
|
+
full_path = os.path.join(i18n_path, f"{QLocale().name()}")
|
|
39
|
+
if translator.load(QLocale().name(), ":/i18n"):
|
|
40
|
+
translator.setParent(self.app)
|
|
41
|
+
self.app.installTranslator(translator)
|
|
42
|
+
QCoreApplication.installTranslator(translator)
|
|
43
|
+
logger.debug(f"translator install success {QCoreApplication.translate('MainWindow', 'Debug')}")
|
|
44
|
+
else:
|
|
45
|
+
logger.debug(f"No translation available for {locale}, falling back to English/default. {full_path}")
|
|
46
|
+
|
|
47
|
+
# Create a context menu for the tray
|
|
48
|
+
menu = QMenu()
|
|
49
|
+
exit_action = menu.addAction("Exit")
|
|
50
|
+
exit_action.triggered.connect(self.quit)
|
|
51
|
+
|
|
52
|
+
# Set the context menu and show the tray icon
|
|
53
|
+
self.tray.setContextMenu(menu)
|
|
54
|
+
self.tray.show()
|
|
55
|
+
|
|
56
|
+
communicate.init.connect(self.on_init)
|
|
57
|
+
communicate.notification.connect(self.show_notification)
|
|
58
|
+
|
|
59
|
+
def show_notification(self, title, message):
|
|
60
|
+
if title is None:
|
|
61
|
+
title = self.title
|
|
62
|
+
self.tray.showMessage(title, message)
|
|
63
|
+
|
|
64
|
+
def show_loading(self):
|
|
65
|
+
self.loading_window = LoadingWindow(self, self.exit_event)
|
|
66
|
+
size = self.size_relative_to_screen(width=0.4, height=0.4)
|
|
67
|
+
self.loading_window.resize(size)
|
|
68
|
+
self.loading_window.setMinimumSize(size)
|
|
69
|
+
self.center_window(self.loading_window)
|
|
70
|
+
self.loading_window.show()
|
|
71
|
+
|
|
72
|
+
def center_window(self, window):
|
|
73
|
+
screen = self.app.primaryScreen()
|
|
74
|
+
size = screen.size()
|
|
75
|
+
# Calculate half the screen size
|
|
76
|
+
half_screen_width = size.width() / 2
|
|
77
|
+
half_screen_height = size.height() / 2
|
|
78
|
+
|
|
79
|
+
window.move(half_screen_width / 2, half_screen_height / 2)
|
|
80
|
+
|
|
81
|
+
def on_init(self, done, message):
|
|
82
|
+
if done:
|
|
83
|
+
self.loading_window.loading_done()
|
|
84
|
+
else:
|
|
85
|
+
self.loading_window.update_progress(message)
|
|
86
|
+
|
|
87
|
+
def show_main_window(self):
|
|
88
|
+
self.loading_window.close()
|
|
89
|
+
self.main_window = MainWindow(self.tasks, self.overlay, exit_event=self.exit_event)
|
|
90
|
+
self.main_window.setWindowTitle(self.title) # Set the window title here
|
|
91
|
+
self.main_window.setWindowIcon(self.icon)
|
|
92
|
+
if self.overlay and autohelper.gui.device_manager.hwnd is not None:
|
|
93
|
+
self.overlay_window = OverlayWindow(autohelper.gui.device_manager.hwnd)
|
|
94
|
+
|
|
95
|
+
size = self.size_relative_to_screen(width=0.5, height=0.6)
|
|
96
|
+
self.main_window.resize(size)
|
|
97
|
+
self.main_window.setMinimumSize(size)
|
|
98
|
+
|
|
99
|
+
# Optional: Move the window to the center of the screen
|
|
100
|
+
|
|
101
|
+
self.main_window.show()
|
|
102
|
+
self.main_window.raise_()
|
|
103
|
+
self.main_window.activateWindow()
|
|
104
|
+
|
|
105
|
+
def size_relative_to_screen(self, width, height):
|
|
106
|
+
screen = self.app.primaryScreen()
|
|
107
|
+
size = screen.size()
|
|
108
|
+
# Calculate half the screen size
|
|
109
|
+
half_screen_width = size.width() * width
|
|
110
|
+
half_screen_height = size.height() * height
|
|
111
|
+
# Resize the window to half the screen size
|
|
112
|
+
size = QSize(half_screen_width, half_screen_height)
|
|
113
|
+
return size
|
|
114
|
+
|
|
115
|
+
def exec(self):
|
|
116
|
+
sys.exit(self.app.exec())
|
|
117
|
+
|
|
118
|
+
def quit(self):
|
|
119
|
+
self.exit_event.set()
|
|
120
|
+
self.app.quit()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from PySide6.QtCore import Signal, QObject
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Communicate(QObject):
|
|
6
|
+
frame = Signal(np.ndarray)
|
|
7
|
+
log = Signal(int, str)
|
|
8
|
+
fps = Signal(int)
|
|
9
|
+
frame_time = Signal(int)
|
|
10
|
+
scene = Signal(str)
|
|
11
|
+
draw_box = Signal(str, object, str)
|
|
12
|
+
tasks = Signal()
|
|
13
|
+
task_info = Signal()
|
|
14
|
+
window = Signal(bool, int, int, int, int, int, int, float)
|
|
15
|
+
loading_progress = Signal(str)
|
|
16
|
+
init = Signal(bool, str)
|
|
17
|
+
notification = Signal(str, str)
|
|
18
|
+
executor_paused: Signal = Signal(bool)
|
|
19
|
+
adb_devices: Signal = Signal()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
communicate = Communicate()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from PySide6.QtCore import QObject, Signal, Slot
|
|
2
|
+
from PySide6.QtWidgets import QMessageBox, QTabWidget
|
|
3
|
+
|
|
4
|
+
from autohelper.gui.debug.DebugTab import DebugTab
|
|
5
|
+
from autohelper.gui.tasks.TaskTab import TaskTab
|
|
6
|
+
from autohelper.logging.Logger import get_logger
|
|
7
|
+
|
|
8
|
+
logger = get_logger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Communicate(QObject):
|
|
12
|
+
speak = Signal(str)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MainWindow(QTabWidget):
|
|
16
|
+
def __init__(self, tasks, debug=False, exit_event=None):
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.exit_event = exit_event
|
|
19
|
+
task_tab = TaskTab(tasks)
|
|
20
|
+
self.addTab(task_tab, self.tr("Task"))
|
|
21
|
+
if debug:
|
|
22
|
+
debug_tab = DebugTab()
|
|
23
|
+
self.addTab(debug_tab, self.tr("Debug"))
|
|
24
|
+
# ... Add other tabs similarly
|
|
25
|
+
|
|
26
|
+
# Styling the tabs and content if needed, for example:
|
|
27
|
+
self.setStyleSheet("""
|
|
28
|
+
QTabWidget::tab-bar {
|
|
29
|
+
alignment: center;
|
|
30
|
+
}
|
|
31
|
+
QTabBar::tab {
|
|
32
|
+
background: #333;
|
|
33
|
+
color: white;
|
|
34
|
+
border-radius: 5px;
|
|
35
|
+
padding: 10px;
|
|
36
|
+
}
|
|
37
|
+
QTabBar::tab:selected {
|
|
38
|
+
background: #555;
|
|
39
|
+
font-weight: bold;
|
|
40
|
+
}
|
|
41
|
+
QWidget {
|
|
42
|
+
background-color: #222;
|
|
43
|
+
color: #ddd;
|
|
44
|
+
}
|
|
45
|
+
""")
|
|
46
|
+
self.setWindowTitle("Close Event Example")
|
|
47
|
+
self.setTabPosition(QTabWidget.West)
|
|
48
|
+
# self.setTabBar(QTabBar())
|
|
49
|
+
self.comm = Communicate()
|
|
50
|
+
self.comm.speak.connect(self.say_hello)
|
|
51
|
+
|
|
52
|
+
@Slot(str)
|
|
53
|
+
def say_hello(self, message):
|
|
54
|
+
print(message)
|
|
55
|
+
|
|
56
|
+
def btn_clicked(self):
|
|
57
|
+
self.comm.speak.emit("Hello, PySide6 with parameters!")
|
|
58
|
+
|
|
59
|
+
def closeEvent(self, event):
|
|
60
|
+
# Create a message box that asks the user if they really want to close the window
|
|
61
|
+
reply = QMessageBox.question(self, 'Window Close', 'Are you sure you want to close the window?',
|
|
62
|
+
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
|
63
|
+
|
|
64
|
+
if reply == QMessageBox.Yes:
|
|
65
|
+
self.exit_event.set()
|
|
66
|
+
event.accept()
|
|
67
|
+
logger.info("Window closed") # Place your code here
|
|
68
|
+
else:
|
|
69
|
+
event.ignore()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from PySide6.QtCore import QRect, QPoint, Qt
|
|
2
|
+
from PySide6.QtWidgets import QTabBar, QStylePainter, QStyleOptionTab, QStyle, QWidget
|
|
3
|
+
from PySide6.QtWidgets import QTabWidget, QVBoxLayout, QLabel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TabBar(QTabBar):
|
|
7
|
+
def __init__(self, *args, **kwargs):
|
|
8
|
+
super().__init__(*args, **kwargs)
|
|
9
|
+
|
|
10
|
+
def tabSizeHint(self, index):
|
|
11
|
+
s = QTabBar.tabSizeHint(self, index)
|
|
12
|
+
if s.width() < s.height():
|
|
13
|
+
s.transpose()
|
|
14
|
+
s.scale(s.width() * 2, s.height() * 2, Qt.KeepAspectRatio)
|
|
15
|
+
return s
|
|
16
|
+
|
|
17
|
+
# Make text visible adequately
|
|
18
|
+
def paintEvent(self, event):
|
|
19
|
+
painter = QStylePainter(self)
|
|
20
|
+
style_option = QStyleOptionTab()
|
|
21
|
+
|
|
22
|
+
for i in range(self.count()):
|
|
23
|
+
self.initStyleOption(style_option, i)
|
|
24
|
+
painter.drawControl(QStyle.CE_TabBarTabShape, style_option)
|
|
25
|
+
painter.save()
|
|
26
|
+
|
|
27
|
+
s = style_option.rect.size()
|
|
28
|
+
s.scale(s.width() * 2, s.height() * 2, Qt.KeepAspectRatio)
|
|
29
|
+
rect = QRect(QPoint(), s)
|
|
30
|
+
rect.moveCenter(style_option.rect.center())
|
|
31
|
+
style_option.rect = rect
|
|
32
|
+
|
|
33
|
+
center = self.tabRect(i).center()
|
|
34
|
+
painter.translate(center)
|
|
35
|
+
painter.rotate(90)
|
|
36
|
+
painter.translate(center * -1)
|
|
37
|
+
painter.drawControl(QStyle.CE_TabBarTabLabel, style_option)
|
|
38
|
+
painter.restore()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class VerticalTabWidget(QTabWidget):
|
|
42
|
+
def __init__(self):
|
|
43
|
+
super().__init__()
|
|
44
|
+
self.setTabBar(TabBar(self))
|
|
45
|
+
self.setTabPosition(QTabWidget.West)
|
|
46
|
+
|
|
47
|
+
# def setTabSize(self, width, height):
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TabContent(QWidget):
|
|
51
|
+
def __init__(self, text):
|
|
52
|
+
super().__init__()
|
|
53
|
+
layout = QVBoxLayout()
|
|
54
|
+
label = QLabel(text)
|
|
55
|
+
layout.addWidget(label)
|
|
56
|
+
self.setLayout(layout)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from PySide6.QtCore import QSize
|
|
2
|
+
from PySide6.QtWidgets import QWidget, QVBoxLayout, QSizePolicy
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AspectRatioWidget(QWidget):
|
|
6
|
+
def __init__(self, child_widget, aspect_ratio=16 / 9, parent=None):
|
|
7
|
+
super(AspectRatioWidget, self).__init__(parent)
|
|
8
|
+
self.aspect_ratio = aspect_ratio
|
|
9
|
+
self.child_widget = child_widget
|
|
10
|
+
self.layout = QVBoxLayout(self)
|
|
11
|
+
self.layout.addWidget(child_widget)
|
|
12
|
+
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
|
|
13
|
+
|
|
14
|
+
def resizeEvent(self, event):
|
|
15
|
+
w = event.size().width()
|
|
16
|
+
h = event.size().height()
|
|
17
|
+
new_width = h * self.aspect_ratio
|
|
18
|
+
new_height = w / self.aspect_ratio
|
|
19
|
+
if new_height > h:
|
|
20
|
+
self.child_widget.setFixedSize(QSize(new_width, h))
|
|
21
|
+
else:
|
|
22
|
+
self.child_widget.setFixedSize(QSize(w, new_height))
|
|
23
|
+
super().resizeEvent(event)
|
|
24
|
+
|
|
25
|
+
def sizeHint(self):
|
|
26
|
+
# Provide a default size hint that respects the aspect ratio
|
|
27
|
+
height = 200 # Default height
|
|
28
|
+
return QSize(int(height * self.aspect_ratio), height)
|
|
29
|
+
|
|
30
|
+
def set_ratio(self, aspect_ratio):
|
|
31
|
+
self.aspect_ratio = aspect_ratio
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from PySide6.QtCore import Qt
|
|
2
|
+
from PySide6.QtWidgets import QWidget, QGridLayout, QSplitter
|
|
3
|
+
|
|
4
|
+
from autohelper.gui.debug.FrameWidget import FrameWidget
|
|
5
|
+
from autohelper.gui.debug.InfoWidget import InfoWidget
|
|
6
|
+
from autohelper.gui.debug.LoggerWidget import LoggerWidget
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DebugTab(QWidget):
|
|
10
|
+
def __init__(self):
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
self.mainLayout = QGridLayout()
|
|
14
|
+
self.setLayout(self.mainLayout)
|
|
15
|
+
|
|
16
|
+
# Top row setup with horizontal splitter
|
|
17
|
+
self.topSplitter = QSplitter(Qt.Horizontal)
|
|
18
|
+
self.topSplitter.setMinimumHeight(300)
|
|
19
|
+
self.info_widget = InfoWidget()
|
|
20
|
+
self.info_widget.setFixedWidth(200)
|
|
21
|
+
self.frame_widget = FrameWidget(True)
|
|
22
|
+
self.topSplitter.addWidget(self.info_widget)
|
|
23
|
+
self.topSplitter.addWidget(self.frame_widget)
|
|
24
|
+
|
|
25
|
+
# Bottom row setup
|
|
26
|
+
self.logger = LoggerWidget()
|
|
27
|
+
|
|
28
|
+
# Main splitter to handle top and bottom rows
|
|
29
|
+
self.mainSplitter = QSplitter(Qt.Vertical)
|
|
30
|
+
self.mainSplitter.addWidget(self.topSplitter)
|
|
31
|
+
self.mainSplitter.addWidget(self.logger)
|
|
32
|
+
|
|
33
|
+
# Add the main splitter to the layout
|
|
34
|
+
self.mainLayout.addWidget(self.mainSplitter, 0, 0)
|
|
35
|
+
|
|
36
|
+
self.mainSplitter.setStretchFactor(0, 8) # 30% for the top widget
|
|
37
|
+
self.mainSplitter.setStretchFactor(1, 2) # 70%
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from PySide6.QtCore import Qt
|
|
5
|
+
from PySide6.QtGui import QPainter, QImage, QPixmap, QColor, QPen
|
|
6
|
+
from PySide6.QtWidgets import QWidget
|
|
7
|
+
|
|
8
|
+
from autohelper.feature.Box import Box
|
|
9
|
+
from autohelper.gui.Communicate import communicate
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FrameWidget(QWidget):
|
|
13
|
+
def __init__(self, draw_frame=False, parent=None):
|
|
14
|
+
super(FrameWidget, self).__init__(parent)
|
|
15
|
+
self.cv_image = None
|
|
16
|
+
self.color_map = {
|
|
17
|
+
"red": QColor(255, 0, 0), # RGB for red
|
|
18
|
+
"green": QColor(0, 255, 0), # RGB for green
|
|
19
|
+
"blue": QColor(0, 0, 255) # RGB for blue
|
|
20
|
+
}
|
|
21
|
+
self.uiDict = {}
|
|
22
|
+
self.time_to_expire = 3
|
|
23
|
+
self.draw_frame = draw_frame
|
|
24
|
+
self.frame_width = 1
|
|
25
|
+
self._visible = True
|
|
26
|
+
communicate.frame.connect(self.set_image)
|
|
27
|
+
communicate.draw_box.connect(self.draw_box)
|
|
28
|
+
|
|
29
|
+
def remove_expired(self):
|
|
30
|
+
current_time = time.time()
|
|
31
|
+
for key in list(self.uiDict.keys()):
|
|
32
|
+
# Filter out the old UI elements and keep the remaining ones
|
|
33
|
+
if current_time - self.uiDict[key][1] > self.time_to_expire:
|
|
34
|
+
del self.uiDict[key]
|
|
35
|
+
|
|
36
|
+
def draw_box(self, key: str, boxes, color=None):
|
|
37
|
+
if boxes is None:
|
|
38
|
+
return
|
|
39
|
+
if isinstance(boxes, Box):
|
|
40
|
+
boxes = [boxes]
|
|
41
|
+
if len(boxes) == 0:
|
|
42
|
+
return
|
|
43
|
+
timestamp = time.time()
|
|
44
|
+
brush = self.color_map.get(color, self.color_map.get("red"))
|
|
45
|
+
if key:
|
|
46
|
+
self.uiDict[key] = [boxes, timestamp, brush]
|
|
47
|
+
else:
|
|
48
|
+
for box in boxes:
|
|
49
|
+
self.uiDict[box.name] = [[box], timestamp, brush]
|
|
50
|
+
self.update()
|
|
51
|
+
|
|
52
|
+
def set_image(self, cv_image):
|
|
53
|
+
"""Set the OpenCV image to be displayed."""
|
|
54
|
+
self.frame_width = cv_image.shape[1]
|
|
55
|
+
if self.draw_frame:
|
|
56
|
+
self.cv_image = cv_image
|
|
57
|
+
self.update() # Trigger a repaint
|
|
58
|
+
|
|
59
|
+
def paintEvent(self, event):
|
|
60
|
+
if not self._visible:
|
|
61
|
+
return
|
|
62
|
+
painter = QPainter(self)
|
|
63
|
+
x_offset = 0
|
|
64
|
+
y_offset = 0
|
|
65
|
+
if self.cv_image is not None:
|
|
66
|
+
frame = np.ascontiguousarray(self.cv_image)
|
|
67
|
+
# frame = self.cv_image
|
|
68
|
+
frame_height, frame_width = frame.shape[:2]
|
|
69
|
+
|
|
70
|
+
# Convert the OpenCV image (BGR) to QImage format (RGB)
|
|
71
|
+
# h, w, ch = self.cv_image.shape
|
|
72
|
+
# bytes_per_line = ch * w
|
|
73
|
+
# convert_to_Qt_format = QImage(self.cv_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
|
|
74
|
+
# qt_image = convert_to_Qt_format.rgbSwapped()
|
|
75
|
+
|
|
76
|
+
qt_image = QImage(frame.data, frame.shape[1], frame.shape[0],
|
|
77
|
+
frame.strides[0],
|
|
78
|
+
QImage.Format_BGR888)
|
|
79
|
+
# qt_image = QImage(frame.data, frame.shape[1], frame.shape[0],
|
|
80
|
+
# frame.strides[0],
|
|
81
|
+
# QImage.Format_RGBA8888)
|
|
82
|
+
|
|
83
|
+
# Scaling the image to fit the aspect ratio of the widget
|
|
84
|
+
qt_image = qt_image.scaled(self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
|
85
|
+
|
|
86
|
+
# Calculate the position to center the image in the widget
|
|
87
|
+
x_offset = (self.width() - qt_image.width()) / 2
|
|
88
|
+
y_offset = (self.height() - qt_image.height()) / 2
|
|
89
|
+
|
|
90
|
+
frame_ratio = qt_image.width() / frame_width
|
|
91
|
+
|
|
92
|
+
painter.drawPixmap(x_offset, y_offset, QPixmap.fromImage(qt_image))
|
|
93
|
+
else:
|
|
94
|
+
frame_ratio = self.width() / self.frame_width
|
|
95
|
+
self.paint_border(painter)
|
|
96
|
+
self.paint_boxes(frame_ratio, painter, x_offset, y_offset)
|
|
97
|
+
|
|
98
|
+
def paint_boxes(self, frame_ratio, painter, x_offset, y_offset):
|
|
99
|
+
pen = QPen() # Set the brush to red color
|
|
100
|
+
pen.setWidth(2) # Set the width of the pen (border thickness)
|
|
101
|
+
painter.setPen(pen) # Apply the pen to the painter
|
|
102
|
+
painter.setBrush(Qt.NoBrush) # Ensure no fill
|
|
103
|
+
# painter.setFont(QFont('Arial', 12))
|
|
104
|
+
# Draw a square. Arguments are (x, y, width, height).
|
|
105
|
+
# Adjust these values according to the desired size and position.
|
|
106
|
+
self.remove_expired()
|
|
107
|
+
for key, value in self.uiDict.items():
|
|
108
|
+
boxes = value[0]
|
|
109
|
+
pen.setColor(value[2])
|
|
110
|
+
painter.setPen(pen)
|
|
111
|
+
for box in boxes:
|
|
112
|
+
width = box.width * frame_ratio
|
|
113
|
+
height = box.height * frame_ratio
|
|
114
|
+
x = x_offset + box.x * frame_ratio
|
|
115
|
+
y = y_offset + box.y * frame_ratio
|
|
116
|
+
painter.drawRect(x, y, width, height)
|
|
117
|
+
painter.drawText(x, y + height + 12, f"{box.name or key}_{round(box.confidence * 100)}")
|
|
118
|
+
|
|
119
|
+
def paint_border(self, painter):
|
|
120
|
+
pen = QPen(QColor(255, 0, 0, 255)) # Solid red color for the border
|
|
121
|
+
pen.setWidth(1) # Set the border width
|
|
122
|
+
painter.setPen(pen)
|
|
123
|
+
|
|
124
|
+
# Draw the border around the widget
|
|
125
|
+
painter.drawRect(0, 0, self.width() - 1, self.height() - 1)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel
|
|
2
|
+
|
|
3
|
+
from autohelper.gui.Communicate import communicate
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class InfoWidget(QWidget):
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
super().__init__()
|
|
10
|
+
self.init_ui()
|
|
11
|
+
# Connect the custom signal to the slot that updates the values
|
|
12
|
+
communicate.fps.connect(self.update_fps)
|
|
13
|
+
communicate.scene.connect(self.update_scene)
|
|
14
|
+
communicate.frame_time.connect(self.update_frame_time)
|
|
15
|
+
|
|
16
|
+
def init_ui(self):
|
|
17
|
+
self.layout = QVBoxLayout(self)
|
|
18
|
+
|
|
19
|
+
self.scene_widget = QLabel()
|
|
20
|
+
self.layout.addWidget(self.scene_widget)
|
|
21
|
+
self.update_scene("None")
|
|
22
|
+
|
|
23
|
+
self.fps_widget = QLabel()
|
|
24
|
+
self.layout.addWidget(self.fps_widget)
|
|
25
|
+
self.update_fps(0)
|
|
26
|
+
|
|
27
|
+
self.frame_time_widget = QLabel()
|
|
28
|
+
self.layout.addWidget(self.frame_time_widget)
|
|
29
|
+
self.update_frame_time(0)
|
|
30
|
+
|
|
31
|
+
self.layout.addStretch()
|
|
32
|
+
|
|
33
|
+
def update_fps(self, fps):
|
|
34
|
+
self.fps_widget.setText(f"FPS: {fps}")
|
|
35
|
+
|
|
36
|
+
def update_frame_time(self, fps):
|
|
37
|
+
self.frame_time_widget.setText(f"FrameTime: {fps}")
|
|
38
|
+
|
|
39
|
+
def update_scene(self, scene):
|
|
40
|
+
self.scene_widget.setText(f"Scene: {scene}")
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QComboBox, QLineEdit, QTextEdit)
|
|
4
|
+
|
|
5
|
+
from autohelper.gui.Communicate import communicate
|
|
6
|
+
|
|
7
|
+
level_map = {'DEBUG': logging.DEBUG, 'INFO': logging.INFO, 'WARNING': logging.WARNING, 'ERROR': logging.ERROR,
|
|
8
|
+
'CRITICAL': logging.CRITICAL}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_colored_message(level, message):
|
|
12
|
+
if level >= logging.ERROR:
|
|
13
|
+
color = "red"
|
|
14
|
+
elif level == logging.WARNING:
|
|
15
|
+
color = "darkorange" # Dark yellow can be represented as 'darkorange' for better visibility
|
|
16
|
+
else:
|
|
17
|
+
return message
|
|
18
|
+
return f'<span style="color: {color};">{message}</span>'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class LoggerWidget(QWidget):
|
|
22
|
+
# Define log levels
|
|
23
|
+
|
|
24
|
+
def __init__(self, parent=None):
|
|
25
|
+
super(LoggerWidget, self).__init__(parent)
|
|
26
|
+
self.max_length = 1000
|
|
27
|
+
self.init_ui()
|
|
28
|
+
self.logs = [] # Store logs as tuples (level, message)
|
|
29
|
+
communicate.log.connect(self.add_log)
|
|
30
|
+
|
|
31
|
+
def init_ui(self):
|
|
32
|
+
self.layout = QVBoxLayout(self)
|
|
33
|
+
|
|
34
|
+
# Filter controls
|
|
35
|
+
controls_layout = QHBoxLayout()
|
|
36
|
+
self.level_filter = QComboBox()
|
|
37
|
+
self.level_filter.addItems(level_map.keys())
|
|
38
|
+
self.level_filter.currentTextChanged.connect(self.update_display)
|
|
39
|
+
|
|
40
|
+
self.text_filter = QLineEdit()
|
|
41
|
+
self.text_filter.setPlaceholderText("Filter logs by text...")
|
|
42
|
+
self.text_filter.textChanged.connect(self.update_display)
|
|
43
|
+
|
|
44
|
+
controls_layout.addWidget(self.level_filter)
|
|
45
|
+
controls_layout.addWidget(self.text_filter)
|
|
46
|
+
|
|
47
|
+
# Log display
|
|
48
|
+
self.log_display = QTextEdit()
|
|
49
|
+
self.log_display.setReadOnly(True)
|
|
50
|
+
|
|
51
|
+
# Add widgets to layout
|
|
52
|
+
self.layout.addLayout(controls_layout)
|
|
53
|
+
self.layout.addWidget(self.log_display)
|
|
54
|
+
|
|
55
|
+
def add_log(self, level, message):
|
|
56
|
+
self.logs.append((level, message))
|
|
57
|
+
if len(self.logs) > self.max_length:
|
|
58
|
+
# Calculate how many elements to remove
|
|
59
|
+
excess_length = len(self.logs) - self.max_length
|
|
60
|
+
# Remove the oldest elements
|
|
61
|
+
del self.logs[:excess_length]
|
|
62
|
+
self.update_display()
|
|
63
|
+
|
|
64
|
+
def update_display(self):
|
|
65
|
+
filtered_logs = self.filter_logs()
|
|
66
|
+
self.log_display.clear()
|
|
67
|
+
for level, message in filtered_logs:
|
|
68
|
+
colored_message = get_colored_message(level, message)
|
|
69
|
+
self.log_display.append(colored_message)
|
|
70
|
+
|
|
71
|
+
def filter_logs(self):
|
|
72
|
+
level = level_map[self.level_filter.currentText()]
|
|
73
|
+
text = self.text_filter.text().lower()
|
|
74
|
+
return [(lvl, msg) for lvl, msg in self.logs if (lvl >= level) and text in msg.lower()]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|