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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from PySide6.QtCore import Slot, QPropertyAnimation
|
|
2
|
+
from PySide6.QtGui import QColor
|
|
3
|
+
from PySide6.QtWidgets import QPushButton
|
|
4
|
+
|
|
5
|
+
import autohelper
|
|
6
|
+
from autohelper.gui.Communicate import communicate
|
|
7
|
+
from autohelper.gui.util.Alert import show_alert
|
|
8
|
+
from autohelper.logging.Logger import get_logger
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class StartButton(QPushButton):
|
|
14
|
+
def __init__(self):
|
|
15
|
+
super().__init__("Start")
|
|
16
|
+
self.setCheckable(True)
|
|
17
|
+
self.clicked.connect(self.toggle_text)
|
|
18
|
+
self.animation = QPropertyAnimation(self, b"color")
|
|
19
|
+
self.update_paused(True)
|
|
20
|
+
communicate.executor_paused.connect(self.update_paused)
|
|
21
|
+
|
|
22
|
+
def update_paused(self, paused):
|
|
23
|
+
if paused:
|
|
24
|
+
self.setText(self.tr("Start All"))
|
|
25
|
+
self.setChecked(False)
|
|
26
|
+
self.animation.stop()
|
|
27
|
+
else:
|
|
28
|
+
self.setText(self.tr("Pause All"))
|
|
29
|
+
self.setChecked(True)
|
|
30
|
+
self.start_animation()
|
|
31
|
+
|
|
32
|
+
@Slot()
|
|
33
|
+
def toggle_text(self):
|
|
34
|
+
if self.isChecked():
|
|
35
|
+
logger.info("Click Start Executor")
|
|
36
|
+
if not autohelper.gui.executor.start():
|
|
37
|
+
show_alert("Error", "No Task to Run, Please Enable Task First!")
|
|
38
|
+
self.setChecked(False)
|
|
39
|
+
else:
|
|
40
|
+
logger.info("Click Pause Executor")
|
|
41
|
+
autohelper.gui.executor.pause()
|
|
42
|
+
|
|
43
|
+
def start_animation(self):
|
|
44
|
+
self.animation.setStartValue(QColor(0, 0, 0))
|
|
45
|
+
self.animation.setEndValue(QColor(255, 255, 255))
|
|
46
|
+
self.animation.setDuration(1000)
|
|
47
|
+
self.animation.start()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from PySide6.QtCore import Slot
|
|
2
|
+
from PySide6.QtWidgets import QPushButton
|
|
3
|
+
|
|
4
|
+
from autohelper.logging.Logger import get_logger
|
|
5
|
+
from autohelper.task.BaseTask import BaseTask
|
|
6
|
+
|
|
7
|
+
logger = get_logger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TaskOpButton(QPushButton):
|
|
11
|
+
def __init__(self, task: BaseTask):
|
|
12
|
+
super().__init__("Enable")
|
|
13
|
+
self.clicked.connect(self.toggle_text)
|
|
14
|
+
self.task = task
|
|
15
|
+
|
|
16
|
+
def update_task(self, task: BaseTask):
|
|
17
|
+
if task.enabled:
|
|
18
|
+
if task.done:
|
|
19
|
+
self.setText(self.tr("Rerun"))
|
|
20
|
+
else:
|
|
21
|
+
self.setText(self.tr("Disable"))
|
|
22
|
+
else:
|
|
23
|
+
self.setText(self.tr("Enable"))
|
|
24
|
+
|
|
25
|
+
@Slot()
|
|
26
|
+
def toggle_text(self):
|
|
27
|
+
if self.task.enabled:
|
|
28
|
+
if self.task.done:
|
|
29
|
+
self.task.enable()
|
|
30
|
+
else:
|
|
31
|
+
self.task.disable()
|
|
32
|
+
else:
|
|
33
|
+
self.task.disable()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from PySide6.QtCore import Qt, QTimer
|
|
4
|
+
from PySide6.QtGui import QColor
|
|
5
|
+
from PySide6.QtWidgets import QTableWidget, QTableWidgetItem, QVBoxLayout, QWidget, QHBoxLayout
|
|
6
|
+
|
|
7
|
+
from autohelper.gui.Communicate import communicate
|
|
8
|
+
from autohelper.gui.tasks.ConfigItemFactory import config_widget_item
|
|
9
|
+
from autohelper.gui.tasks.StartButton import StartButton
|
|
10
|
+
from autohelper.gui.tasks.TaskOpButton import TaskOpButton
|
|
11
|
+
from autohelper.gui.tasks.TooltipTableWidget import TooltipTableWidget
|
|
12
|
+
from autohelper.gui.widget.RoundCornerContainer import RoundCornerContainer
|
|
13
|
+
from autohelper.gui.widget.UpdateConfigWidgetItem import value_to_string
|
|
14
|
+
from autohelper.logging.Logger import get_logger
|
|
15
|
+
from autohelper.task.BaseTask import BaseTask
|
|
16
|
+
|
|
17
|
+
logger = get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TaskTab(QWidget):
|
|
21
|
+
def __init__(self, tasks: List[BaseTask]):
|
|
22
|
+
super().__init__()
|
|
23
|
+
|
|
24
|
+
self.mainLayout = QVBoxLayout(self)
|
|
25
|
+
self.setLayout(self.mainLayout)
|
|
26
|
+
self.tasks = tasks
|
|
27
|
+
# Top row setup with horizontal splitter
|
|
28
|
+
self.top_layout = QHBoxLayout()
|
|
29
|
+
self.task_table = TooltipTableWidget([0.7, 0.15, 0.15])
|
|
30
|
+
self.task_container = RoundCornerContainer(self.tr('Tasks'), self.task_table)
|
|
31
|
+
self.start_button = StartButton()
|
|
32
|
+
self.task_container.add_top_widget(self.start_button)
|
|
33
|
+
|
|
34
|
+
self.top_layout.addWidget(self.task_container)
|
|
35
|
+
self.task_labels = [self.tr('Name'), self.tr('Status'), self.tr('Operation')]
|
|
36
|
+
self.create_table()
|
|
37
|
+
self.mainLayout.addLayout(self.top_layout)
|
|
38
|
+
|
|
39
|
+
self.task_config_table = TooltipTableWidget([0.3, 0.7])
|
|
40
|
+
self.task_config_container = RoundCornerContainer(self.tr('Tasks Config'), self.task_config_table)
|
|
41
|
+
self.task_config_labels = [self.tr('Config'), self.tr('Value')]
|
|
42
|
+
self.task_config_table.setColumnCount(len(self.task_config_labels)) # Name and Value
|
|
43
|
+
self.task_config_table.setHorizontalHeaderLabels(self.task_config_labels)
|
|
44
|
+
self.update_config_table()
|
|
45
|
+
self.mainLayout.addWidget(self.task_config_container)
|
|
46
|
+
|
|
47
|
+
self.task_info_table = TooltipTableWidget([0.3, 0.7])
|
|
48
|
+
self.task_info_container = RoundCornerContainer(self.tr('Tasks Info'), self.task_info_table)
|
|
49
|
+
self.top_layout.addWidget(self.task_info_container)
|
|
50
|
+
self.task_info_labels = [self.tr('Info'), self.tr('Value')]
|
|
51
|
+
self.task_info_table.setColumnCount(len(self.task_info_labels)) # Name and Value
|
|
52
|
+
self.task_info_table.setHorizontalHeaderLabels(self.task_config_labels)
|
|
53
|
+
self.update_info_table()
|
|
54
|
+
|
|
55
|
+
communicate.tasks.connect(self.update_table)
|
|
56
|
+
communicate.task_info.connect(self.update_info_table)
|
|
57
|
+
self.timer = QTimer()
|
|
58
|
+
self.timer.timeout.connect(self.update_info_table)
|
|
59
|
+
self.timer.timeout.connect(self.update_table)
|
|
60
|
+
self.timer.start(1000)
|
|
61
|
+
|
|
62
|
+
def update_config_table(self):
|
|
63
|
+
task = self.tasks[self.task_table.selectedIndexes()[0].row()]
|
|
64
|
+
config = task.config
|
|
65
|
+
self.task_config_container.title_label.setText(f"{self.tr('Config')}: {task.name}")
|
|
66
|
+
self.task_config_table.setRowCount(len(config))
|
|
67
|
+
for row, (key, value) in enumerate(config.items()):
|
|
68
|
+
if not self.task_config_table.item(row, 0):
|
|
69
|
+
item0 = self.uneditable_item()
|
|
70
|
+
self.task_config_table.setItem(row, 0, item0)
|
|
71
|
+
self.task_config_table.item(row, 0).setText(key)
|
|
72
|
+
config_widget_item(self.task_config_table, row, 1, config, key, value)
|
|
73
|
+
|
|
74
|
+
def update_info_table(self):
|
|
75
|
+
task = self.tasks[self.task_table.selectedIndexes()[0].row()]
|
|
76
|
+
info = task.info
|
|
77
|
+
self.task_info_container.title_label.setText(f"{self.tr('Info')}: {task.name}")
|
|
78
|
+
self.task_info_table.setRowCount(len(info))
|
|
79
|
+
for row, (key, value) in enumerate(info.items()):
|
|
80
|
+
if not self.task_info_table.item(row, 0):
|
|
81
|
+
item0 = self.uneditable_item()
|
|
82
|
+
self.task_info_table.setItem(row, 0, item0)
|
|
83
|
+
self.task_info_table.item(row, 0).setText(key)
|
|
84
|
+
if not self.task_info_table.item(row, 1):
|
|
85
|
+
item1 = self.uneditable_item()
|
|
86
|
+
self.task_info_table.setItem(row, 1, item1)
|
|
87
|
+
self.task_info_table.item(row, 1).setText(value_to_string(value))
|
|
88
|
+
|
|
89
|
+
def create_table(self):
|
|
90
|
+
self.task_table.setRowCount(len(self.tasks)) # Adjust the row count to match the number of attributes
|
|
91
|
+
self.task_table.setColumnCount(len(self.task_labels)) # Name and Value
|
|
92
|
+
self.task_table.setHorizontalHeaderLabels(self.task_labels)
|
|
93
|
+
self.task_table.setSelectionBehavior(QTableWidget.SelectRows)
|
|
94
|
+
self.task_table.setSelectionMode(QTableWidget.SingleSelection)
|
|
95
|
+
self.task_table.selectRow(0)
|
|
96
|
+
self.task_table.itemSelectionChanged.connect(self.update_config_table)
|
|
97
|
+
for row, task in enumerate(self.tasks):
|
|
98
|
+
for i in range(2):
|
|
99
|
+
item = self.uneditable_item()
|
|
100
|
+
self.task_table.setItem(row, i, item)
|
|
101
|
+
op_button = TaskOpButton(task)
|
|
102
|
+
self.task_table.setCellWidget(row, 2, op_button)
|
|
103
|
+
self.update_table()
|
|
104
|
+
|
|
105
|
+
def uneditable_item(self):
|
|
106
|
+
item = QTableWidgetItem()
|
|
107
|
+
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
|
|
108
|
+
return item
|
|
109
|
+
|
|
110
|
+
def update_table(self):
|
|
111
|
+
for row, task in enumerate(self.tasks):
|
|
112
|
+
self.task_table.item(row, 0).setText(task.name)
|
|
113
|
+
status = task.get_status()
|
|
114
|
+
self.task_table.item(row, 1).setText(self.tr(status))
|
|
115
|
+
if status == "Running":
|
|
116
|
+
self.task_table.item(row, 1).setBackground(QColor("green"))
|
|
117
|
+
elif status == "Disabled":
|
|
118
|
+
self.task_table.item(row, 1).setBackground(QColor("red"))
|
|
119
|
+
else:
|
|
120
|
+
self.task_table.item(row, 1).setBackground(QColor(0, 0, 0, 0))
|
|
121
|
+
self.task_table.cellWidget(row, 2).update_task(task)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from PySide6.QtCore import QEvent
|
|
2
|
+
from PySide6.QtWidgets import QTableWidget, QToolTip, QHeaderView
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TooltipTableWidget(QTableWidget):
|
|
6
|
+
|
|
7
|
+
def __init__(self, width_percentages=None):
|
|
8
|
+
super().__init__()
|
|
9
|
+
self.verticalHeader().setVisible(False)
|
|
10
|
+
self.width_percentages = width_percentages
|
|
11
|
+
|
|
12
|
+
# Set the QTableWidget to fill the width of its parent
|
|
13
|
+
self.horizontalHeader().setStretchLastSection(True)
|
|
14
|
+
|
|
15
|
+
# Set the columns to resize to fill the width of the QTableWidget
|
|
16
|
+
self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
|
17
|
+
self.setWordWrap(True)
|
|
18
|
+
|
|
19
|
+
def setItem(self, row, column, item):
|
|
20
|
+
super().setItem(row, column, item)
|
|
21
|
+
|
|
22
|
+
def resizeEvent(self, event):
|
|
23
|
+
if self.width_percentages is not None:
|
|
24
|
+
width = self.width() - self.verticalScrollBar().width()
|
|
25
|
+
for i, percentage in enumerate(self.width_percentages):
|
|
26
|
+
self.setColumnWidth(i, int(width * percentage))
|
|
27
|
+
super().resizeEvent(event)
|
|
28
|
+
|
|
29
|
+
def viewportEvent(self, event):
|
|
30
|
+
if event.type() == QEvent.ToolTip:
|
|
31
|
+
pos = event.pos()
|
|
32
|
+
item = self.itemAt(pos)
|
|
33
|
+
if item:
|
|
34
|
+
if self.visualItemRect(item).width() < self.fontMetrics().horizontalAdvance(item.text()):
|
|
35
|
+
QToolTip.showText(event.globalPos(), item.text(), self)
|
|
36
|
+
else:
|
|
37
|
+
QToolTip.hideText()
|
|
38
|
+
return True
|
|
39
|
+
return super().viewportEvent(event)
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from PySide6.QtWidgets import QMessageBox
|
|
2
|
+
|
|
3
|
+
import autohelper
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def show_alert(title, message):
|
|
7
|
+
# Create a QMessageBox
|
|
8
|
+
msg = QMessageBox()
|
|
9
|
+
|
|
10
|
+
# Set the title and message
|
|
11
|
+
msg.setWindowTitle(title)
|
|
12
|
+
msg.setText(message)
|
|
13
|
+
|
|
14
|
+
msg.setWindowIcon(autohelper.gui.app.icon)
|
|
15
|
+
|
|
16
|
+
# Add a confirm button
|
|
17
|
+
msg.setStandardButtons(QMessageBox.Ok)
|
|
18
|
+
|
|
19
|
+
# Show the message box
|
|
20
|
+
msg.exec()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from PySide6.QtCore import QThread
|
|
2
|
+
|
|
3
|
+
from autohelper.gui.Communicate import communicate
|
|
4
|
+
from autohelper.logging.Logger import get_logger
|
|
5
|
+
|
|
6
|
+
logger = get_logger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class InitWorker(QThread):
|
|
10
|
+
|
|
11
|
+
def __init__(self, fun):
|
|
12
|
+
super().__init__()
|
|
13
|
+
self.fun = fun
|
|
14
|
+
|
|
15
|
+
def run(self):
|
|
16
|
+
self.fun()
|
|
17
|
+
communicate.init.emit(True, "")
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from PySide6.QtCore import Qt
|
|
2
|
+
from PySide6.QtWidgets import QTableWidgetItem
|
|
3
|
+
|
|
4
|
+
from autohelper.gui.widget.UpdateConfigWidgetItem import UpdateConfigWidgetItem, value_to_string
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ListTableWidgetItem(UpdateConfigWidgetItem, QTableWidgetItem):
|
|
8
|
+
|
|
9
|
+
def __init__(self, config, key, value, parent=None):
|
|
10
|
+
UpdateConfigWidgetItem.__init__(self, config, key, value)
|
|
11
|
+
QTableWidgetItem.__init__(self)
|
|
12
|
+
self.setText(value_to_string(value))
|
|
13
|
+
|
|
14
|
+
def setData(self, role, value):
|
|
15
|
+
if role == Qt.EditRole:
|
|
16
|
+
list_value = convert_to_list(value)
|
|
17
|
+
self.set_value(list_value)
|
|
18
|
+
self.setText(value_to_string(list_value))
|
|
19
|
+
super().setData(role, value)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def convert_to_list(s):
|
|
23
|
+
# Replace the Chinese comma with the English comma
|
|
24
|
+
s = s.replace(",", ",")
|
|
25
|
+
|
|
26
|
+
# Split the string by comma and strip the white spaces
|
|
27
|
+
return [item.strip() for item in s.split(",")]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from PySide6.QtCore import Qt
|
|
2
|
+
from PySide6.QtGui import QIntValidator, QDoubleValidator
|
|
3
|
+
from PySide6.QtWidgets import QTableWidgetItem
|
|
4
|
+
|
|
5
|
+
from autohelper.gui.widget.UpdateConfigWidgetItem import UpdateConfigWidgetItem, value_to_string
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NumericTableWidgetItem(UpdateConfigWidgetItem, QTableWidgetItem):
|
|
9
|
+
|
|
10
|
+
def __init__(self, config, key, value, parent=None):
|
|
11
|
+
UpdateConfigWidgetItem.__init__(self, config, key, value)
|
|
12
|
+
QTableWidgetItem.__init__(self)
|
|
13
|
+
|
|
14
|
+
# Determine if the original value is int or float
|
|
15
|
+
if isinstance(value, int):
|
|
16
|
+
self.validator = QIntValidator(0, 999999, self.tableWidget())
|
|
17
|
+
elif isinstance(value, float):
|
|
18
|
+
self.validator = QDoubleValidator(0, 999999, 2, self.tableWidget())
|
|
19
|
+
else:
|
|
20
|
+
raise ValueError("Value must be an int or float")
|
|
21
|
+
self.setText(value_to_string(value))
|
|
22
|
+
|
|
23
|
+
def setData(self, role, value):
|
|
24
|
+
if role == Qt.EditRole:
|
|
25
|
+
state, _, _ = self.validator.validate(str(value), 0)
|
|
26
|
+
if state != QIntValidator.Acceptable:
|
|
27
|
+
return
|
|
28
|
+
if isinstance(self.value, int):
|
|
29
|
+
value = int(value)
|
|
30
|
+
else:
|
|
31
|
+
value = float(value)
|
|
32
|
+
self.set_value(value)
|
|
33
|
+
super().setData(role, value)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from PySide6.QtGui import QPalette
|
|
2
|
+
from PySide6.QtWidgets import QVBoxLayout, QLabel, QFrame, QSizePolicy, QLayout, QWidget, QHBoxLayout, QSpacerItem
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RoundCornerContainer(QFrame):
|
|
6
|
+
def __init__(self, title, child=None, parent=None):
|
|
7
|
+
super(RoundCornerContainer, self).__init__(parent)
|
|
8
|
+
|
|
9
|
+
# Set the layout
|
|
10
|
+
self.top_layout = QHBoxLayout()
|
|
11
|
+
layout = QVBoxLayout(self)
|
|
12
|
+
self.title_label = QLabel(title)
|
|
13
|
+
self.title_label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
|
14
|
+
self.top_layout.addWidget(self.title_label)
|
|
15
|
+
self.top_layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum))
|
|
16
|
+
layout.addLayout(self.top_layout)
|
|
17
|
+
if child is not None and isinstance(child, QWidget):
|
|
18
|
+
child.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
19
|
+
layout.addWidget(child)
|
|
20
|
+
elif child is not None and isinstance(child, QLayout):
|
|
21
|
+
child.setSizeConstraint(QVBoxLayout.SetMaximumSize)
|
|
22
|
+
layout.addLayout(child)
|
|
23
|
+
layout.setStretchFactor(child, 1)
|
|
24
|
+
layout.setStretchFactor(self.title_label, 0)
|
|
25
|
+
|
|
26
|
+
self.setLayout(layout)
|
|
27
|
+
|
|
28
|
+
self.setFrameStyle(QFrame.Panel | QFrame.Raised)
|
|
29
|
+
self.setLineWidth(2)
|
|
30
|
+
|
|
31
|
+
color = self.get_palette_color(QPalette.Base)
|
|
32
|
+
self.setStyleSheet(f"background-color:{color};")
|
|
33
|
+
|
|
34
|
+
def add_top_widget(self, widget):
|
|
35
|
+
self.top_layout.addWidget(widget)
|
|
36
|
+
|
|
37
|
+
def get_palette_color(self, palette_color):
|
|
38
|
+
palette = self.palette()
|
|
39
|
+
return color_to_hex(palette.color(palette_color))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def color_to_hex(color):
|
|
43
|
+
"""Converts a QColor object to a hex string representation.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
color: The QColor object to convert.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
A string representing the hex code of the color (e.g., "#FF0000" for red).
|
|
50
|
+
"""
|
|
51
|
+
red = color.red()
|
|
52
|
+
green = color.green()
|
|
53
|
+
blue = color.blue()
|
|
54
|
+
hex_color = f"#{red:02X}{green:02X}{blue:02X}"
|
|
55
|
+
return hex_color
|
|
56
|
+
|
|
57
|
+
# Usage:
|
|
58
|
+
# widget = RoundCornerContainer("Title")
|
|
59
|
+
# widget.show()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from PySide6.QtGui import QPalette
|
|
2
|
+
from PySide6.QtWidgets import QWidget
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TabWidget(QWidget):
|
|
6
|
+
|
|
7
|
+
def __init__(self):
|
|
8
|
+
super().__init__()
|
|
9
|
+
color = self.get_palette_color(QPalette.Base)
|
|
10
|
+
self.setStyleSheet(f"background-color:{color};")
|
|
11
|
+
|
|
12
|
+
def get_palette_color(self, palette_color):
|
|
13
|
+
palette = self.palette()
|
|
14
|
+
return color_to_hex(palette.color(palette_color))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def color_to_hex(color):
|
|
18
|
+
"""Converts a QColor object to a hex string representation.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
color: The QColor object to convert.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
A string representing the hex code of the color (e.g., "#FF0000" for red).
|
|
25
|
+
"""
|
|
26
|
+
red = color.red()
|
|
27
|
+
green = color.green()
|
|
28
|
+
blue = color.blue()
|
|
29
|
+
hex_color = f"#{red:02X}{green:02X}{blue:02X}"
|
|
30
|
+
return hex_color
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class UpdateConfigWidgetItem:
|
|
2
|
+
def __init__(self, config, key, value):
|
|
3
|
+
self.key = key
|
|
4
|
+
self.config = config
|
|
5
|
+
self.value = value
|
|
6
|
+
|
|
7
|
+
def set_value(self, value):
|
|
8
|
+
self.config[self.key] = value
|
|
9
|
+
self.value = value
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def value_to_string(obj):
|
|
13
|
+
if isinstance(obj, list):
|
|
14
|
+
return ', '.join(map(str, obj))
|
|
15
|
+
else:
|
|
16
|
+
return str(obj)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from PySide6.QtWidgets import QComboBox
|
|
2
|
+
|
|
3
|
+
from autohelper.gui.widget.UpdateConfigWidgetItem import UpdateConfigWidgetItem
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class YesNonWidgetItem(UpdateConfigWidgetItem, QComboBox):
|
|
7
|
+
|
|
8
|
+
def __init__(self, config, key, value):
|
|
9
|
+
UpdateConfigWidgetItem.__init__(self, config, key, value)
|
|
10
|
+
QComboBox.__init__(self)
|
|
11
|
+
self.addItems(["Yes", "No"])
|
|
12
|
+
if value:
|
|
13
|
+
self.setCurrentIndex(0)
|
|
14
|
+
else:
|
|
15
|
+
self.setCurrentIndex(1)
|
|
16
|
+
|
|
17
|
+
self.currentIndexChanged.connect(self.index_changed)
|
|
18
|
+
|
|
19
|
+
def index_changed(self, index):
|
|
20
|
+
if index == 0:
|
|
21
|
+
self.set_value(True)
|
|
22
|
+
else:
|
|
23
|
+
self.set_value(False)
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from autohelper.interaction.BaseInteraction import BaseInteraction
|
|
2
|
+
from autohelper.logging.Logger import get_logger
|
|
3
|
+
|
|
4
|
+
logger = get_logger(__name__)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ADBBaseInteraction(BaseInteraction):
|
|
8
|
+
|
|
9
|
+
def __init__(self, device_manager, capture):
|
|
10
|
+
super().__init__(capture)
|
|
11
|
+
self.device_manager = device_manager
|
|
12
|
+
logger.info(f"width: {self.width}, height: {self.height}")
|
|
13
|
+
if self.width == 0 or self.height == 0:
|
|
14
|
+
logger.warning(f"Could not parse screen resolution.")
|
|
15
|
+
# raise RuntimeError(f"ADBBaseInteraction: Could not parse screen resolution.")
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def width(self):
|
|
19
|
+
return self.device_manager.width
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def height(self):
|
|
23
|
+
return self.device_manager.height
|
|
24
|
+
|
|
25
|
+
def send_key(self, key, down_time=0.02):
|
|
26
|
+
super().send_key(key, down_time)
|
|
27
|
+
self.device_manager.device.shell(f"input keyevent {key}")
|
|
28
|
+
|
|
29
|
+
def click(self, x=-1, y=-1):
|
|
30
|
+
super().click(x, y)
|
|
31
|
+
x = int(x * self.width / self.capture.width)
|
|
32
|
+
y = int(y * self.height / self.capture.height)
|
|
33
|
+
self.device_manager.device.shell(f"input tap {x} {y}")
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from autohelper.feature.Box import Box
|
|
2
|
+
from autohelper.gui.Communicate import communicate
|
|
3
|
+
from autohelper.logging.Logger import get_logger
|
|
4
|
+
|
|
5
|
+
logger = get_logger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseInteraction:
|
|
9
|
+
|
|
10
|
+
def __init__(self, capture):
|
|
11
|
+
self.capture = capture
|
|
12
|
+
|
|
13
|
+
def should_capture(self):
|
|
14
|
+
return True
|
|
15
|
+
|
|
16
|
+
def send_key(self, key, down_time=0.02):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
def move(self, x, y):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def click_relative(self, x, y):
|
|
23
|
+
self.click(int(self.capture.width * x), int(self.capture.height * y))
|
|
24
|
+
|
|
25
|
+
def move_relative(self, x, y):
|
|
26
|
+
self.move(int(self.capture.width * x), int(self.capture.height * y))
|
|
27
|
+
|
|
28
|
+
def click_box(self, box: Box, relative_x=0.5, relative_y=0.5):
|
|
29
|
+
logger.info(f"click_box {box}")
|
|
30
|
+
x, y = box.relative_with_variance(relative_x, relative_y)
|
|
31
|
+
self.click(x, y)
|
|
32
|
+
|
|
33
|
+
def click(self, x=-1, y=-1):
|
|
34
|
+
logger.info(f"click {x, y}")
|
|
35
|
+
communicate.draw_box.emit("click", [Box(max(0, x - 10), max(0, y - 10), 20, 20, name="click")], "green")
|
|
36
|
+
pass
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
import pydirectinput
|
|
5
|
+
|
|
6
|
+
from autohelper.capture.BaseCaptureMethod import BaseCaptureMethod
|
|
7
|
+
from autohelper.interaction.BaseInteraction import BaseInteraction
|
|
8
|
+
from autohelper.logging.Logger import get_logger
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Win32Interaction(BaseInteraction):
|
|
14
|
+
|
|
15
|
+
def __init__(self, capture: BaseCaptureMethod):
|
|
16
|
+
super().__init__(capture)
|
|
17
|
+
self.post = ctypes.windll.user32.PostMessageW
|
|
18
|
+
if not is_admin():
|
|
19
|
+
logger.error(f"You must be an admin to use Win32Interaction")
|
|
20
|
+
|
|
21
|
+
def send_key(self, key, down_time=0.02):
|
|
22
|
+
if not self.capture.clickable():
|
|
23
|
+
return
|
|
24
|
+
pydirectinput.keyDown(key)
|
|
25
|
+
time.sleep(down_time)
|
|
26
|
+
pydirectinput.keyUp(key)
|
|
27
|
+
|
|
28
|
+
def move(self, x, y):
|
|
29
|
+
if not self.capture.clickable():
|
|
30
|
+
return
|
|
31
|
+
x, y = self.capture.get_abs_cords(x, y)
|
|
32
|
+
pydirectinput.moveTo(x, y)
|
|
33
|
+
|
|
34
|
+
def click(self, x=-1, y=-1):
|
|
35
|
+
super().click(x, y)
|
|
36
|
+
if not self.capture.clickable():
|
|
37
|
+
logger.info(f"window in background, not clickable")
|
|
38
|
+
return
|
|
39
|
+
# Convert the x, y position to lParam
|
|
40
|
+
# lParam = win32api.MAKELONG(x, y)
|
|
41
|
+
if x != -1 and y != -1:
|
|
42
|
+
x, y = self.capture.get_abs_cords(x, y)
|
|
43
|
+
logger.info(f"left_click {x, y}")
|
|
44
|
+
pydirectinput.moveTo(x, y)
|
|
45
|
+
pydirectinput.click()
|
|
46
|
+
|
|
47
|
+
def should_capture(self):
|
|
48
|
+
return self.capture.clickable()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def is_admin():
|
|
52
|
+
try:
|
|
53
|
+
# Only Windows users with admin privileges can read the C drive directly
|
|
54
|
+
return ctypes.windll.shell32.IsUserAnAdmin()
|
|
55
|
+
except:
|
|
56
|
+
return False
|
|
File without changes
|