mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-06-17 23:13:03 +08:00
refactor(autoscript): 完善 Lua 错误分类与 Date/Time 严格校验,清理死代码并补齐类型注解
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+129
-30
@@ -14,6 +14,15 @@ from datetime import (
|
|||||||
|
|
||||||
from lupa import LuaRuntime as _LuaRuntime
|
from lupa import LuaRuntime as _LuaRuntime
|
||||||
|
|
||||||
|
try:
|
||||||
|
from lupa.lua55 import LuaError as _LuaError, LuaSyntaxError as _LuaSyntaxError
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from lupa.lua54 import LuaError as _LuaError, LuaSyntaxError as _LuaSyntaxError
|
||||||
|
except ImportError:
|
||||||
|
_LuaError = Exception
|
||||||
|
_LuaSyntaxError = Exception
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["execute", "addTargetVar", "resetEngine"]
|
__all__ = ["execute", "addTargetVar", "resetEngine"]
|
||||||
|
|
||||||
@@ -23,11 +32,19 @@ _TARGET_VARS: dict[str, dict] = {}
|
|||||||
_lua = None
|
_lua = None
|
||||||
|
|
||||||
# Built-in meta variable definitions (name / type / display-name)
|
# Built-in meta variable definitions (name / type / display-name)
|
||||||
META_VARS = {
|
META_VARS: dict[str, dict[str, str]] = {
|
||||||
"CURRENT_DATE": {"name": "CURRENT_DATE", "type": "Date", "display": "当前日期"},
|
"CURRENT_DATE": {"name": "CURRENT_DATE", "type": "Date", "display": "当前日期"},
|
||||||
"CURRENT_TIME": {"name": "CURRENT_TIME", "type": "Time", "display": "当前时间"},
|
"CURRENT_TIME": {"name": "CURRENT_TIME", "type": "Time", "display": "当前时间"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Per-type fallback value when target_data entry is missing.
|
||||||
|
_DEFAULT_BY_TYPE: dict[str, str | int | float | bool] = {
|
||||||
|
"String": "",
|
||||||
|
"Int": 0,
|
||||||
|
"Float": 0.0,
|
||||||
|
"Boolean": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _getLua(
|
def _getLua(
|
||||||
):
|
):
|
||||||
@@ -170,6 +187,62 @@ def _assignPath(
|
|||||||
d[key_path[-1]] = value
|
d[key_path[-1]] = value
|
||||||
|
|
||||||
|
|
||||||
|
def _pyTypeToASType(
|
||||||
|
value
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Map a Python runtime value to its AutoScript type name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return "Boolean"
|
||||||
|
if isinstance(value, int):
|
||||||
|
return "Int"
|
||||||
|
if isinstance(value, float):
|
||||||
|
return "Float"
|
||||||
|
if isinstance(value, str):
|
||||||
|
return "String"
|
||||||
|
return "Unknown"
|
||||||
|
|
||||||
|
|
||||||
|
def _checkDateFormat(
|
||||||
|
date_str: str,
|
||||||
|
var_name: str = "",
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Validate that *date_str* is in YYYY-MM-DD format.
|
||||||
|
Raises ValueError with a descriptive message on failure.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prefix = f"Date 类型变量 '{var_name}' 的" if var_name else ""
|
||||||
|
try:
|
||||||
|
date.fromisoformat(date_str)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(
|
||||||
|
f"{prefix}值 '{date_str}' 不是合法的日期格式,"
|
||||||
|
f"应为 YYYY-MM-DD"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _checkTimeFormat(
|
||||||
|
time_str: str,
|
||||||
|
var_name: str = "",
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Validate that *time_str* is in HH:MM format.
|
||||||
|
Raises ValueError with a descriptive message on failure.
|
||||||
|
"""
|
||||||
|
|
||||||
|
prefix = f"Time 类型变量 '{var_name}' 的" if var_name else ""
|
||||||
|
try:
|
||||||
|
datetime.strptime(time_str, "%H:%M")
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(
|
||||||
|
f"{prefix}值 '{time_str}' 不是合法的时间格式,"
|
||||||
|
f"应为 HH:MM"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _checkType(
|
def _checkType(
|
||||||
var_name: str,
|
var_name: str,
|
||||||
var_type: str,
|
var_type: str,
|
||||||
@@ -188,17 +261,17 @@ def _checkType(
|
|||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Date 类型变量 '{var_name}' 只能接受日期字符串,"
|
f"Date 类型变量 '{var_name}' 只能接受日期字符串,"
|
||||||
f"不能接受 {type(value).__name__} 类型"
|
f"不能接受 {_pyTypeToASType(value)} 类型"
|
||||||
)
|
)
|
||||||
date.fromisoformat(value)
|
_checkDateFormat(value, var_name)
|
||||||
return
|
return
|
||||||
if var_type == "Time":
|
if var_type == "Time":
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Time 类型变量 '{var_name}' 只能接受时间字符串,"
|
f"Time 类型变量 '{var_name}' 只能接受时间字符串,"
|
||||||
f"不能接受 {type(value).__name__} 类型"
|
f"不能接受 {_pyTypeToASType(value)} 类型"
|
||||||
)
|
)
|
||||||
datetime.strptime(value, "%H:%M")
|
_checkTimeFormat(value, var_name)
|
||||||
return
|
return
|
||||||
if var_type == "Int":
|
if var_type == "Int":
|
||||||
if isinstance(value, bool):
|
if isinstance(value, bool):
|
||||||
@@ -207,7 +280,7 @@ def _checkType(
|
|||||||
)
|
)
|
||||||
if not isinstance(value, int) and not (isinstance(value, float) and value == int(value)):
|
if not isinstance(value, int) and not (isinstance(value, float) and value == int(value)):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Int 类型变量 '{var_name}' 不能接受 {type(value).__name__} 类型的值"
|
f"Int 类型变量 '{var_name}' 不能接受 {_pyTypeToASType(value)} 类型的值"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if var_type == "Float":
|
if var_type == "Float":
|
||||||
@@ -217,19 +290,19 @@ def _checkType(
|
|||||||
)
|
)
|
||||||
if not isinstance(value, (int, float)):
|
if not isinstance(value, (int, float)):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Float 类型变量 '{var_name}' 不能接受 {type(value).__name__} 类型的值"
|
f"Float 类型变量 '{var_name}' 不能接受 {_pyTypeToASType(value)} 类型的值"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if var_type == "Boolean":
|
if var_type == "Boolean":
|
||||||
if not isinstance(value, bool):
|
if not isinstance(value, bool):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Boolean 类型变量 '{var_name}' 不能接受 {type(value).__name__} 类型的值"
|
f"Boolean 类型变量 '{var_name}' 不能接受 {_pyTypeToASType(value)} 类型的值"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if var_type == "String":
|
if var_type == "String":
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"String 类型变量 '{var_name}' 不能接受 {type(value).__name__} 类型的值"
|
f"String 类型变量 '{var_name}' 不能接受 {_pyTypeToASType(value)} 类型的值"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -238,7 +311,7 @@ def addTargetVar(
|
|||||||
name: str,
|
name: str,
|
||||||
var_type: str,
|
var_type: str,
|
||||||
key_path: list,
|
key_path: list,
|
||||||
display_name: str = None,
|
_display_name: str = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Register a new target variable bound to a path in the application data dict.
|
Register a new target variable bound to a path in the application data dict.
|
||||||
@@ -247,7 +320,6 @@ def addTargetVar(
|
|||||||
name (str): The canonical variable name (e.g. "RESERVE_DATE").
|
name (str): The canonical variable name (e.g. "RESERVE_DATE").
|
||||||
var_type (str): "Int" | "Float" | "Boolean" | "Date" | "Time" | "String".
|
var_type (str): "Int" | "Float" | "Boolean" | "Date" | "Time" | "String".
|
||||||
key_path (list): Nested path into target_data, e.g. ["reserve_info", "date"].
|
key_path (list): Nested path into target_data, e.g. ["reserve_info", "date"].
|
||||||
display_name (str): Optional Chinese alias (unused by the engine).
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
upper_name = name.upper().strip()
|
upper_name = name.upper().strip()
|
||||||
@@ -263,6 +335,7 @@ def resetEngine(
|
|||||||
Reset the engine to its initial state: clear all target variables
|
Reset the engine to its initial state: clear all target variables
|
||||||
and release the Lua runtime.
|
and release the Lua runtime.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
global _TARGET_VARS, _lua
|
global _TARGET_VARS, _lua
|
||||||
_TARGET_VARS = {}
|
_TARGET_VARS = {}
|
||||||
_lua = None
|
_lua = None
|
||||||
@@ -274,6 +347,9 @@ def _push(
|
|||||||
"""
|
"""
|
||||||
Push target_data values into Lua globals.
|
Push target_data values into Lua globals.
|
||||||
Date / Time strings are converted to native Lua types (timestamp / minutes).
|
Date / Time strings are converted to native Lua types (timestamp / minutes).
|
||||||
|
|
||||||
|
Raises ValueError for missing / malformed Date or Time values so that
|
||||||
|
execute() can surface them as user-visible AutoScript execution errors.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
lua = _getLua()
|
lua = _getLua()
|
||||||
@@ -285,28 +361,27 @@ def _push(
|
|||||||
key_path = info["key_path"]
|
key_path = info["key_path"]
|
||||||
vt = info["type"]
|
vt = info["type"]
|
||||||
raw = _navigatePath(target_data, key_path)
|
raw = _navigatePath(target_data, key_path)
|
||||||
|
|
||||||
if vt == "Date":
|
if vt == "Date":
|
||||||
if raw and isinstance(raw, str):
|
if not isinstance(raw, str) or not raw.strip():
|
||||||
try:
|
raise ValueError(
|
||||||
date.fromisoformat(raw.strip())
|
f"Date 类型变量 '{var_name}' 对应的数据为空或不是字符串类型,"
|
||||||
except (ValueError, AttributeError):
|
f"请检查路径 {key_path} 的值是否为合法的日期字符串 (YYYY-MM-DD)"
|
||||||
raw = "2099-01-01"
|
)
|
||||||
else:
|
raw = raw.strip()
|
||||||
raw = "2099-01-01"
|
_checkDateFormat(raw, var_name)
|
||||||
g[var_name] = _toDate(raw)
|
g[var_name] = _toDate(raw)
|
||||||
elif vt == "Time":
|
elif vt == "Time":
|
||||||
if raw and isinstance(raw, str):
|
if not isinstance(raw, str) or not raw.strip():
|
||||||
try:
|
raise ValueError(
|
||||||
datetime.strptime(raw.strip(), "%H:%M")
|
f"Time 类型变量 '{var_name}' 对应的数据为空或不是字符串类型,"
|
||||||
except (ValueError, AttributeError):
|
f"请检查路径 {key_path} 的值是否为合法的时间字符串 (HH:MM)"
|
||||||
raw = "00:00"
|
)
|
||||||
else:
|
raw = raw.strip()
|
||||||
raw = "00:00"
|
_checkTimeFormat(raw, var_name)
|
||||||
g[var_name] = _toTime(raw)
|
g[var_name] = _toTime(raw)
|
||||||
else:
|
else:
|
||||||
if raw is None:
|
if raw is None:
|
||||||
raw = "" if vt == "String" else 0 if vt == "Int" else 0.0 if vt == "Float" else False
|
raw = _DEFAULT_BY_TYPE.get(vt, False)
|
||||||
g[var_name] = raw
|
g[var_name] = raw
|
||||||
|
|
||||||
|
|
||||||
@@ -326,7 +401,7 @@ def _pull(
|
|||||||
for var_name, info in _TARGET_VARS.items():
|
for var_name, info in _TARGET_VARS.items():
|
||||||
try:
|
try:
|
||||||
lua_val = g[var_name]
|
lua_val = g[var_name]
|
||||||
except (KeyError, AttributeError):
|
except KeyError:
|
||||||
continue
|
continue
|
||||||
vt = info["type"]
|
vt = info["type"]
|
||||||
if vt == "Date":
|
if vt == "Date":
|
||||||
@@ -339,6 +414,20 @@ def _pull(
|
|||||||
_assignPath(target_data, info["key_path"], lua_val)
|
_assignPath(target_data, info["key_path"], lua_val)
|
||||||
|
|
||||||
|
|
||||||
|
def _cleanLuaError(
|
||||||
|
raw_msg: str
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Strip internal source prefix and stack traceback from a Lua error message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
msg = raw_msg.replace('[string "<python>"]:', "").strip()
|
||||||
|
stack_idx = msg.find("stack traceback:")
|
||||||
|
if stack_idx != -1:
|
||||||
|
msg = msg[:stack_idx].strip()
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
def execute(
|
def execute(
|
||||||
script_text: str,
|
script_text: str,
|
||||||
target_data: dict,
|
target_data: dict,
|
||||||
@@ -366,9 +455,19 @@ def execute(
|
|||||||
|
|
||||||
if not script_text or not script_text.strip():
|
if not script_text or not script_text.strip():
|
||||||
return
|
return
|
||||||
_push(target_data)
|
|
||||||
try:
|
try:
|
||||||
|
_push(target_data)
|
||||||
_getLua().execute(script_text)
|
_getLua().execute(script_text)
|
||||||
_pull(target_data)
|
_pull(target_data)
|
||||||
|
except _LuaSyntaxError as e:
|
||||||
|
raise ValueError(
|
||||||
|
f"AutoScript 语法错误: {_cleanLuaError(str(e))}"
|
||||||
|
)
|
||||||
|
except _LuaError as e:
|
||||||
|
raise ValueError(
|
||||||
|
f"AutoScript 运行时错误: {_cleanLuaError(str(e))}"
|
||||||
|
)
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValueError(f"AutoScript 数据错误: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"AutoScript 执行错误: {e}")
|
raise ValueError(f"AutoScript 未知错误: {e}")
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ __all__ = [
|
|||||||
# Key paths into target_data dict for each target variable.
|
# Key paths into target_data dict for each target variable.
|
||||||
# (name, type, key_path, display_name)
|
# (name, type, key_path, display_name)
|
||||||
_TARGET_VAR_DEFS = [
|
_TARGET_VAR_DEFS = [
|
||||||
("USERNAME", "String",["username"], "用户名"),
|
("USERNAME", "String", ["username"], "用户名"),
|
||||||
("USER_ENABLE", "Boolean",["enabled"], "用户启用"),
|
("USER_ENABLE", "Boolean",["enabled"], "用户启用"),
|
||||||
("RESERVE_DATE", "Date", ["reserve_info", "date"], "预约日期"),
|
("RESERVE_DATE", "Date", ["reserve_info", "date"], "预约日期"),
|
||||||
("RESERVE_BEGIN_TIME", "Time", ["reserve_info", "begin_time", "time"], "预约开始时间"),
|
("RESERVE_BEGIN_TIME", "Time", ["reserve_info", "begin_time", "time"], "预约开始时间"),
|
||||||
("RESERVE_END_TIME", "Time", ["reserve_info", "end_time", "time"], "预约结束时间"),
|
("RESERVE_END_TIME", "Time", ["reserve_info", "end_time", "time"], "预约结束时间"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# All variables (display_name -> (name, type)), derived from target vars + meta vars.
|
# All variables (display_name -> (name, type)), derived from target vars + meta vars.
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ class ALAutoScriptEditDialog(QDialog):
|
|||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._fontSize = 19
|
self._fontSize = 13
|
||||||
self._mockWidgets = {}
|
self._mockWidgets = {}
|
||||||
|
|
||||||
self.setupUi()
|
self.setupUi()
|
||||||
@@ -267,17 +267,17 @@ class ALAutoScriptEditDialog(QDialog):
|
|||||||
basicLayout.setContentsMargins(4, 4, 4, 4)
|
basicLayout.setContentsMargins(4, 4, 4, 4)
|
||||||
basicLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
basicLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||||
controlButtons = [
|
controlButtons = [
|
||||||
("if", "if then\n \nend"),
|
("如果 (if...)", "if then\n \nend"),
|
||||||
("elseif", "elseif then\n "),
|
("再如果 (elseif...)", "elseif then\n "),
|
||||||
("else", "else"),
|
("否则 (else)", "else"),
|
||||||
("end", "end"),
|
("结束 (end)", "end"),
|
||||||
("-- pass", "-- pass"),
|
("跳过 (pass)", "-- pass"),
|
||||||
]
|
]
|
||||||
self._addButtonsToGrid(basicLayout, controlButtons, 0, 0, 5)
|
self._addButtonsToGrid(basicLayout, controlButtons, 0, 0, 3)
|
||||||
assignButtons = [
|
assignButtons = [
|
||||||
("=", " = "),
|
("赋值 (=)", " = "),
|
||||||
]
|
]
|
||||||
self._addButtonsToGrid(basicLayout, assignButtons, 0, 5, 1)
|
self._addButtonsToGrid(basicLayout, assignButtons, 1, 2, 3)
|
||||||
tabWidget.addTab(basicWidget, "基本语法")
|
tabWidget.addTab(basicWidget, "基本语法")
|
||||||
operatorWidget = QWidget()
|
operatorWidget = QWidget()
|
||||||
operatorLayout = QGridLayout(operatorWidget)
|
operatorLayout = QGridLayout(operatorWidget)
|
||||||
@@ -285,24 +285,24 @@ class ALAutoScriptEditDialog(QDialog):
|
|||||||
operatorLayout.setContentsMargins(4, 4, 4, 4)
|
operatorLayout.setContentsMargins(4, 4, 4, 4)
|
||||||
operatorLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
operatorLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||||
arithmeticButtons = [
|
arithmeticButtons = [
|
||||||
("+", " + "),
|
("加 (+)", " + "),
|
||||||
("-", " - "),
|
("减 (-)", " - "),
|
||||||
]
|
]
|
||||||
self._addButtonsToGrid(operatorLayout, arithmeticButtons, 0, 0, 2)
|
self._addButtonsToGrid(operatorLayout, arithmeticButtons, 0, 0, 3)
|
||||||
compareButtons = [
|
compareButtons = [
|
||||||
("==", " == "),
|
("等于 (==)", " == "),
|
||||||
("~=", " ~= "),
|
("不等于 (~=)", " ~= "),
|
||||||
(">", " > "),
|
("大于 (>)", " > "),
|
||||||
("<", " < "),
|
("小于 (<)", " < "),
|
||||||
(">=", " >= "),
|
("大于等于 (>=)", " >= "),
|
||||||
("<=", " <= "),
|
("小于等于 (<=)", " <= "),
|
||||||
]
|
]
|
||||||
self._addButtonsToGrid(operatorLayout, compareButtons, 1, 0, 6)
|
self._addButtonsToGrid(operatorLayout, compareButtons, 1, 0, 3)
|
||||||
logic_buttons = [
|
logic_buttons = [
|
||||||
("and", " and "),
|
("且 (and)", " and "),
|
||||||
("or", " or "),
|
("或 (or)", " or "),
|
||||||
]
|
]
|
||||||
self._addButtonsToGrid(operatorLayout, logic_buttons, 2, 0, 2)
|
self._addButtonsToGrid(operatorLayout, logic_buttons, 2, 0, 3)
|
||||||
tabWidget.addTab(operatorWidget, "运算符")
|
tabWidget.addTab(operatorWidget, "运算符")
|
||||||
literalWidget = QWidget()
|
literalWidget = QWidget()
|
||||||
literalLayout = QGridLayout(literalWidget)
|
literalLayout = QGridLayout(literalWidget)
|
||||||
@@ -310,15 +310,15 @@ class ALAutoScriptEditDialog(QDialog):
|
|||||||
literalLayout.setContentsMargins(4, 4, 4, 4)
|
literalLayout.setContentsMargins(4, 4, 4, 4)
|
||||||
literalLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
literalLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||||
bool_buttons = [
|
bool_buttons = [
|
||||||
("true", "true"),
|
("真 (true)", "true"),
|
||||||
("false", "false"),
|
("假 (false)", "false"),
|
||||||
]
|
]
|
||||||
self._addButtonsToGrid(literalLayout, bool_buttons, 0, 0, 2)
|
self._addButtonsToGrid(literalLayout, bool_buttons, 0, 0, 3)
|
||||||
dateTimeButtons = [
|
dateTimeButtons = [
|
||||||
("日期", '"2099-01-01"'),
|
("日期", '"2099-01-01"'),
|
||||||
("时间", '"00:00"'),
|
("时间", '"00:00"'),
|
||||||
]
|
]
|
||||||
self._addButtonsToGrid(literalLayout, dateTimeButtons, 1, 0, 2)
|
self._addButtonsToGrid(literalLayout, dateTimeButtons, 1, 0, 3)
|
||||||
hintButtons = [
|
hintButtons = [
|
||||||
("字符串", '"请输入文本"'),
|
("字符串", '"请输入文本"'),
|
||||||
("数字", "123"),
|
("数字", "123"),
|
||||||
@@ -334,16 +334,15 @@ class ALAutoScriptEditDialog(QDialog):
|
|||||||
varButtons = [
|
varButtons = [
|
||||||
(display_name, name) for display_name, (name, _) in ALL_VARIABLES.items()
|
(display_name, name) for display_name, (name, _) in ALL_VARIABLES.items()
|
||||||
]
|
]
|
||||||
|
self._addButtonsToGrid(varLayout, varButtons, 0, 0, 3)
|
||||||
self._addButtonsToGrid(varLayout, varButtons, 0, 0, 5)
|
|
||||||
tabWidget.addTab(varWidget, "变量")
|
tabWidget.addTab(varWidget, "变量")
|
||||||
mockPanel = self._createMockPanel()
|
mockPanel = self._createMockPanel()
|
||||||
mockPanel.setMinimumWidth(260)
|
mockPanel.setMinimumWidth(260)
|
||||||
splitter.addWidget(tabWidget)
|
splitter.addWidget(tabWidget)
|
||||||
splitter.addWidget(mockPanel)
|
splitter.addWidget(mockPanel)
|
||||||
splitter.setStretchFactor(0, 1)
|
splitter.setStretchFactor(0, 1)
|
||||||
splitter.setStretchFactor(1, 0)
|
splitter.setStretchFactor(1, 1)
|
||||||
splitter.setSizes([660, 400])
|
splitter.setSizes([530, 530])
|
||||||
parent_layout.addWidget(splitter)
|
parent_layout.addWidget(splitter)
|
||||||
|
|
||||||
|
|
||||||
@@ -367,7 +366,6 @@ class ALAutoScriptEditDialog(QDialog):
|
|||||||
btn.setFixedHeight(25)
|
btn.setFixedHeight(25)
|
||||||
btn.setToolTip(f"插入: {template}")
|
btn.setToolTip(f"插入: {template}")
|
||||||
grid_layout.addWidget(btn, row, col)
|
grid_layout.addWidget(btn, row, col)
|
||||||
|
|
||||||
col += 1
|
col += 1
|
||||||
if col >= start_col + max_columns:
|
if col >= start_col + max_columns:
|
||||||
col = start_col
|
col = start_col
|
||||||
@@ -585,9 +583,6 @@ class ALAutoScriptEditDialog(QDialog):
|
|||||||
self
|
self
|
||||||
):
|
):
|
||||||
|
|
||||||
font = self.textEdit.font()
|
|
||||||
font.setPointSize(self._fontSize)
|
|
||||||
self.textEdit.setFont(font)
|
|
||||||
self.textEdit.setStyleSheet(
|
self.textEdit.setStyleSheet(
|
||||||
"QPlainTextEdit {"
|
"QPlainTextEdit {"
|
||||||
" font-family: 'Courier New', 'Consolas', monospace;"
|
" font-family: 'Courier New', 'Consolas', monospace;"
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ DATE_RELATIVE_OPTIONS = [
|
|||||||
DATE_OFFSET_UNITS = [
|
DATE_OFFSET_UNITS = [
|
||||||
("天", "days"),
|
("天", "days"),
|
||||||
("周", "weeks"),
|
("周", "weeks"),
|
||||||
|
# NOTE: "月" and "年" use fixed day counts (30 / 365), not calendar months/years,
|
||||||
|
# because date_add() works with second-level offsets (n * 86400).
|
||||||
("月", "months"),
|
("月", "months"),
|
||||||
("年", "years"),
|
("年", "years"),
|
||||||
]
|
]
|
||||||
@@ -657,6 +659,8 @@ def _encodeDateOrTime(
|
|||||||
|
|
||||||
s = raw_value.strip()
|
s = raw_value.strip()
|
||||||
up = s.upper()
|
up = s.upper()
|
||||||
|
# Input comes from widget values — single binary expressions only (e.g. "A + 3",
|
||||||
|
# "CURRENT_DATE + 5"). Multi-operator expressions are not produced by the UI.
|
||||||
m_arith_spaced = re.match(r'^(.+?)\s+([+-])\s+(.+)$', s)
|
m_arith_spaced = re.match(r'^(.+?)\s+([+-])\s+(.+)$', s)
|
||||||
m_arith_nospace = re.match(r'^([A-Za-z_]\w*)([+-])(\d+|[A-Za-z_]\w*)$', s)
|
m_arith_nospace = re.match(r'^([A-Za-z_]\w*)([+-])(\d+|[A-Za-z_]\w*)$', s)
|
||||||
m_arith = m_arith_spaced or m_arith_nospace
|
m_arith = m_arith_spaced or m_arith_nospace
|
||||||
|
|||||||
Reference in New Issue
Block a user