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>
469 lines
14 KiB
Python
469 lines
14 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.
|
||
"""
|
||
"""
|
||
Widget components for the AutoScript orchestration dialog.
|
||
"""
|
||
from PySide6.QtCore import Slot
|
||
from PySide6.QtWidgets import (
|
||
QComboBox,
|
||
QFrame,
|
||
QHBoxLayout,
|
||
QLabel,
|
||
QPushButton,
|
||
QSizePolicy,
|
||
QStackedWidget
|
||
)
|
||
|
||
from gui.ALAutoScriptOrchDialog._helpers import (
|
||
ACTION_OPTIONS,
|
||
COMPARE_OPTIONS,
|
||
LOGIC_OPTIONS,
|
||
encodeValueStr,
|
||
getPresetVars,
|
||
getTypeOrder,
|
||
getValueFromWidget,
|
||
getArithType,
|
||
makeComboWidget,
|
||
makeLabel,
|
||
makeOffsetWidget,
|
||
makeValueWidget,
|
||
makeVarRefCombo,
|
||
)
|
||
|
||
|
||
class ConditionRowFrame(QFrame):
|
||
|
||
def __init__(
|
||
self,
|
||
varMgr,
|
||
parentBlockIndex: int = 0,
|
||
isFirst: bool = False,
|
||
parent = None
|
||
):
|
||
|
||
super().__init__(parent)
|
||
self._varMgr = varMgr
|
||
self._blockIndex = parentBlockIndex
|
||
self._isFirst = isFirst
|
||
self._isBoolMode = False
|
||
self._rawRhsExpr = ""
|
||
|
||
self.setupUi()
|
||
self.connectSignals()
|
||
|
||
def setupUi(
|
||
self
|
||
):
|
||
|
||
self.setUpdatesEnabled(False)
|
||
self.setFrameShape(QFrame.StyledPanel)
|
||
self.setFrameShadow(QFrame.Raised)
|
||
self.setFixedHeight(32)
|
||
Layout = QHBoxLayout(self)
|
||
Layout.setContentsMargins(2, 2, 2, 2)
|
||
Layout.setSpacing(4)
|
||
if self._isFirst:
|
||
self.LogicCombo = None
|
||
else:
|
||
self.LogicCombo = makeComboWidget(LOGIC_OPTIONS, min_width=110, parent=self)
|
||
Layout.addWidget(self.LogicCombo)
|
||
self.LeftVarCombo = QComboBox(self)
|
||
self.LeftVarCombo.setFixedHeight(25)
|
||
self.LeftVarCombo.setMinimumWidth(120)
|
||
self.LeftVarCombo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||
self.populateLeftVarCombo()
|
||
Layout.addWidget(self.LeftVarCombo)
|
||
self.OpCombo = makeComboWidget(COMPARE_OPTIONS, min_width=80, parent=self)
|
||
Layout.addWidget(self.OpCombo)
|
||
self._CompTypeCombo = makeComboWidget([
|
||
("特定值", "literal"),
|
||
("变量", "variable"),
|
||
], min_width=70, parent=self)
|
||
Layout.addWidget(self._CompTypeCombo)
|
||
self.RhsStack = QStackedWidget(self)
|
||
self.RhsStack.setFixedHeight(25)
|
||
self.initLiteralStack()
|
||
self.RhsVarCombo = makeVarRefCombo(self)
|
||
self.RhsStack.addWidget(self.RhsVarCombo)
|
||
self.RhsStack.setCurrentIndex(0)
|
||
Layout.addWidget(self.RhsStack)
|
||
if not self._isFirst:
|
||
self.DeleteBtn = QPushButton("×", self)
|
||
self.DeleteBtn.setFixedSize(25, 25)
|
||
self.DeleteBtn.setStyleSheet("color: red; font-weight: bold;")
|
||
Layout.addWidget(self.DeleteBtn)
|
||
else:
|
||
self.DeleteBtn = None
|
||
Layout.addStretch()
|
||
self.setUpdatesEnabled(True)
|
||
|
||
def populateLeftVarCombo(
|
||
self
|
||
):
|
||
|
||
wasBool = self._isBoolMode
|
||
boolName = None
|
||
if wasBool:
|
||
data = self.LeftVarCombo.currentData()
|
||
if data:
|
||
boolName = data[0]
|
||
self._varMgr.populateCombo(self.LeftVarCombo)
|
||
# Append boolean literal sentinels at the end
|
||
self.LeftVarCombo.insertSeparator(self.LeftVarCombo.count())
|
||
self.LeftVarCombo.addItem("true", ("true", "Boolean"))
|
||
self.LeftVarCombo.addItem("false", ("false", "Boolean"))
|
||
if wasBool and boolName:
|
||
for ci in range(self.LeftVarCombo.count()):
|
||
d = self.LeftVarCombo.itemData(ci)
|
||
if d and d[0] == boolName:
|
||
self.LeftVarCombo.setCurrentIndex(ci)
|
||
break
|
||
|
||
def populateRHSVarCombo(
|
||
self
|
||
):
|
||
|
||
self._varMgr.populateCombo(self.RhsVarCombo)
|
||
|
||
def initLiteralStack(
|
||
self
|
||
):
|
||
|
||
self.LiteralStack = QStackedWidget(self)
|
||
self.LiteralStack.setFixedHeight(25)
|
||
self._literalWidgets = {}
|
||
for vt in getTypeOrder():
|
||
W = makeValueWidget(vt, self.LiteralStack)
|
||
self._literalWidgets[vt] = W
|
||
self.LiteralStack.addWidget(W)
|
||
self.LiteralStack.setCurrentWidget(self._literalWidgets.get("String"))
|
||
self.RhsStack.addWidget(self.LiteralStack)
|
||
|
||
def connectSignals(
|
||
self
|
||
):
|
||
|
||
self.LeftVarCombo.currentIndexChanged.connect(self.onLeftVarChanged)
|
||
self._CompTypeCombo.currentIndexChanged.connect(self.onCompTypeChanged)
|
||
|
||
def getLogic(
|
||
self
|
||
) -> str:
|
||
|
||
return self.LogicCombo.currentData() if self.LogicCombo else ""
|
||
|
||
def updateRHSLiteralWidget(
|
||
self,
|
||
vartype: str
|
||
):
|
||
|
||
if vartype not in self._literalWidgets:
|
||
vartype = "String"
|
||
self.LiteralStack.setCurrentWidget(self._literalWidgets[vartype])
|
||
|
||
def toScript(
|
||
self
|
||
) -> str:
|
||
|
||
data = self.LeftVarCombo.currentData()
|
||
if self._isBoolMode and data:
|
||
return data[0]
|
||
if not data:
|
||
return ""
|
||
name, vartype = data
|
||
# CURRENT_DATE / CURRENT_TIME map to datenow() / timenow()
|
||
if name == "CURRENT_DATE":
|
||
name = "datenow()"
|
||
elif name == "CURRENT_TIME":
|
||
name = "timenow()"
|
||
opSym = self.OpCombo.currentData()
|
||
if self._rawRhsExpr:
|
||
return f"{name} {opSym} {self._rawRhsExpr}"
|
||
isVarRef = (self._CompTypeCombo.currentData() == "variable")
|
||
if isVarRef:
|
||
rd = self.RhsVarCombo.currentData()
|
||
if rd:
|
||
rhsName = rd[0]
|
||
if rhsName == "CURRENT_DATE":
|
||
rhsName = "datenow()"
|
||
elif rhsName == "CURRENT_TIME":
|
||
rhsName = "timenow()"
|
||
return f"{name} {opSym} {rhsName}"
|
||
rhsText = self.RhsVarCombo.currentText().strip()
|
||
if rhsText:
|
||
return f"{name} {opSym} {rhsText}"
|
||
return ""
|
||
w = self._literalWidgets.get(vartype)
|
||
if w:
|
||
rawVal = getValueFromWidget(w)
|
||
encoded = encodeValueStr(rawVal, vartype)
|
||
return f"{name} {opSym} {encoded}"
|
||
return ""
|
||
|
||
def refreshVarCombos(
|
||
self
|
||
):
|
||
|
||
self.populateLeftVarCombo()
|
||
self.populateRHSVarCombo()
|
||
|
||
@Slot(int)
|
||
def onLeftVarChanged(
|
||
self,
|
||
idx
|
||
):
|
||
|
||
self._rawRhsExpr = ""
|
||
if idx < 0:
|
||
return
|
||
data = self.LeftVarCombo.itemData(idx)
|
||
if not data:
|
||
return
|
||
name, vartype = data
|
||
isBool = name in ("true", "false")
|
||
self._isBoolMode = isBool
|
||
self.OpCombo.setVisible(not isBool)
|
||
self._CompTypeCombo.setVisible(not isBool)
|
||
self.RhsStack.setVisible(not isBool)
|
||
if not isBool:
|
||
self.updateRHSLiteralWidget(vartype)
|
||
|
||
@Slot(int)
|
||
def onCompTypeChanged(
|
||
self,
|
||
idx
|
||
):
|
||
|
||
self._rawRhsExpr = ""
|
||
isVar = (self._CompTypeCombo.currentData() == "variable")
|
||
self.RhsStack.setCurrentIndex(1 if isVar else 0)
|
||
if isVar:
|
||
self.populateRHSVarCombo()
|
||
|
||
|
||
class ActionStepFrame(QFrame):
|
||
|
||
def __init__(
|
||
self,
|
||
varMgr,
|
||
parentBlockIndex: int = 0,
|
||
parent = None
|
||
):
|
||
|
||
super().__init__(parent)
|
||
self._varMgr = varMgr
|
||
self._blockIndex = parentBlockIndex
|
||
self._currentTargetType = "String"
|
||
|
||
self.setupUi()
|
||
self.connectSignals()
|
||
|
||
def setupUi(
|
||
self
|
||
):
|
||
|
||
self.setUpdatesEnabled(False)
|
||
self.setFrameShape(QFrame.StyledPanel)
|
||
self.setFrameShadow(QFrame.Raised)
|
||
self.setFixedHeight(35)
|
||
Layout = QHBoxLayout(self)
|
||
Layout.setContentsMargins(2, 2, 2, 2)
|
||
Layout.setSpacing(4)
|
||
self.OpTypeCombo = makeComboWidget(ACTION_OPTIONS, min_width=70, parent=self)
|
||
Layout.addWidget(self.OpTypeCombo)
|
||
Layout.addWidget(makeLabel("设置", self))
|
||
self.TargetCombo = QComboBox(self)
|
||
self.TargetCombo.setFixedHeight(25)
|
||
self.TargetCombo.setMinimumWidth(120)
|
||
self.populateTargetCombo()
|
||
Layout.addWidget(self.TargetCombo)
|
||
Layout.addWidget(makeLabel("为", self))
|
||
self.ValueSrcCombo = makeComboWidget([
|
||
("特定值", "literal"),
|
||
("变量", "variable"),
|
||
], min_width=70, parent=self)
|
||
Layout.addWidget(self.ValueSrcCombo)
|
||
self.ValueStack = QStackedWidget(self)
|
||
self.ValueStack.setFixedHeight(25)
|
||
self.initValueStacks()
|
||
Layout.addWidget(self.ValueStack)
|
||
self.ExistingVarCombo = makeVarRefCombo(self)
|
||
self.ExistingVarCombo.setVisible(False)
|
||
Layout.addWidget(self.ExistingVarCombo)
|
||
self.DeleteBtn = QPushButton("×", self)
|
||
self.DeleteBtn.setFixedSize(25, 25)
|
||
self.DeleteBtn.setStyleSheet("color: red; font-weight: bold;")
|
||
Layout.addWidget(self.DeleteBtn)
|
||
self.setUpdatesEnabled(True)
|
||
|
||
def populateTargetCombo(
|
||
self
|
||
):
|
||
|
||
self.TargetCombo.blockSignals(True)
|
||
self.TargetCombo.clear()
|
||
for p in getPresetVars():
|
||
if p["name"] in ("CURRENT_TIME", "CURRENT_DATE"):
|
||
continue
|
||
info = self._varMgr.getInfoByName(p["name"])
|
||
if info:
|
||
self.TargetCombo.addItem(
|
||
info["display"],
|
||
(info["name"], info["type"])
|
||
)
|
||
self.TargetCombo.blockSignals(False)
|
||
|
||
def initValueStacks(
|
||
self
|
||
):
|
||
|
||
self._literalWidgets = {}
|
||
self._offsetWidgets = {}
|
||
for vt in getTypeOrder():
|
||
self._literalWidgets[vt] = makeValueWidget(vt, self.ValueStack)
|
||
self.ValueStack.addWidget(self._literalWidgets[vt])
|
||
if getArithType(vt):
|
||
self._offsetWidgets[vt] = makeOffsetWidget(vt, self.ValueStack)
|
||
self.ValueStack.addWidget(self._offsetWidgets[vt])
|
||
else:
|
||
Lbl = QLabel("(不支持该操作)", self.ValueStack)
|
||
Lbl.setFixedHeight(25)
|
||
self._offsetWidgets[vt] = Lbl
|
||
self.ValueStack.addWidget(Lbl)
|
||
|
||
def connectSignals(
|
||
self
|
||
):
|
||
|
||
self.OpTypeCombo.currentIndexChanged.connect(self.onOpTypeChanged)
|
||
self.TargetCombo.currentIndexChanged.connect(self.onTargetChanged)
|
||
self.ValueSrcCombo.currentIndexChanged.connect(self.onValueSrcChanged)
|
||
|
||
def getTargetName(
|
||
self
|
||
) -> str:
|
||
|
||
data = self.TargetCombo.currentData()
|
||
return data[0] if data else ""
|
||
|
||
def updateValueWidget(
|
||
self
|
||
):
|
||
|
||
op = self.OpTypeCombo.currentData()
|
||
isArith = (op in ("add", "sub"))
|
||
actualType = self._currentTargetType
|
||
if isArith and actualType in self._offsetWidgets:
|
||
self.ValueStack.setCurrentWidget(self._offsetWidgets[actualType])
|
||
elif actualType in self._literalWidgets:
|
||
self.ValueStack.setCurrentWidget(self._literalWidgets[actualType])
|
||
else:
|
||
self.ValueStack.setCurrentWidget(self._literalWidgets.get("String"))
|
||
|
||
def toScript(
|
||
self
|
||
) -> str:
|
||
"""
|
||
Generate a single line of Lua script from the current widget state.
|
||
"""
|
||
|
||
target = self.getTargetName()
|
||
op = self.OpTypeCombo.currentData()
|
||
if op == "pass":
|
||
return " -- pass"
|
||
if not target:
|
||
return ""
|
||
rawVal = self.getValueRaw()
|
||
vartype = self._currentTargetType
|
||
if op == "set":
|
||
encoded = encodeValueStr(rawVal, vartype)
|
||
return f" {target} = {encoded}"
|
||
elif op == "add":
|
||
if vartype == "Date" and hasattr(self.ValueStack.currentWidget(), "getOffsetDays"):
|
||
days = self.ValueStack.currentWidget().getOffsetDays()
|
||
return f" {target} = dateadd({target}, {days})"
|
||
if vartype == "Time" and hasattr(self.ValueStack.currentWidget(), "getOffsetHours"):
|
||
hours = self.ValueStack.currentWidget().getOffsetHours()
|
||
return f" {target} = timeadd({target}, {hours})"
|
||
return f" {target} = {target} + {rawVal}"
|
||
elif op == "sub":
|
||
if vartype == "Date" and hasattr(self.ValueStack.currentWidget(), "getOffsetDays"):
|
||
days = self.ValueStack.currentWidget().getOffsetDays()
|
||
return f" {target} = dateadd({target}, -{days})"
|
||
if vartype == "Time" and hasattr(self.ValueStack.currentWidget(), "getOffsetHours"):
|
||
hours = self.ValueStack.currentWidget().getOffsetHours()
|
||
return f" {target} = timeadd({target}, -{hours})"
|
||
return f" {target} = {target} - {rawVal}"
|
||
return ""
|
||
|
||
def getValueRaw(
|
||
self
|
||
) -> str:
|
||
|
||
if self.ValueSrcCombo.currentData() == "variable":
|
||
data = self.ExistingVarCombo.currentData()
|
||
return data[0] if data else ""
|
||
w = self.ValueStack.currentWidget()
|
||
if w:
|
||
return getValueFromWidget(w)
|
||
return ""
|
||
|
||
def refreshVarCombos(
|
||
self
|
||
):
|
||
|
||
currentData = self.TargetCombo.currentData()
|
||
self.populateTargetCombo()
|
||
if currentData:
|
||
for i in range(self.TargetCombo.count()):
|
||
d = self.TargetCombo.itemData(i)
|
||
if d and d[0] == currentData[0]:
|
||
self.TargetCombo.setCurrentIndex(i)
|
||
break
|
||
self._varMgr.populateCombo(self.ExistingVarCombo)
|
||
|
||
@Slot(int)
|
||
def onTargetChanged(
|
||
self,
|
||
idx
|
||
):
|
||
|
||
if idx < 0:
|
||
return
|
||
data = self.TargetCombo.itemData(idx)
|
||
if not data:
|
||
return
|
||
_, vartype = data
|
||
self._currentTargetType = vartype
|
||
self.updateValueWidget()
|
||
self.onValueSrcChanged(self.ValueSrcCombo.currentIndex())
|
||
|
||
@Slot(int)
|
||
def onOpTypeChanged(
|
||
self,
|
||
idx
|
||
):
|
||
|
||
self.updateValueWidget()
|
||
|
||
@Slot(int)
|
||
def onValueSrcChanged(
|
||
self,
|
||
idx
|
||
):
|
||
|
||
isVar = (self.ValueSrcCombo.currentData() == "variable")
|
||
self.ValueStack.setVisible(not isVar)
|
||
self.ExistingVarCombo.setVisible(isVar)
|
||
if isVar:
|
||
self._varMgr.populateCombo(self.ExistingVarCombo)
|
||
else:
|
||
self.updateValueWidget()
|