mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-06-18 07:23:03 +08:00
bb63ee6f03
将所有 self.xxx 形式的 Qt 控件属性名以及 Qt 对象局部变量由 snake_case 重命名为 PascalCase,提升代码可读性和一致性。涉及 14 个文件,涵盖: - AutoScript 编排/编辑对话框子模块 - 配置/主窗口/用户树/座位图等核心界面组件 - 定时任务管理相关界面 - 状态标签/浏览器驱动下载对话框 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
670 lines
21 KiB
Python
670 lines
21 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Copyright (c) 2026 KenanZhu.
|
||
All rights reserved.
|
||
|
||
This software is provided "as is", without any warranty of any kind.
|
||
You may use, modify, and distribute this file under the terms of the MIT License.
|
||
See the LICENSE file for details.
|
||
"""
|
||
from copy import deepcopy
|
||
|
||
from PySide6.QtCore import (
|
||
QDate,
|
||
QSize,
|
||
Qt,
|
||
QTime,
|
||
QTimer,
|
||
Slot
|
||
)
|
||
from PySide6.QtGui import (
|
||
QColor,
|
||
QFont,
|
||
QIcon,
|
||
QSyntaxHighlighter,
|
||
QTextCharFormat,
|
||
)
|
||
from PySide6.QtWidgets import (
|
||
QApplication,
|
||
QComboBox,
|
||
QDateEdit,
|
||
QDialog,
|
||
QDialogButtonBox,
|
||
QDoubleSpinBox,
|
||
QFormLayout,
|
||
QFrame,
|
||
QGridLayout,
|
||
QGroupBox,
|
||
QHBoxLayout,
|
||
QHeaderView,
|
||
QLabel,
|
||
QLineEdit,
|
||
QMessageBox,
|
||
QPlainTextEdit,
|
||
QPushButton,
|
||
QSpinBox,
|
||
QSplitter,
|
||
QStyle,
|
||
QTabWidget,
|
||
QTableWidget,
|
||
QTableWidgetItem,
|
||
QTimeEdit,
|
||
QVBoxLayout,
|
||
QWidget,
|
||
)
|
||
|
||
from autoscript import (
|
||
createAllVariablesTable,
|
||
createMockTargetData,
|
||
createTargetVarDefs,
|
||
createEngine,
|
||
)
|
||
|
||
|
||
class ALScriptHighlighter(QSyntaxHighlighter):
|
||
"""
|
||
Syntax highlighter for Lua-based AutoScript.
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
parent = None
|
||
):
|
||
|
||
super().__init__(parent)
|
||
self._rules = []
|
||
|
||
KeywordFmt = QTextCharFormat()
|
||
KeywordFmt.setForeground(QColor("#569CD6"))
|
||
KeywordFmt.setFontWeight(QFont.Weight.Bold)
|
||
for kw in [
|
||
"if", "elseif", "else", "end", "then",
|
||
"and", "or", "not",
|
||
"local", "function", "return", "nil",
|
||
]:
|
||
self._rules.append((r"\b" + kw + r"\b", KeywordFmt))
|
||
BoolFmt = QTextCharFormat()
|
||
BoolFmt.setForeground(QColor("#4FC1FF"))
|
||
BoolFmt.setFontWeight(QFont.Weight.Bold)
|
||
self._rules.append((r"\btrue\b", BoolFmt))
|
||
self._rules.append((r"\bfalse\b", BoolFmt))
|
||
CmpFmt = QTextCharFormat()
|
||
CmpFmt.setForeground(QColor("#C586C0"))
|
||
CmpFmt.setFontWeight(QFont.Weight.Normal)
|
||
for op in [r"==", r"~=", r">=", r"<=", r">", r"<"]:
|
||
self._rules.append((op, CmpFmt))
|
||
ArithFmt = QTextCharFormat()
|
||
ArithFmt.setForeground(QColor("#C586C0"))
|
||
ArithFmt.setFontWeight(QFont.Weight.Normal)
|
||
for op in [r"\+", r"-", r"\*", r"/", r"\.\."]:
|
||
self._rules.append((op, ArithFmt))
|
||
FuncFmt = QTextCharFormat()
|
||
FuncFmt.setForeground(QColor("#DCDCAA"))
|
||
FuncFmt.setFontWeight(QFont.Weight.Normal)
|
||
for fn in [ "time", "date", "datenow", "timenow", "dateadd", "timeadd"]:
|
||
self._rules.append((r"\b" + fn + r"\b", FuncFmt))
|
||
VarFmt = QTextCharFormat()
|
||
VarFmt.setForeground(QColor("#9CDCFE"))
|
||
VarFmt.setFontWeight(QFont.Weight.Normal)
|
||
var_names = [name for _, (name, _) in createAllVariablesTable().items()]
|
||
for var in var_names:
|
||
self._rules.append((r"\b" + var + r"\b", VarFmt))
|
||
StrFmt = QTextCharFormat()
|
||
StrFmt.setForeground(QColor("#CE9178"))
|
||
StrFmt.setFontWeight(QFont.Weight.Normal)
|
||
self._rules.append((r'"[^"]*"', StrFmt))
|
||
self._rules.append((r"'[^']*'", StrFmt))
|
||
NumFmt = QTextCharFormat()
|
||
NumFmt.setForeground(QColor("#B5CEA8"))
|
||
NumFmt.setFontWeight(QFont.Weight.Normal)
|
||
self._rules.append((r"\b\d+(?:\.\d+)?\b", NumFmt))
|
||
CommentFmt = QTextCharFormat()
|
||
CommentFmt.setForeground(QColor("#6A9955"))
|
||
CommentFmt.setFontItalic(True)
|
||
self._rules.append((r"--[^\n]*", CommentFmt))
|
||
|
||
def highlightBlock(
|
||
self,
|
||
text
|
||
):
|
||
|
||
import re
|
||
for pattern, fmt in self._rules:
|
||
for match in re.finditer(pattern, text, re.IGNORECASE):
|
||
start = match.start()
|
||
length = match.end() - match.start()
|
||
self.setFormat(start, length, fmt)
|
||
|
||
|
||
class _DebugResultDialog(QDialog):
|
||
|
||
def __init__(
|
||
self,
|
||
changes: list,
|
||
parent = None
|
||
):
|
||
|
||
super().__init__(parent)
|
||
self.setWindowTitle("调试运行结果 - AutoLibrary")
|
||
self.setMinimumSize(600, 200)
|
||
DbgLayout = QVBoxLayout(self)
|
||
DbgTable = QTableWidget(len(changes), 3)
|
||
DbgTable.setHorizontalHeaderLabels(["目标变量", "原始数据", "运行后数据"])
|
||
DbgTable.horizontalHeader().setStretchLastSection(True)
|
||
DbgTable.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||
DbgTable.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
|
||
for row, (display_name, name, var_type, before_val, after_val) in enumerate(changes):
|
||
label = f"{display_name}: {name}({var_type})"
|
||
DbgTable.setItem(row, 0, QTableWidgetItem(label))
|
||
DbgTable.setItem(row, 1, QTableWidgetItem(str(before_val)))
|
||
DbgTable.setItem(row, 2, QTableWidgetItem(str(after_val)))
|
||
DbgLayout.addWidget(DbgTable)
|
||
DbgBtnBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok)
|
||
DbgBtnBox.button(QDialogButtonBox.StandardButton.Ok).setText("确定")
|
||
DbgBtnBox.accepted.connect(self.accept)
|
||
DbgLayout.addWidget(DbgBtnBox)
|
||
|
||
|
||
class _TabToSpacesEditor(QPlainTextEdit):
|
||
|
||
def keyPressEvent(
|
||
self,
|
||
event
|
||
):
|
||
|
||
if event.key() == Qt.Key.Key_Tab:
|
||
self.insertPlainText(" ")
|
||
return
|
||
super().keyPressEvent(event)
|
||
|
||
|
||
class ALAutoScriptEditDialog(QDialog):
|
||
|
||
def __init__(
|
||
self,
|
||
parent = None,
|
||
script: str = "",
|
||
mockData: dict = None
|
||
):
|
||
|
||
super().__init__(parent)
|
||
self._fontSize = 21
|
||
self._mockWidgets = {}
|
||
|
||
self.setupUi()
|
||
self.connectSignals()
|
||
self.TextEdit.setPlainText(script)
|
||
self._Highlighter = ALScriptHighlighter(
|
||
self.TextEdit.document()
|
||
)
|
||
if mockData:
|
||
self.setMockData(mockData)
|
||
|
||
def setupUi(
|
||
self
|
||
):
|
||
|
||
self.setWindowTitle("AutoScript 编辑 - AutoLibrary")
|
||
self.setMinimumSize(660, 600)
|
||
Layout = QVBoxLayout(self)
|
||
Layout.setSpacing(3)
|
||
Layout.setContentsMargins(3, 3, 3, 3)
|
||
ToolbarLayout = QHBoxLayout()
|
||
self.ZoomInBtn = QPushButton("+")
|
||
self.ZoomInBtn.setFixedSize(25, 25)
|
||
self.ZoomOutBtn = QPushButton("-")
|
||
self.ZoomOutBtn.setFixedSize(25, 25)
|
||
self.ZoomResetBtn = QPushButton("")
|
||
self.ZoomResetBtn.setIcon(QIcon(":/res/icons/Reset.svg"))
|
||
self.ZoomResetBtn.setIconSize(QSize(20, 20))
|
||
self.ZoomResetBtn.setFixedSize(25, 25)
|
||
self.ZoomResetBtn.setToolTip("重置缩放")
|
||
self.ZoomLabel = QLabel(f"{self._fontSize}px")
|
||
self.ZoomLabel.setFixedHeight(25)
|
||
self.OrchBtn = QPushButton("编排")
|
||
self.OrchBtn.setFixedHeight(25)
|
||
self.OrchBtn.setToolTip("可视化生成 AutoScript 代码并插入到光标位置")
|
||
ToolbarLayout.addWidget(self.OrchBtn)
|
||
self.DebugBtn = QPushButton("▶ 调试运行")
|
||
self.DebugBtn.setFixedHeight(25)
|
||
self.DebugBtn.setToolTip("使用右侧模拟数据执行脚本,查看目标变量变化")
|
||
ToolbarLayout.addWidget(self.DebugBtn)
|
||
Sep = QFrame()
|
||
Sep.setFrameShape(QFrame.Shape.VLine)
|
||
Sep.setFrameShadow(QFrame.Shadow.Sunken)
|
||
Sep.setFixedWidth(1)
|
||
ToolbarLayout.addWidget(Sep)
|
||
ToolbarLayout.addWidget(self.ZoomInBtn)
|
||
ToolbarLayout.addWidget(self.ZoomOutBtn)
|
||
ToolbarLayout.addWidget(self.ZoomResetBtn)
|
||
ToolbarLayout.addWidget(self.ZoomLabel)
|
||
ToolbarLayout.addStretch()
|
||
self.CopyBtn = QPushButton("")
|
||
self.CopyBtn.setIcon(QIcon(":/res/icons/Copy.svg"))
|
||
self.CopyBtn.setIconSize(QSize(20, 20))
|
||
self.CopyBtn.setFixedSize(25, 25)
|
||
self.CopyBtn.setToolTip("复制脚本")
|
||
ToolbarLayout.addWidget(self.CopyBtn)
|
||
Layout.addLayout(ToolbarLayout)
|
||
self.TextEdit = _TabToSpacesEditor(self)
|
||
self.TextEdit.setTabStopDistance(40)
|
||
self.TextEdit.setLineWrapMode(
|
||
QPlainTextEdit.LineWrapMode.NoWrap
|
||
)
|
||
self.TextEdit.setStyleSheet(
|
||
"QPlainTextEdit {"
|
||
" font-family: 'Courier New', 'Consolas', monospace;"
|
||
f" font-size: {self._fontSize}px;"
|
||
"}"
|
||
)
|
||
Layout.addWidget(self.TextEdit)
|
||
self.createButtonPanel(Layout)
|
||
self.BtnBox = QDialogButtonBox(
|
||
QDialogButtonBox.StandardButton.Ok |
|
||
QDialogButtonBox.StandardButton.Cancel
|
||
)
|
||
self.BtnBox.button(QDialogButtonBox.StandardButton.Ok).setText("确定")
|
||
self.BtnBox.button(QDialogButtonBox.StandardButton.Cancel).setText("取消")
|
||
Layout.addWidget(self.BtnBox)
|
||
|
||
def createButtonPanel(
|
||
self,
|
||
ParentLayout
|
||
):
|
||
|
||
Splitter = QSplitter(Qt.Orientation.Horizontal)
|
||
TabWidget = QTabWidget()
|
||
TabWidget.setMaximumHeight(150)
|
||
BasicWidget = QWidget()
|
||
BasicLayout = QGridLayout(BasicWidget)
|
||
BasicLayout.setSpacing(4)
|
||
BasicLayout.setContentsMargins(4, 4, 4, 4)
|
||
BasicLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||
controlButtons = [
|
||
("如果 (if...)", "if then\n \nend"),
|
||
("再如果 (elseif...)", "elseif then\n "),
|
||
("否则 (else)", "else"),
|
||
("结束 (end)", "end"),
|
||
("跳过 (pass)", "-- pass"),
|
||
]
|
||
self.addButtonsToGrid(BasicLayout, controlButtons, 0, 0, 3)
|
||
assignButtons = [
|
||
("赋值 (=)", " = "),
|
||
]
|
||
self.addButtonsToGrid(BasicLayout, assignButtons, 1, 2, 3)
|
||
TabWidget.addTab(BasicWidget, "基本语法")
|
||
OperatorWidget = QWidget()
|
||
OperatorLayout = QGridLayout(OperatorWidget)
|
||
OperatorLayout.setSpacing(4)
|
||
OperatorLayout.setContentsMargins(4, 4, 4, 4)
|
||
OperatorLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||
arithmeticButtons = [
|
||
("加 (+)", " + "),
|
||
("减 (-)", " - "),
|
||
]
|
||
self.addButtonsToGrid(OperatorLayout, arithmeticButtons, 0, 0, 3)
|
||
compareButtons = [
|
||
("等于 (==)", " == "),
|
||
("不等于 (~=)", " ~= "),
|
||
("大于 (>)", " > "),
|
||
("小于 (<)", " < "),
|
||
("大于等于 (>=)", " >= "),
|
||
("小于等于 (<=)", " <= "),
|
||
]
|
||
self.addButtonsToGrid(OperatorLayout, compareButtons, 1, 0, 3)
|
||
logic_buttons = [
|
||
("且 (and)", " and "),
|
||
("或 (or)", " or "),
|
||
]
|
||
self.addButtonsToGrid(OperatorLayout, logic_buttons, 2, 0, 3)
|
||
TabWidget.addTab(OperatorWidget, "运算符")
|
||
LiteralWidget = QWidget()
|
||
LiteralLayout = QGridLayout(LiteralWidget)
|
||
LiteralLayout.setSpacing(4)
|
||
LiteralLayout.setContentsMargins(4, 4, 4, 4)
|
||
LiteralLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||
bool_buttons = [
|
||
("真 (true)", "true"),
|
||
("假 (false)", "false"),
|
||
]
|
||
self.addButtonsToGrid(LiteralLayout, bool_buttons, 0, 0, 3)
|
||
dateTimeButtons = [
|
||
("日期", 'date(2026, 1, 1)'),
|
||
("时间", 'time(0, 0)'),
|
||
]
|
||
self.addButtonsToGrid(LiteralLayout, dateTimeButtons, 1, 0, 3)
|
||
hintButtons = [
|
||
("字符串", '"请输入文本"'),
|
||
("数字", "123"),
|
||
("注释", "-- 请输入注释"),
|
||
]
|
||
self.addButtonsToGrid(LiteralLayout, hintButtons, 2, 0, 3)
|
||
TabWidget.addTab(LiteralWidget, "字面量")
|
||
VarWidget = QWidget()
|
||
VarLayout = QGridLayout(VarWidget)
|
||
VarLayout.setSpacing(4)
|
||
VarLayout.setContentsMargins(4, 4, 4, 4)
|
||
VarLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||
varButtons = [
|
||
(display_name, name) for display_name, (name, _) in createAllVariablesTable().items()
|
||
]
|
||
self.addButtonsToGrid(VarLayout, varButtons, 0, 0, 3)
|
||
TabWidget.addTab(VarWidget, "变量")
|
||
FuncWidget = QWidget()
|
||
FuncLayout = QGridLayout(FuncWidget)
|
||
FuncLayout.setSpacing(4)
|
||
FuncLayout.setContentsMargins(4, 4, 4, 4)
|
||
FuncLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||
funcButtons = [
|
||
("datenow()", "datenow()", "返回当前日期的 Unix 时间戳"),
|
||
("timenow()", "timenow()", "返回当前时间在一天中的分钟数"),
|
||
("dateadd(day, n)", "dateadd(, )", "日期偏移: dateadd(日期时间戳, 天数)"),
|
||
("timeadd(time, n)", "timeadd(, )", "时间偏移: timeadd(分钟数, 分钟数)"),
|
||
]
|
||
for i, (text, template, tooltip) in enumerate(funcButtons):
|
||
Btn = QPushButton(text)
|
||
Btn.setProperty("template", template)
|
||
Btn.clicked.connect(self.insertTemplate)
|
||
Btn.setFixedWidth(100)
|
||
Btn.setFixedHeight(25)
|
||
Btn.setToolTip(tooltip)
|
||
FuncLayout.addWidget(Btn, i // 2, i % 2)
|
||
TabWidget.addTab(FuncWidget, "工具函数")
|
||
MockPanel = self.createMockPanel()
|
||
MockPanel.setMinimumWidth(260)
|
||
Splitter.addWidget(TabWidget)
|
||
Splitter.addWidget(MockPanel)
|
||
Splitter.setStretchFactor(0, 1)
|
||
Splitter.setStretchFactor(1, 1)
|
||
Splitter.setSizes([530, 530])
|
||
ParentLayout.addWidget(Splitter)
|
||
|
||
def addButtonsToGrid(
|
||
self,
|
||
grid_layout,
|
||
buttons,
|
||
start_row,
|
||
start_col,
|
||
max_columns
|
||
):
|
||
|
||
col = start_col
|
||
row = start_row
|
||
|
||
for btn_text, template in buttons:
|
||
Btn = QPushButton(btn_text)
|
||
Btn.setProperty("template", template)
|
||
Btn.clicked.connect(self.insertTemplate)
|
||
Btn.setFixedWidth(100)
|
||
Btn.setFixedHeight(25)
|
||
Btn.setToolTip(f"插入: {template}")
|
||
grid_layout.addWidget(Btn, row, col)
|
||
col += 1
|
||
if col >= start_col + max_columns:
|
||
col = start_col
|
||
row += 1
|
||
|
||
def createMockPanel(
|
||
self
|
||
) -> QGroupBox:
|
||
|
||
Group = QGroupBox("模拟目标数据")
|
||
Form = QFormLayout(Group)
|
||
Form.setSpacing(4)
|
||
Form.setContentsMargins(5, 10, 5, 5)
|
||
self._mockWidgets = {}
|
||
mockData = createMockTargetData()
|
||
for name, var_type, key_path, display_name in createTargetVarDefs():
|
||
d = mockData
|
||
for key in key_path:
|
||
d = d[key]
|
||
default = d
|
||
Widget = self.makeMockInput(var_type, default)
|
||
Label = QLabel(f"{display_name}: {name}({var_type})")
|
||
Form.addRow(Label, Widget)
|
||
self._mockWidgets[name] = (Widget, var_type, key_path)
|
||
return Group
|
||
|
||
def makeMockInput(
|
||
self,
|
||
var_type: str,
|
||
default
|
||
) -> QWidget:
|
||
|
||
if var_type == "String":
|
||
W = QLineEdit()
|
||
W.setText(str(default))
|
||
return W
|
||
if var_type == "Boolean":
|
||
W = QComboBox()
|
||
W.addItems(["是", "否"])
|
||
W.setCurrentIndex(0 if default else 1)
|
||
return W
|
||
if var_type == "Date":
|
||
W = QDateEdit()
|
||
W.setCalendarPopup(True)
|
||
W.setDisplayFormat("yyyy-MM-dd")
|
||
W.setDate(QDate.fromString(str(default), "yyyy-MM-dd"))
|
||
return W
|
||
if var_type == "Time":
|
||
W = QTimeEdit()
|
||
W.setDisplayFormat("HH:mm")
|
||
W.setTime(QTime.fromString(str(default), "HH:mm"))
|
||
return W
|
||
if var_type == "Int":
|
||
W = QSpinBox()
|
||
W.setMinimum(-999999)
|
||
W.setMaximum(999999)
|
||
W.setValue(int(default) if default else 0)
|
||
return W
|
||
if var_type == "Float":
|
||
W = QDoubleSpinBox()
|
||
W.setMinimum(-999999.0)
|
||
W.setMaximum(999999.0)
|
||
W.setDecimals(2)
|
||
W.setValue(float(default) if default else 0.0)
|
||
return W
|
||
W = QLineEdit()
|
||
W.setText(str(default))
|
||
return W
|
||
|
||
def getMockData(
|
||
self
|
||
) -> dict:
|
||
|
||
data = {}
|
||
for name, var_type, key_path, display_name in createTargetVarDefs():
|
||
widget, _, _ = self._mockWidgets[name]
|
||
value = self.getMockValue(widget, var_type)
|
||
d = data
|
||
for key in key_path[:-1]:
|
||
d = d.setdefault(key, {})
|
||
d[key_path[-1]] = value
|
||
return data
|
||
|
||
def setMockData(
|
||
self,
|
||
data: dict
|
||
):
|
||
|
||
if not data:
|
||
return
|
||
for name, var_type, key_path, display_name in createTargetVarDefs():
|
||
d = data
|
||
try:
|
||
for key in key_path:
|
||
d = d[key]
|
||
except (KeyError, TypeError):
|
||
continue
|
||
widget, _, _ = self._mockWidgets[name]
|
||
self.setMockValue(widget, var_type, d)
|
||
|
||
def getMockValue(
|
||
self,
|
||
widget: QWidget,
|
||
var_type: str
|
||
):
|
||
|
||
if var_type == "Boolean":
|
||
return widget.currentIndex() == 0
|
||
if var_type == "Date":
|
||
return widget.date().toString("yyyy-MM-dd")
|
||
if var_type == "Time":
|
||
return widget.time().toString("HH:mm")
|
||
if var_type == "Int":
|
||
return widget.value()
|
||
if var_type == "Float":
|
||
return widget.value()
|
||
return widget.text()
|
||
|
||
def setMockValue(
|
||
self,
|
||
widget: QWidget,
|
||
var_type: str,
|
||
value
|
||
):
|
||
|
||
if var_type == "Boolean":
|
||
widget.setCurrentIndex(0 if value else 1)
|
||
elif var_type == "Date":
|
||
widget.setDate(QDate.fromString(str(value), "yyyy-MM-dd"))
|
||
elif var_type == "Time":
|
||
widget.setTime(QTime.fromString(str(value), "HH:mm"))
|
||
elif var_type == "Int":
|
||
widget.setValue(int(value))
|
||
elif var_type == "Float":
|
||
widget.setValue(float(value))
|
||
else:
|
||
widget.setText(str(value))
|
||
|
||
def connectSignals(
|
||
self
|
||
):
|
||
|
||
self.BtnBox.accepted.connect(self.accept)
|
||
self.BtnBox.rejected.connect(self.reject)
|
||
self.OrchBtn.clicked.connect(self.onOpenOrchDialog)
|
||
self.DebugBtn.clicked.connect(self.onDebugRun)
|
||
self.ZoomInBtn.clicked.connect(self.onZoomIn)
|
||
self.ZoomOutBtn.clicked.connect(self.onZoomOut)
|
||
self.ZoomResetBtn.clicked.connect(self.onZoomReset)
|
||
self.CopyBtn.clicked.connect(self.onCopy)
|
||
|
||
def getScript(
|
||
self
|
||
) -> str:
|
||
|
||
return self.TextEdit.toPlainText()
|
||
|
||
def updateFontSize(
|
||
self
|
||
):
|
||
|
||
self.TextEdit.setStyleSheet(
|
||
"QPlainTextEdit {"
|
||
" font-family: 'Courier New', 'Consolas', monospace;"
|
||
f" font-size: {self._fontSize}px;"
|
||
"}"
|
||
)
|
||
self.ZoomLabel.setText(f"{self._fontSize}px")
|
||
|
||
@Slot()
|
||
def insertTemplate(
|
||
self
|
||
):
|
||
|
||
Btn = self.sender()
|
||
if not isinstance(Btn, QPushButton):
|
||
return
|
||
template = Btn.property("template")
|
||
if not template:
|
||
return
|
||
cursor = self.TextEdit.textCursor()
|
||
cursor.insertText(template)
|
||
|
||
@Slot()
|
||
def onZoomIn(
|
||
self
|
||
):
|
||
|
||
self._fontSize = min(self._fontSize + 2, 40)
|
||
self.updateFontSize()
|
||
|
||
@Slot()
|
||
def onZoomOut(
|
||
self
|
||
):
|
||
|
||
self._fontSize = max(self._fontSize - 2, 8)
|
||
self.updateFontSize()
|
||
|
||
@Slot()
|
||
def onZoomReset(
|
||
self
|
||
):
|
||
|
||
self._fontSize = 21
|
||
self.updateFontSize()
|
||
|
||
@Slot()
|
||
def onCopy(
|
||
self
|
||
):
|
||
|
||
Clipboard = QApplication.clipboard()
|
||
Clipboard.setText(self.TextEdit.toPlainText())
|
||
self.CopyBtn.setEnabled(False)
|
||
QTimer.singleShot(2000, lambda: (
|
||
self.CopyBtn.setEnabled(True)
|
||
))
|
||
|
||
@Slot()
|
||
def onOpenOrchDialog(
|
||
self
|
||
):
|
||
|
||
from gui.ALAutoScriptOrchDialog import ALAutoScriptOrchDialog
|
||
Dlg = ALAutoScriptOrchDialog(self)
|
||
if Dlg.exec() == QDialog.DialogCode.Accepted:
|
||
script = Dlg.getScript()
|
||
if script:
|
||
cursor = self.TextEdit.textCursor()
|
||
cursor.insertText(script)
|
||
Dlg.deleteLater()
|
||
|
||
@Slot()
|
||
def onDebugRun(
|
||
self
|
||
):
|
||
|
||
script = self.TextEdit.toPlainText().strip()
|
||
if not script:
|
||
QMessageBox.warning(self, "提示", "脚本内容为空。")
|
||
return
|
||
target_data = self.getMockData()
|
||
before = deepcopy(target_data)
|
||
try:
|
||
engine = createEngine()
|
||
engine.execute(script, target_data)
|
||
except ValueError as e:
|
||
QMessageBox.warning(self, "运行错误", str(e))
|
||
return
|
||
changes = []
|
||
for name, var_type, key_path, display_name in createTargetVarDefs():
|
||
before_val = before
|
||
after_val = target_data
|
||
try:
|
||
for key in key_path:
|
||
before_val = before_val[key]
|
||
after_val = after_val[key]
|
||
except (KeyError, TypeError):
|
||
continue
|
||
if before_val != after_val:
|
||
changes.append((display_name, name, var_type, before_val, after_val))
|
||
if not changes:
|
||
QMessageBox.information(self, "调试运行", "目标变量未发生变化。")
|
||
return
|
||
Dlg = _DebugResultDialog(changes, self)
|
||
Dlg.exec()
|
||
Dlg.deleteLater()
|