diff --git a/src/autoscript/ASEngine.py b/src/autoscript/ASEngine.py index feee592..e8623a4 100644 --- a/src/autoscript/ASEngine.py +++ b/src/autoscript/ASEngine.py @@ -45,7 +45,6 @@ _DEFAULT_BY_TYPE: dict[str, str | int | float | bool] = { "Boolean": False, } - def _getLua( ): """ @@ -59,7 +58,6 @@ def _getLua( _registerHelpers(_lua) return _lua - def _sandbox( lua, ) -> None: @@ -92,7 +90,6 @@ def _sandbox( end """) - def _registerHelpers( lua, ) -> None: @@ -154,7 +151,6 @@ def _registerHelpers( end """) - def _navigatePath( data: dict, key_path: list, @@ -171,7 +167,6 @@ def _navigatePath( return default return d.get(key_path[-1], default) - def _assignPath( data: dict, key_path: list, @@ -186,7 +181,6 @@ def _assignPath( d = d.setdefault(key, {}) d[key_path[-1]] = value - def _pyTypeToASType( value ) -> str: @@ -204,7 +198,6 @@ def _pyTypeToASType( return "String" return "Unknown" - def _checkDateFormat( date_str: str, var_name: str = "", @@ -223,7 +216,6 @@ def _checkDateFormat( f"应为 YYYY-MM-DD" ) - def _checkTimeFormat( time_str: str, var_name: str = "", @@ -242,7 +234,6 @@ def _checkTimeFormat( f"应为 HH:MM" ) - def _checkType( var_name: str, var_type: str, @@ -306,7 +297,6 @@ def _checkType( ) return - def addTargetVar( name: str, var_type: str, @@ -328,7 +318,6 @@ def addTargetVar( "key_path": key_path, } - def resetEngine( ) -> None: """ @@ -340,7 +329,6 @@ def resetEngine( _TARGET_VARS = {} _lua = None - def _push( target_data: dict, ) -> None: @@ -384,7 +372,6 @@ def _push( raw = _DEFAULT_BY_TYPE.get(vt, False) g[var_name] = raw - def _pull( target_data: dict, ) -> None: @@ -413,7 +400,6 @@ def _pull( _checkType(var_name, vt, lua_val) _assignPath(target_data, info["key_path"], lua_val) - def _cleanLuaError( raw_msg: str ) -> str: @@ -427,7 +413,6 @@ def _cleanLuaError( msg = msg[:stack_idx].strip() return msg - def execute( script_text: str, target_data: dict, diff --git a/src/autoscript/__init__.py b/src/autoscript/__init__.py index 3f85baf..2315f78 100644 --- a/src/autoscript/__init__.py +++ b/src/autoscript/__init__.py @@ -55,7 +55,6 @@ _MOCK_TYPE_VALUES = { "Float": 0.0, } - def buildMockTargetData( ) -> dict: """ @@ -70,7 +69,6 @@ def buildMockTargetData( d[key_path[-1]] = _MOCK_TYPE_VALUES.get(var_type, "") return data - def registerDefaultTargetVars( ) -> None: """ diff --git a/src/base/LibOperator.py b/src/base/LibOperator.py index de57392..c1ae287 100644 --- a/src/base/LibOperator.py +++ b/src/base/LibOperator.py @@ -29,7 +29,6 @@ class LibOperator(MsgBase): super().__init__(input_queue, output_queue) - def _waitResponseLoad( self ) -> bool: diff --git a/src/base/MsgBase.py b/src/base/MsgBase.py index 8ac1aaf..68c9ae0 100644 --- a/src/base/MsgBase.py +++ b/src/base/MsgBase.py @@ -58,7 +58,6 @@ class MsgBase: except RuntimeError: self._logger = None - def _showMsg( self, msg: str @@ -66,7 +65,6 @@ class MsgBase: self._output_queue.put(f"[{self._class_name:<15}] >>> : {msg}") - def _showTrace( self, msg: str, @@ -79,7 +77,6 @@ class MsgBase: if self._logger and not no_log: self._logger.log(level, msg) - def _showLog( self, msg: str, @@ -89,7 +86,6 @@ class MsgBase: if self._logger: self._logger.log(level, msg) - def _waitMsg( self, timeout: float = 1.0 diff --git a/src/base/__init__.py b/src/base/__init__.py index 9f385bf..bac4fcf 100644 --- a/src/base/__init__.py +++ b/src/base/__init__.py @@ -1,8 +1,7 @@ - """ Base module for the AutoLibrary project. Here are the classes and modules in this package: - - MsgBase: Base class for messages.\ + - MsgBase: Base class for messages. - LibOperator: Base class for library operators. """ \ No newline at end of file diff --git a/src/boot/AppInitializer.py b/src/boot/AppInitializer.py index 6267892..4512368 100644 --- a/src/boot/AppInitializer.py +++ b/src/boot/AppInitializer.py @@ -16,7 +16,7 @@ from managers.config.ConfigManager import instance as configInstance from managers.driver.WebDriverManager import instance as webdriverInstance -def initializeLogManager( +def _initializeLogManager( ) -> bool: app_dir = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppDataLocation) @@ -27,7 +27,7 @@ def initializeLogManager( logInstance(log_dir) return True -def initializeConfigManager( +def _initializeConfigManager( ) -> bool: logger = logInstance().getLogger("AppInitializer") @@ -49,7 +49,7 @@ def initializeConfigManager( configInstance(new_config_dir) return True -def initializeWebDriverManager( +def _initializeWebDriverManager( ) -> bool: logger = logInstance().getLogger("AppInitializer") @@ -66,11 +66,17 @@ def initializeWebDriverManager( def initializeApp( ) -> bool: + """ + Initialize the application components - if not initializeLogManager(): + Order: + LogManager -> ConfigManager -> WebDriverManager + """ + + if not _initializeLogManager(): return False - if not initializeConfigManager(): + if not _initializeConfigManager(): return False - if not initializeWebDriverManager(): + if not _initializeWebDriverManager(): return False return True diff --git a/src/gui/ALAboutDialog.py b/src/gui/ALAboutDialog.py index 024e61d..4441403 100644 --- a/src/gui/ALAboutDialog.py +++ b/src/gui/ALAboutDialog.py @@ -9,14 +9,17 @@ See the LICENSE file for details. """ import platform +from PySide6.QtCore import ( + Qt, + QTimer +) from PySide6.QtGui import ( - QIcon, QFont + QFont, + QIcon ) from PySide6.QtWidgets import ( - QDialog, QApplication -) -from PySide6.QtCore import ( - QTimer, Qt + QApplication, + QDialog ) from gui.ALVersionInfo import ( @@ -38,7 +41,6 @@ class ALAboutDialog(QDialog, Ui_ALAboutDialog): self.modifyUi() self.connectSignals() - def modifyUi( self ): @@ -51,14 +53,12 @@ class ALAboutDialog(QDialog, Ui_ALAboutDialog): self.AboutInfoBrowser.setFont(browser_font) self.AboutInfoBrowser.setTextInteractionFlags(Qt.TextBrowserInteraction) - def connectSignals( self ): self.CopyButton.clicked.connect(self.copyAboutInfo) - def generateAboutText( self ) -> str: @@ -91,7 +91,6 @@ GitHub: =)", " >= "), ("小于等于 (<=)", " <= "), ] - self._addButtonsToGrid(operatorLayout, compareButtons, 1, 0, 3) + self.addButtonsToGrid(operatorLayout, compareButtons, 1, 0, 3) logic_buttons = [ ("且 (and)", " and "), ("或 (or)", " or "), ] - self._addButtonsToGrid(operatorLayout, logic_buttons, 2, 0, 3) + self.addButtonsToGrid(operatorLayout, logic_buttons, 2, 0, 3) tabWidget.addTab(operatorWidget, "运算符") literalWidget = QWidget() literalLayout = QGridLayout(literalWidget) @@ -313,18 +310,18 @@ class ALAutoScriptEditDialog(QDialog): ("真 (true)", "true"), ("假 (false)", "false"), ] - self._addButtonsToGrid(literalLayout, bool_buttons, 0, 0, 3) + self.addButtonsToGrid(literalLayout, bool_buttons, 0, 0, 3) dateTimeButtons = [ ("日期", '"2099-01-01"'), ("时间", '"00:00"'), ] - self._addButtonsToGrid(literalLayout, dateTimeButtons, 1, 0, 3) + self.addButtonsToGrid(literalLayout, dateTimeButtons, 1, 0, 3) hintButtons = [ ("字符串", '"请输入文本"'), ("数字", "123"), ("注释", "-- 请输入注释"), ] - self._addButtonsToGrid(literalLayout, hintButtons, 2, 0, 3) + self.addButtonsToGrid(literalLayout, hintButtons, 2, 0, 3) tabWidget.addTab(literalWidget, "字面量") varWidget = QWidget() varLayout = QGridLayout(varWidget) @@ -334,9 +331,9 @@ class ALAutoScriptEditDialog(QDialog): varButtons = [ (display_name, name) for display_name, (name, _) in ALL_VARIABLES.items() ] - self._addButtonsToGrid(varLayout, varButtons, 0, 0, 3) + self.addButtonsToGrid(varLayout, varButtons, 0, 0, 3) tabWidget.addTab(varWidget, "变量") - mockPanel = self._createMockPanel() + mockPanel = self.createMockPanel() mockPanel.setMinimumWidth(260) splitter.addWidget(tabWidget) splitter.addWidget(mockPanel) @@ -345,8 +342,7 @@ class ALAutoScriptEditDialog(QDialog): splitter.setSizes([530, 530]) parent_layout.addWidget(splitter) - - def _addButtonsToGrid( + def addButtonsToGrid( self, grid_layout, buttons, @@ -361,7 +357,7 @@ class ALAutoScriptEditDialog(QDialog): for btn_text, template in buttons: btn = QPushButton(btn_text) btn.setProperty("template", template) - btn.clicked.connect(self._insertTemplate) + btn.clicked.connect(self.insertTemplate) btn.setFixedWidth(100) btn.setFixedHeight(25) btn.setToolTip(f"插入: {template}") @@ -371,22 +367,7 @@ class ALAutoScriptEditDialog(QDialog): col = start_col row += 1 - @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) - - - def _createMockPanel( + def createMockPanel( self ) -> QGroupBox: @@ -397,14 +378,13 @@ class ALAutoScriptEditDialog(QDialog): self._mockWidgets = {} for name, var_type, key_path, display_name in _TARGET_VAR_DEFS: default = _MOCK_TYPE_VALUES.get(var_type, "") - widget = self._makeMockInput(var_type, default) + 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( + def makeMockInput( self, var_type: str, default @@ -447,7 +427,6 @@ class ALAutoScriptEditDialog(QDialog): w.setText(str(default)) return w - def getMockData( self ) -> dict: @@ -455,14 +434,13 @@ class ALAutoScriptEditDialog(QDialog): data = {} for name, var_type, key_path, display_name in _TARGET_VAR_DEFS: widget, _, _ = self._mockWidgets[name] - value = self._getMockValue(widget, var_type) + 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 @@ -478,10 +456,9 @@ class ALAutoScriptEditDialog(QDialog): except (KeyError, TypeError): continue widget, _, _ = self._mockWidgets[name] - self._setMockValue(widget, var_type, d) + self.setMockValue(widget, var_type, d) - - def _getMockValue( + def getMockValue( self, widget: QWidget, var_type: str @@ -499,8 +476,7 @@ class ALAutoScriptEditDialog(QDialog): return widget.value() return widget.text() - - def _setMockValue( + def setMockValue( self, widget: QWidget, var_type: str, @@ -520,6 +496,103 @@ class ALAutoScriptEditDialog(QDialog): 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()) + original = self.copyBtn.text() + self.copyBtn.setText("已复制") + self.copyBtn.setEnabled(False) + QTimer.singleShot(2000, lambda: ( + self.copyBtn.setText(original), + 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( @@ -556,90 +629,3 @@ class ALAutoScriptEditDialog(QDialog): dlg = _DebugResultDialog(changes, self) dlg.exec() dlg.deleteLater() - - - 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 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 = 13 - self.updateFontSize() - - @Slot() - def onCopy( - self - ): - - clipboard = QApplication.clipboard() - clipboard.setText(self.textEdit.toPlainText()) - original = self.copyBtn.text() - self.copyBtn.setText("已复制") - self.copyBtn.setEnabled(False) - QTimer.singleShot(2000, lambda: ( - self.copyBtn.setText(original), - 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() diff --git a/src/gui/ALAutoScriptOrchDialog/_blocks.py b/src/gui/ALAutoScriptOrchDialog/_blocks.py index 5ad7a94..de4b133 100644 --- a/src/gui/ALAutoScriptOrchDialog/_blocks.py +++ b/src/gui/ALAutoScriptOrchDialog/_blocks.py @@ -47,7 +47,6 @@ class ConditionalBlock(QGroupBox): self.connectSignals() self.addInitialConditionRow() - def setupUi( self ): @@ -103,7 +102,6 @@ class ConditionalBlock(QGroupBox): mainLayout.addWidget(self.addActionBtn) self.setUpdatesEnabled(True) - def connectSignals( self ): @@ -112,7 +110,6 @@ class ConditionalBlock(QGroupBox): self.addCondBtn.clicked.connect(self.addConditionRow) self.addActionBtn.clicked.connect(self.addActionStep) - def addInitialConditionRow( self ): @@ -124,7 +121,6 @@ class ConditionalBlock(QGroupBox): self._conditionRows.append(row) self.condRowsLayout.addWidget(row) - def addConditionRow( self ): @@ -137,7 +133,6 @@ class ConditionalBlock(QGroupBox): self._conditionRows.append(row) self.condRowsLayout.addWidget(row) - def removeConditionRow( self, row: ConditionRowFrame @@ -149,7 +144,6 @@ class ConditionalBlock(QGroupBox): row.hide() row.deleteLater() - def addActionStep( self ): @@ -159,7 +153,6 @@ class ConditionalBlock(QGroupBox): self._actionWidgets.append(step) self.actionsLayout.addWidget(step) - def removeActionStep( self, step: ActionStepFrame @@ -171,48 +164,31 @@ class ConditionalBlock(QGroupBox): step.hide() step.deleteLater() - @Slot(int) - def onTypeChanged( - self, - _idx - ): - - isCond = self.typeCombo.currentData() in ("IF", "ELSE IF") - self.conditionWidget.setVisible(isCond) - self.actionLabel.setText( - "执行步骤:" if isCond else "ELSE 执行步骤:" - ) - - def getBlockType( self ) -> str: return self.typeCombo.currentData() - def getConditionRows( self ): return list(self._conditionRows) - def getActionSteps( self ): return list(self._actionWidgets) - def countActionSteps( self ) -> int: return len(self._actionWidgets) - - def toScriptLines( + def toScript( self ) -> list: """ @@ -223,7 +199,7 @@ class ConditionalBlock(QGroupBox): lines = [] if blockType in ("IF", "ELSE IF"): condTexts = [ - r.toConditionText() for r in self._conditionRows if r.toConditionText() + r.toScript() for r in self._conditionRows if r.toScript() ] if not condTexts: condTexts = ["true"] @@ -245,12 +221,11 @@ class ConditionalBlock(QGroupBox): else: lines.append("else") for step in self._actionWidgets: - scriptLine = step.toScriptLine() + scriptLine = step.toScript() if scriptLine: lines.append(scriptLine) return lines - def refreshVarCombos( self ): @@ -260,7 +235,6 @@ class ConditionalBlock(QGroupBox): for step in self._actionWidgets: step.refreshVarCombos() - def setPrevBlockType( self, prevType: str | None @@ -278,3 +252,15 @@ class ConditionalBlock(QGroupBox): item.setEnabled(shouldEnable) if prevType == "ELSE" and self.typeCombo.currentData() in ("ELSE IF", "ELSE"): self.typeCombo.setCurrentIndex(0) + + @Slot(int) + def onTypeChanged( + self, + _idx + ): + + isCond = self.typeCombo.currentData() in ("IF", "ELSE IF") + self.conditionWidget.setVisible(isCond) + self.actionLabel.setText( + "执行步骤:" if isCond else "ELSE 执行步骤:" + ) diff --git a/src/gui/ALAutoScriptOrchDialog/_dialog.py b/src/gui/ALAutoScriptOrchDialog/_dialog.py index 35c3ab4..839a14b 100644 --- a/src/gui/ALAutoScriptOrchDialog/_dialog.py +++ b/src/gui/ALAutoScriptOrchDialog/_dialog.py @@ -42,7 +42,6 @@ class ALAutoScriptOrchDialog(QDialog): self.addBlock() self.scrollLayout.addStretch() - def setupUi( self ): @@ -70,7 +69,6 @@ class ALAutoScriptOrchDialog(QDialog): self.btnBox.button(QDialogButtonBox.StandardButton.Cancel).setText("取消") mainLayout.addWidget(self.btnBox) - def connectSignals( self ): @@ -79,8 +77,7 @@ class ALAutoScriptOrchDialog(QDialog): self.btnBox.rejected.connect(self.reject) self.addBlockBtn.clicked.connect(self.addBlock) - - def _updateBlockTypeRestrictions( + def updateBlockTypeRestrictions( self ): @@ -89,7 +86,6 @@ class ALAutoScriptOrchDialog(QDialog): block.setPrevBlockType(prevType) prevType = block.getBlockType() - def addBlock( self ): @@ -98,10 +94,10 @@ class ALAutoScriptOrchDialog(QDialog): len(self._blocks), self._varMgr, parent=self ) block.deleteBlockBtn.clicked.connect(lambda: self.removeBlock(block)) - block.typeCombo.currentIndexChanged.connect(self._updateBlockTypeRestrictions) + block.typeCombo.currentIndexChanged.connect(self.updateBlockTypeRestrictions) block.addActionStep() self._blocks.append(block) - self._updateBlockTypeRestrictions() + self.updateBlockTypeRestrictions() if self.scrollLayout.count() > 0: lastItem = self.scrollLayout.itemAt( self.scrollLayout.count() - 1 @@ -113,7 +109,6 @@ class ALAutoScriptOrchDialog(QDialog): return self.scrollLayout.addWidget(block) - def removeBlock( self, block: ConditionalBlock @@ -135,8 +130,7 @@ class ALAutoScriptOrchDialog(QDialog): else: blk.typeCombo.setEnabled(True) blk.refreshVarCombos() - self._updateBlockTypeRestrictions() - + self.updateBlockTypeRestrictions() def getScript( self @@ -151,7 +145,7 @@ class ALAutoScriptOrchDialog(QDialog): blockType = block.getBlockType() if blockType == "IF" and prevType is not None: parts.append("end") - lines = block.toScriptLines() + lines = block.toScript() parts.extend(lines) prevType = blockType if self._blocks and self._blocks[0].getBlockType() == "IF": diff --git a/src/gui/ALAutoScriptOrchDialog/_helpers.py b/src/gui/ALAutoScriptOrchDialog/_helpers.py index b48ee37..bec9ee6 100644 --- a/src/gui/ALAutoScriptOrchDialog/_helpers.py +++ b/src/gui/ALAutoScriptOrchDialog/_helpers.py @@ -95,181 +95,6 @@ DATE_OFFSET_UNITS = [ ] -class VariableManager(QObject): - - def __init__( - self, - parent = None - ): - - super().__init__(parent) - self._vars = [] - self._nameMap = {} - - self._initPresetVars() - - - def _initPresetVars( - self - ): - - for p in PRESET_VARIABLES: - entry = {"name": p["name"], "type": p["type"], "display": p["display"]} - self._vars.append(entry) - self._nameMap[p["name"]] = entry - - - def getInfoByName( - self, - name: str - ): - - return self._nameMap.get(name.upper().strip()) - - - def populateCombo( - self, - combo: QComboBox - ): - - currentData = combo.currentData() - combo.blockSignals(True) - combo.clear() - for entry in self._vars: - combo.addItem( - entry["display"], - (entry["name"], entry["type"]) - ) - if currentData: - for i in range(combo.count()): - d = combo.itemData(i) - if d and d[0] == currentData[0]: - combo.setCurrentIndex(i) - break - combo.blockSignals(False) - - - def findExactNameEntry( - self, - combo: QComboBox, - name: str - ) -> int: - - name = name.upper().strip() - for i in range(combo.count()): - d = combo.itemData(i) - if d and len(d) >= 1 and d[0].upper().strip() == name: - return i - return -1 - - -def makeValueWidget( - var_type: str, - parent: QWidget = None -) -> QWidget: - - if var_type == "Int": - w = QSpinBox(parent) - w.setRange(-999999, 999999) - w.setFixedHeight(25) - w.setMinimumWidth(100) - return w - if var_type == "Float": - w = QDoubleSpinBox(parent) - w.setRange(-999999.0, 999999.0) - w.setDecimals(2) - w.setFixedHeight(25) - w.setMinimumWidth(100) - return w - if var_type == "String": - w = QLineEdit(parent) - w.setPlaceholderText("输入值") - w.setFixedHeight(25) - w.setMinimumWidth(120) - return w - if var_type == "Boolean": - w = QComboBox(parent) - w.addItem("是 (true)", "true") - w.addItem("否 (false)", "false") - w.setFixedHeight(25) - w.setMinimumWidth(100) - return w - if var_type == "Date": - return _DateInputContainer(parent) - if var_type == "Time": - return _TimeInputContainer(parent) - w = QLineEdit(parent) - w.setPlaceholderText("输入值") - w.setFixedHeight(25) - w.setMinimumWidth(120) - return w - - -def makeOffsetWidget( - var_type: str, - parent: QWidget = None -) -> QWidget: - - if var_type == "Int": - w = QSpinBox(parent) - w.setRange(-999999, 999999) - w.setFixedHeight(25) - w.setMinimumWidth(100) - return w - if var_type == "Float": - w = QDoubleSpinBox(parent) - w.setRange(-999999.0, 999999.0) - w.setDecimals(2) - w.setFixedHeight(25) - w.setMinimumWidth(100) - return w - if var_type == "Date": - return _DateOffsetContainer(parent) - if var_type == "Time": - return _TimeOffsetContainer(parent) - w = QLabel("(不支持该操作)", parent) - w.setFixedHeight(25) - return w - - -def makeVarRefCombo( - parent: QWidget = None -) -> QComboBox: - - cb = QComboBox(parent) - cb.setFixedHeight(25) - cb.setMinimumWidth(120) - cb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) - return cb - - -def makeComboWidget( - items, - min_width: int = 80, - parent: QWidget = None -) -> QComboBox: - - cb = QComboBox(parent) - for display, data in items: - cb.addItem(display, data) - cb.setFixedHeight(25) - cb.setMinimumWidth(min_width) - return cb - - -def makeLabel( - text: str, - parent: QWidget = None, - width: int = None -) -> QLabel: - - lbl = QLabel(text, parent) - lbl.setFixedHeight(25) - if width: - lbl.setFixedWidth(width) - return lbl - - class _DateInputContainer(QWidget): def __init__( @@ -281,7 +106,6 @@ class _DateInputContainer(QWidget): self._dynamicItems = {} # index -> raw expression, for one-way parsed items self.setupUi() - def setupUi( self ): @@ -327,7 +151,6 @@ class _DateInputContainer(QWidget): return self._relCombo.currentText() return self._dateEdit.date().toString("yyyy-MM-dd") - def setValue( self, expr: str @@ -392,14 +215,12 @@ class _TimeInputContainer(QWidget): layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._timeEdit) - def getValue( self ) -> str: return self._timeEdit.time().toString("HH:mm") - def setValue( self, expr: str @@ -443,14 +264,12 @@ class _DateOffsetContainer(QWidget): layout.addWidget(self._unitCombo) layout.addStretch() - def getValue( self ) -> str: return str(self.getOffsetDays()) - def setValue( self, expr: str @@ -462,7 +281,6 @@ class _DateOffsetContainer(QWidget): except ValueError: pass - def getOffsetDays( self ) -> int: @@ -477,7 +295,6 @@ class _DateOffsetContainer(QWidget): return val * 365 return val - def getRawValue( self ) -> str: @@ -502,14 +319,12 @@ class _TimeOffsetContainer(QWidget): layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._spinBox) - def getValue( self ) -> str: return str(self.getOffsetHours()) - def setValue( self, expr: str @@ -521,14 +336,12 @@ class _TimeOffsetContainer(QWidget): except ValueError: pass - def getOffsetHours( self ) -> int: return self._spinBox.value() - def getRawValue( self ) -> str: @@ -536,6 +349,172 @@ class _TimeOffsetContainer(QWidget): return str(self._spinBox.value()) +class VariableManager(QObject): + + def __init__( + self, + parent = None + ): + + super().__init__(parent) + self._vars = [] + self._nameMap = {} + + self._initPresetVars() + + def _initPresetVars( + self + ): + + for p in PRESET_VARIABLES: + entry = {"name": p["name"], "type": p["type"], "display": p["display"]} + self._vars.append(entry) + self._nameMap[p["name"]] = entry + + def getInfoByName( + self, + name: str + ): + + return self._nameMap.get(name.upper().strip()) + + def populateCombo( + self, + combo: QComboBox + ): + + currentData = combo.currentData() + combo.blockSignals(True) + combo.clear() + for entry in self._vars: + combo.addItem( + entry["display"], + (entry["name"], entry["type"]) + ) + if currentData: + for i in range(combo.count()): + d = combo.itemData(i) + if d and d[0] == currentData[0]: + combo.setCurrentIndex(i) + break + combo.blockSignals(False) + + def findExactNameEntry( + self, + combo: QComboBox, + name: str + ) -> int: + + name = name.upper().strip() + for i in range(combo.count()): + d = combo.itemData(i) + if d and len(d) >= 1 and d[0].upper().strip() == name: + return i + return -1 + + +def makeValueWidget( + var_type: str, + parent: QWidget = None +) -> QWidget: + + if var_type == "Int": + w = QSpinBox(parent) + w.setRange(-999999, 999999) + w.setFixedHeight(25) + w.setMinimumWidth(100) + return w + if var_type == "Float": + w = QDoubleSpinBox(parent) + w.setRange(-999999.0, 999999.0) + w.setDecimals(2) + w.setFixedHeight(25) + w.setMinimumWidth(100) + return w + if var_type == "String": + w = QLineEdit(parent) + w.setPlaceholderText("输入值") + w.setFixedHeight(25) + w.setMinimumWidth(120) + return w + if var_type == "Boolean": + w = QComboBox(parent) + w.addItem("是 (true)", "true") + w.addItem("否 (false)", "false") + w.setFixedHeight(25) + w.setMinimumWidth(100) + return w + if var_type == "Date": + return _DateInputContainer(parent) + if var_type == "Time": + return _TimeInputContainer(parent) + w = QLineEdit(parent) + w.setPlaceholderText("输入值") + w.setFixedHeight(25) + w.setMinimumWidth(120) + return w + +def makeOffsetWidget( + var_type: str, + parent: QWidget = None +) -> QWidget: + + if var_type == "Int": + w = QSpinBox(parent) + w.setRange(-999999, 999999) + w.setFixedHeight(25) + w.setMinimumWidth(100) + return w + if var_type == "Float": + w = QDoubleSpinBox(parent) + w.setRange(-999999.0, 999999.0) + w.setDecimals(2) + w.setFixedHeight(25) + w.setMinimumWidth(100) + return w + if var_type == "Date": + return _DateOffsetContainer(parent) + if var_type == "Time": + return _TimeOffsetContainer(parent) + w = QLabel("(不支持该操作)", parent) + w.setFixedHeight(25) + return w + +def makeVarRefCombo( + parent: QWidget = None +) -> QComboBox: + + cb = QComboBox(parent) + cb.setFixedHeight(25) + cb.setMinimumWidth(120) + cb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + return cb + +def makeComboWidget( + items, + min_width: int = 80, + parent: QWidget = None +) -> QComboBox: + + cb = QComboBox(parent) + for display, data in items: + cb.addItem(display, data) + cb.setFixedHeight(25) + cb.setMinimumWidth(min_width) + return cb + +def makeLabel( + text: str, + parent: QWidget = None, + width: int = None +) -> QLabel: + + lbl = QLabel(text, parent) + lbl.setFixedHeight(25) + if width: + lbl.setFixedWidth(width) + return lbl + def getValueFromWidget( w: QWidget ) -> str: @@ -556,8 +535,7 @@ def getValueFromWidget( return w.text() return "" - -def setWidgetValue( +def setValueToWidget( w: QWidget, var_type: str, expr: str @@ -619,7 +597,6 @@ def setWidgetValue( inner = inner[1:-1].replace('\\"', '"') w.setText(inner) - def encodeValueStr( raw_value: str, var_type: str @@ -632,7 +609,7 @@ def encodeValueStr( """ if var_type in ("Date", "Time"): - return _encodeDateOrTime(str(raw_value), var_type) + return encodeDateOrTime(str(raw_value), var_type) if isinstance(raw_value, bool): return "true" if raw_value else "false" s = str(raw_value) @@ -648,8 +625,7 @@ def encodeValueStr( return f'"{escaped}"' return s - -def _encodeDateOrTime( +def encodeDateOrTime( raw_value: str, var_type: str ) -> str: @@ -707,7 +683,6 @@ def _encodeDateOrTime( return s return f'"{s}"' - def stripOuterParens( s: str ) -> str: @@ -725,12 +700,10 @@ def stripOuterParens( return s[1:-1].strip() return s - # Pre-compiled patterns for detecting arithmetic expressions (A + B / A - B) _RE_ARITH_SPACED = re.compile(r'^(.+?)\s+([+-])\s+(.+)$') _RE_ARITH_NOSPACE = re.compile(r'^([A-Za-z_]\w*)([+-])(\d+|[A-Za-z_]\w*)$') - def isArithExpr( expr: str ) -> bool: @@ -741,7 +714,6 @@ def isArithExpr( s = expr.strip() return bool(_RE_ARITH_SPACED.match(s) or _RE_ARITH_NOSPACE.match(s)) - def isVarReference( expr: str ) -> bool: @@ -764,27 +736,7 @@ def isVarReference( return False return bool(re.match(r"^[A-Z_][A-Z0-9_]*$", up)) - -def findOperatorIn( - text: str, - operators: list -) -> tuple[int, str] | None: - - for op in operators: - op_upper = op.upper() - start = 0 - while True: - idx = text.upper().find(op_upper, start) - if idx < 0: - break - if _isInsideLiteral(text, idx): - start = idx + 1 - continue - return (idx, op) - return None - - -def _isInsideLiteral( +def isInsideLiteral( text: str, pos: int ) -> bool: @@ -799,3 +751,21 @@ def _isInsideLiteral( elif ch == '"' and not in_single: in_double = not in_double return in_single or in_double + +def findOperatorIn( + text: str, + operators: list +) -> tuple[int, str] | None: + + for op in operators: + op_upper = op.upper() + start = 0 + while True: + idx = text.upper().find(op_upper, start) + if idx < 0: + break + if isInsideLiteral(text, idx): + start = idx + 1 + continue + return (idx, op) + return None diff --git a/src/gui/ALAutoScriptOrchDialog/_widgets.py b/src/gui/ALAutoScriptOrchDialog/_widgets.py index c62e370..daa5710 100644 --- a/src/gui/ALAutoScriptOrchDialog/_widgets.py +++ b/src/gui/ALAutoScriptOrchDialog/_widgets.py @@ -58,7 +58,6 @@ class ConditionRowFrame(QFrame): self.setupUi() self.connectSignals() - def setupUi( self ): @@ -90,15 +89,7 @@ class ConditionRowFrame(QFrame): layout.addWidget(self._compTypeCombo) self.rhsStack = QStackedWidget(self) self.rhsStack.setFixedHeight(25) - self.literalStack = QStackedWidget(self) - self.literalStack.setFixedHeight(25) - self.literalWidgets = {} - for vt in VAR_TYPE_ORDER: - 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) + self.initLiteralStack() self.rhsVarCombo = makeVarRefCombo(self) self.rhsStack.addWidget(self.rhsVarCombo) self.rhsStack.setCurrentIndex(0) @@ -113,7 +104,6 @@ class ConditionRowFrame(QFrame): layout.addStretch() self.setUpdatesEnabled(True) - def populateLeftVarCombo( self ): @@ -136,13 +126,25 @@ class ConditionRowFrame(QFrame): self.leftVarCombo.setCurrentIndex(ci) break - - def populateRhsVarCombo( + def populateRHSVarCombo( self ): self._varMgr.populateCombo(self.rhsVarCombo) + def initLiteralStack( + self + ): + + self.literalStack = QStackedWidget(self) + self.literalStack.setFixedHeight(25) + self._literalWidgets = {} + for vt in VAR_TYPE_ORDER: + 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 @@ -151,58 +153,22 @@ class ConditionRowFrame(QFrame): self.leftVarCombo.currentIndexChanged.connect(self.onLeftVarChanged) self._compTypeCombo.currentIndexChanged.connect(self.onCompTypeChanged) - @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) - - - def updateRhsLiteralWidget( - self, - vartype: str - ): - - if vartype not in self.literalWidgets: - vartype = "String" - self.literalStack.setCurrentWidget(self.literalWidgets[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() - - def getLogic( self ) -> str: return self.logicCombo.currentData() if self.logicCombo else "" + def updateRHSLiteralWidget( + self, + vartype: str + ): - def toConditionText( + if vartype not in self._literalWidgets: + vartype = "String" + self.literalStack.setCurrentWidget(self._literalWidgets[vartype]) + + def toScript( self ) -> str: @@ -230,20 +196,53 @@ class ConditionRowFrame(QFrame): if rhsText: return f"{name} {opSym} {rhsText}" return "" - w = self.literalWidgets.get(vartype) + 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() + 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): @@ -262,7 +261,6 @@ class ActionStepFrame(QFrame): self.setupUi() self.connectSignals() - def setupUi( self ): @@ -280,7 +278,7 @@ class ActionStepFrame(QFrame): self.targetCombo = QComboBox(self) self.targetCombo.setFixedHeight(25) self.targetCombo.setMinimumWidth(120) - self.buildTargetCombo() + self.populateTargetCombo() layout.addWidget(self.targetCombo) layout.addWidget(makeLabel("为", self)) self.valueSrcCombo = makeComboWidget([ @@ -301,8 +299,7 @@ class ActionStepFrame(QFrame): layout.addWidget(self.deleteBtn) self.setUpdatesEnabled(True) - - def buildTargetCombo( + def populateTargetCombo( self ): @@ -319,7 +316,6 @@ class ActionStepFrame(QFrame): ) self.targetCombo.blockSignals(False) - def initValueStacks( self ): @@ -338,7 +334,6 @@ class ActionStepFrame(QFrame): self._offsetWidgets[vt] = lbl self.valueStack.addWidget(lbl) - def connectSignals( self ): @@ -347,32 +342,14 @@ class ActionStepFrame(QFrame): self.targetCombo.currentIndexChanged.connect(self.onTargetChanged) self.valueSrcCombo.currentIndexChanged.connect(self.onValueSrcChanged) - @Slot(int) - def onTargetChanged( - self, - idx - ): + def getTargetName( + self + ) -> str: - if idx < 0: - return - data = self.targetCombo.itemData(idx) - if not data: - return - _, vartype = data - self._currentTargetType = vartype - self.updateRHSWidget() - self.onValueSrcChanged(self.valueSrcCombo.currentIndex()) + data = self.targetCombo.currentData() + return data[0] if data else "" - @Slot(int) - def onOpTypeChanged( - self, - idx - ): - - self.updateRHSWidget() - - - def updateRHSWidget( + def updateValueWidget( self ): @@ -386,30 +363,7 @@ class ActionStepFrame(QFrame): else: self.valueStack.setCurrentWidget(self._literalWidgets.get("String")) - @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.updateRHSWidget() - - - def getTargetName( - self - ) -> str: - - data = self.targetCombo.currentData() - return data[0] if data else "" - - - def toScriptLine( + def toScript( self ) -> str: """ @@ -422,7 +376,7 @@ class ActionStepFrame(QFrame): return " -- pass" if not target: return "" - rawVal = self._getValueRaw() + rawVal = self.getValueRaw() vartype = self._currentTargetType if op == "set": encoded = encodeValueStr(rawVal, vartype) @@ -445,8 +399,7 @@ class ActionStepFrame(QFrame): return f" {target} = {target} - {rawVal}" return "" - - def _getValueRaw( + def getValueRaw( self ) -> str: @@ -458,13 +411,12 @@ class ActionStepFrame(QFrame): return getValueFromWidget(w) return "" - def refreshVarCombos( self ): currentData = self.targetCombo.currentData() - self.buildTargetCombo() + self.populateTargetCombo() if currentData: for i in range(self.targetCombo.count()): d = self.targetCombo.itemData(i) @@ -472,3 +424,41 @@ class ActionStepFrame(QFrame): 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() diff --git a/src/gui/ALConfigWidget.py b/src/gui/ALConfigWidget.py index 052b580..9c2ddfb 100644 --- a/src/gui/ALConfigWidget.py +++ b/src/gui/ALConfigWidget.py @@ -10,28 +10,46 @@ See the LICENSE file for details. import os from PySide6.QtCore import ( - Qt, Signal, Slot, QTime, QDate, QDir, QFileInfo -) -from PySide6.QtWidgets import ( - QDialog, QWidget, QLineEdit, QMessageBox, QFileDialog, - QTreeWidgetItem, QMenu, QInputDialog + QDate, + QDir, + QFileInfo, + Qt, + QTime, + Signal, + Slot ) from PySide6.QtGui import ( - QCloseEvent, QAction + QAction, + QCloseEvent +) +from PySide6.QtWidgets import ( + QDialog, + QFileDialog, + QInputDialog, + QLineEdit, + QMenu, + QMessageBox, + QTreeWidgetItem, + QWidget ) import managers.config.ConfigManager as ConfigManager -from utils.JSONReader import JSONReader -from utils.JSONWriter import JSONWriter -from interfaces.ConfigProvider import ConfigProvider, CfgKey -from managers.config.ConfigUtils import ConfigUtils - -from gui.resources.ui.Ui_ALConfigWidget import Ui_ALConfigWidget from gui.ALSeatMapSelectDialog import ALSeatMapSelectDialog from gui.ALSeatMapTable import ALSeatMapTable -from gui.ALUserTreeWidget import ALUserTreeWidget, ALUserTreeItemType +from gui.ALUserTreeWidget import ( + ALUserTreeItemType, + ALUserTreeWidget +) from gui.ALWebDriverDownloadDialog import ALWebDriverDownloadDialog +from gui.resources.ui.Ui_ALConfigWidget import Ui_ALConfigWidget +from interfaces.ConfigProvider import ( + CfgKey, + ConfigProvider +) +from managers.config.ConfigUtils import ConfigUtils +from utils.JSONReader import JSONReader +from utils.JSONWriter import JSONWriter class ALConfigWidget(QWidget, Ui_ALConfigWidget): @@ -54,7 +72,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): if not self.initializeConfigs(): self.close() - def modifyUi( self ): @@ -70,7 +87,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): self.initializeFloorRoomMap() self.initializeUserInfoWidget() - def connectSignals( self ): @@ -94,7 +110,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): self.ConfirmButton.clicked.connect(self.onConfirmButtonClicked) self.CancelButton.clicked.connect(self.onCancelButtonClicked) - def showEvent( self, event @@ -118,7 +133,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): return result - def closeEvent( self, event: QCloseEvent @@ -127,7 +141,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): self.configWidgetIsClosed.emit() super().closeEvent(event) - def initializeFloorRoomMap( self ): @@ -161,7 +174,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): "五层": ["五层考研"] } - def initializeConfigToWidget( self, which: str, @@ -176,7 +188,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): self.setUsersToTreeWidget(config_data) self.CurrentUserConfigEdit.setText(self.__config_paths["user"]) - def initializeConfig( self, which: str @@ -210,7 +221,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): is_success = False return is_success - def initializeConfigs( self ) -> bool: @@ -223,7 +233,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): self.initializeConfigToWidget(which, self.__config_data[which]) return is_success - def defaultRunConfig( self ) -> dict: @@ -247,7 +256,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): } } - def defaultUserConfig( self ) -> dict: @@ -257,7 +265,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): ] } - def collectRunConfigFromWidget( self ) -> dict: @@ -279,7 +286,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): run_config["mode"]["run_mode"] = run_mode return run_config - def setRunConfigToWidget( self, run_config: dict @@ -318,7 +324,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): "文件可能被意外修改或已经损坏\n" ) - def initializeUserInfoWidget( self ): @@ -343,7 +348,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): self.MaxRenewTimeDiffSpinBox.setValue(30) self.PreferLateRenewTimeCheckBox.setChecked(False) - def collectUserFromWidget( self ) -> dict: @@ -376,7 +380,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): user["reserve_info"]["renew_time"]["prefer_early"] = not self.PreferLateRenewTimeCheckBox.isChecked() return user - def collectUsersFromTreeWidget( self ) -> dict: @@ -399,7 +402,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): user_config["groups"].append(group_config) return user_config - def setUserToWidget( self, user: dict @@ -441,7 +443,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): "文件可能被意外修改或已经损坏\n" ) - def setUsersToTreeWidget( self, users: dict @@ -483,7 +484,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): finally: self.UserTreeWidget.itemChanged.connect(self.onUserTreeWidgetItemChanged) - def loadRunConfig( self, run_config_path: str @@ -507,7 +507,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): ) return None - def saveRunConfig( self, run_config_path: str, @@ -529,7 +528,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): ) return False - def loadUserConfig( self, user_config_path: str @@ -563,7 +561,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): ) return None - def saveUserConfig( self, user_config_path: str, @@ -585,7 +582,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): ) return False - def saveConfigs( self, run_config_path: str, @@ -608,7 +604,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): return False return True - def loadConfig( self, config_path: str @@ -637,7 +632,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): except: return False - def addGroup( self, group_name: str = "" @@ -654,7 +648,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): self.UserTreeWidget.itemChanged.connect(self.onUserTreeWidgetItemChanged) return group_item - def delGroup( self, group_item: QTreeWidgetItem = None @@ -667,7 +660,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): index = self.UserTreeWidget.indexOfTopLevelItem(group_item) self.UserTreeWidget.takeTopLevelItem(index) - def addUser( self, group_item: QTreeWidgetItem = None @@ -722,7 +714,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): self.UserTreeWidget.itemChanged.connect(self.onUserTreeWidgetItemChanged) return user_item - def delUser( self, user_item: QTreeWidgetItem = None @@ -738,7 +729,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): if parent_item.childCount() == 0: self.UserTreeWidget.setCurrentItem(None) - def renameItem( self, item: QTreeWidgetItem, @@ -862,7 +852,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): is_checked = item.checkState(1) == Qt.CheckState.Checked item.setText(1, "" if is_checked else "跳过") - def showTreeMenu( self, menu: QMenu @@ -872,7 +861,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): add_group_action.triggered.connect(self.addGroup) menu.addAction(add_group_action) - def showGroupMenu( self, menu: QMenu, @@ -892,7 +880,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): if group_item.checkState(1) == Qt.CheckState.Unchecked: add_user_action.setEnabled(False) - def showUserMenu( self, menu: QMenu, @@ -952,7 +939,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): if browser_driver_path: self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(browser_driver_path)) - @Slot() def onAutoDownloadWebDriverButtonClicked( self @@ -966,7 +952,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget): self.BrowserTypeComboBox.setCurrentText(selected_driver_info.driver_type.value) self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(str(selected_driver_info.driver_path))) - @Slot() def onBrowseCurrentRunConfigButtonClicked( self diff --git a/src/gui/ALMainWindow.py b/src/gui/ALMainWindow.py index 022e46a..20dd3f8 100644 --- a/src/gui/ALMainWindow.py +++ b/src/gui/ALMainWindow.py @@ -10,24 +10,37 @@ See the LICENSE file for details. import queue from PySide6.QtCore import ( - Qt, Signal, Slot, QTimer, QUrl, -) -from PySide6.QtWidgets import ( - QMainWindow, QMenu, QSystemTrayIcon, QMessageBox + QTimer, + QUrl, + Qt, + Signal, + Slot ) from PySide6.QtGui import ( - QTextCursor, QCloseEvent, QFont, QIcon, QDesktopServices + QCloseEvent, + QDesktopServices, + QFont, + QIcon, + QTextCursor +) +from PySide6.QtWidgets import ( + QMainWindow, + QMenu, + QMessageBox, + QSystemTrayIcon ) from base.MsgBase import MsgBase -from managers.config.ConfigUtils import ConfigUtils - -from gui.resources.ui.Ui_ALMainWindow import Ui_ALMainWindow -from gui.resources import ALResource -from gui.ALConfigWidget import ALConfigWidget -from gui.ALTimerTaskManageWidget import ALTimerTaskManageWidget from gui.ALAboutDialog import ALAboutDialog -from gui.ALMainWorkers import TimerTaskWorker, AutoLibWorker +from gui.ALConfigWidget import ALConfigWidget +from gui.ALMainWorkers import ( + AutoLibWorker, + TimerTaskWorker +) +from gui.ALTimerTaskManageWidget import ALTimerTaskManageWidget +from gui.resources import ALResource +from gui.resources.ui.Ui_ALMainWindow import Ui_ALMainWindow +from managers.config.ConfigUtils import ConfigUtils class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): @@ -59,7 +72,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.startTimerTaskPolling() self._showLog("主窗口初始化完成") - def modifyUi( self ): @@ -90,7 +102,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.__alTimerTaskManageWidget.timerTaskManageWidgetIsClosed.connect(self.onTimerTaskManageWidgetClosed) self.__alTimerTaskManageWidget.setWindowFlags(Qt.WindowType.Window|Qt.WindowType.WindowCloseButtonHint) - def onAboutActionTriggered( self ): @@ -98,7 +109,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): about_dialog = ALAboutDialog(self) about_dialog.exec() - def onManualActionTriggered( self ): @@ -106,7 +116,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): url = QUrl("https://www.autolibrary.kenanzhu.com/manuals") QDesktopServices.openUrl(url) - def setupTray( self ): @@ -128,7 +137,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.TrayIcon.activated.connect(self.onTrayIconActivated) self.TrayIcon.show() - def hideToTray( self ): @@ -141,7 +149,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): 2000 ) - def onTrayIconActivated( self, reason: QSystemTrayIcon.ActivationReason @@ -150,7 +157,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): if reason == QSystemTrayIcon.DoubleClick: self.showNormal() - def connectSignals( self ): @@ -162,7 +168,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.SendButton.clicked.connect(self.onSendButtonClicked) self.MessageEdit.returnPressed.connect(self.onSendButtonClicked) - def closeEvent( self, event: QCloseEvent @@ -188,7 +193,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self._showLog("主窗口关闭") QMainWindow.closeEvent(self, event) - def appendToTextEdit( self, text: str @@ -202,7 +206,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): scrollbar = self.MessageIOTextEdit.verticalScrollBar() scrollbar.setValue(scrollbar.maximum()) - def startMsgPolling( self ): @@ -211,7 +214,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.__msg_queue_timer.timeout.connect(self.pollMsgQueue) self.__msg_queue_timer.start(100) - def startTimerTaskPolling( self ): @@ -220,7 +222,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.__timer_task_timer.timeout.connect(self.pollTimerTaskQueue) self.__timer_task_timer.start(500) - def pollTimerTaskQueue( self ): @@ -254,7 +255,6 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.__is_running_timer_task = False pass - def setControlButtons( self, config_button_enabled: bool, diff --git a/src/gui/ALMainWorkers.py b/src/gui/ALMainWorkers.py index f1680b0..e4f08d8 100644 --- a/src/gui/ALMainWorkers.py +++ b/src/gui/ALMainWorkers.py @@ -37,7 +37,6 @@ class AutoLibWorker(MsgBase, QThread): QThread.__init__(self) self.__config_paths = config_paths - def checkTimeAvailable( self, ) -> bool: @@ -52,7 +51,6 @@ class AutoLibWorker(MsgBase, QThread): self._showLog(f"时间检查通过, 当前时间: {current_time}", self.TraceLevel.INFO) return True - def checkConfigPaths( self, ) -> bool: @@ -68,7 +66,6 @@ class AutoLibWorker(MsgBase, QThread): self._showLog(f"配置文件路径检查通过, 路径: {self.__config_paths}", self.TraceLevel.INFO) return True - def loadConfigs( self ) -> bool: @@ -101,7 +98,6 @@ class AutoLibWorker(MsgBase, QThread): ) return True - def run( self ): @@ -161,7 +157,6 @@ class TimerTaskWorker(AutoLibWorker): self.autoLibWorkerIsFinished.connect(self.onTimerTaskIsFinished) self.autoLibWorkerFinishedWithError.connect(self.onTimerTaskFinishedWithError) - def run( self ): @@ -206,7 +201,6 @@ class TimerTaskWorker(AutoLibWorker): self._showTrace(f"定时任务 {self.__timer_task['name']} 运行结束") self.timerTaskWorkerIsFinished.emit(False, self.__timer_task) - def applyRepeatAutoScript( self ): diff --git a/src/gui/ALSeatFrame.py b/src/gui/ALSeatFrame.py index 331862c..48057ee 100644 --- a/src/gui/ALSeatFrame.py +++ b/src/gui/ALSeatFrame.py @@ -31,7 +31,6 @@ class ALSeatFrame(QFrame): self.setupUi() - def setupUi( self ): @@ -55,7 +54,6 @@ class ALSeatFrame(QFrame): self.Label.setAlignment(Qt.AlignCenter) self.Label.setGeometry(0, 0, 60, 40) - def mousePressEvent( self, event @@ -65,14 +63,12 @@ class ALSeatFrame(QFrame): self.toggleSelection() self.clicked.emit(self.__seat_number) - def isSelected( self ): return self.__is_selected - def toggleSelection(self): self.__is_selected = not self.__is_selected diff --git a/src/gui/ALSeatMapSelectDialog.py b/src/gui/ALSeatMapSelectDialog.py index 032ec66..0602dc5 100644 --- a/src/gui/ALSeatMapSelectDialog.py +++ b/src/gui/ALSeatMapSelectDialog.py @@ -8,15 +8,20 @@ You may use, modify, and distribute this file under the terms of the MIT License See the LICENSE file for details. """ from PySide6.QtCore import ( - Qt, Slot, Signal -) -from PySide6.QtWidgets import ( - QDialog, QLabel, QHBoxLayout, QVBoxLayout, - QPushButton, + Qt, + Signal, + Slot ) from PySide6.QtGui import ( QCloseEvent ) +from PySide6.QtWidgets import ( + QDialog, + QHBoxLayout, + QLabel, + QPushButton, + QVBoxLayout +) from gui.ALSeatMapView import ALSeatMapView @@ -42,7 +47,6 @@ class ALSeatMapSelectDialog(QDialog): self.setupUi() self.connectSignals() - def setupUi( self ): @@ -85,7 +89,6 @@ class ALSeatMapSelectDialog(QDialog): self.SeatMapWidgetControlLayout.addWidget(self.ConfirmButton) self.SeatMapWidgetMainLayout.addLayout(self.SeatMapWidgetControlLayout) - def connectSignals( self ): @@ -93,7 +96,6 @@ class ALSeatMapSelectDialog(QDialog): self.ConfirmButton.clicked.connect(self.onConfirmButtonClicked) self.CancelButton.clicked.connect(self.onCancelButtonClicked) - def showEvent( self, event @@ -117,7 +119,6 @@ class ALSeatMapSelectDialog(QDialog): return result - def closeEvent( self, event: QCloseEvent @@ -131,7 +132,6 @@ class ALSeatMapSelectDialog(QDialog): self.seatMapSelectDialogIsClosed.emit(self.getSelectedSeats()) super().closeEvent(event) - def selectSeat( self, seat_number: str @@ -139,7 +139,6 @@ class ALSeatMapSelectDialog(QDialog): self.SeatMapGraphicsView.selectSeat(seat_number) - def selectSeats( self, seat_numbers: list[str] @@ -147,14 +146,12 @@ class ALSeatMapSelectDialog(QDialog): return self.SeatMapGraphicsView.selectSeats(seat_numbers) - def getSelectedSeats( self ) -> list[str]: return self.SeatMapGraphicsView.getSelectedSeats() - def clearSelections( self ): diff --git a/src/gui/ALSeatMapView.py b/src/gui/ALSeatMapView.py index c687b45..a215e2a 100644 --- a/src/gui/ALSeatMapView.py +++ b/src/gui/ALSeatMapView.py @@ -11,11 +11,16 @@ from PySide6.QtCore import ( Qt, Slot, QEvent ) from PySide6.QtWidgets import ( - QFrame, QWidget, - QGridLayout, QGraphicsView, QGraphicsScene, QGraphicsItem + QFrame, + QWidget, + QGridLayout, + QGraphicsView, + QGraphicsScene, + QGraphicsItem ) from PySide6.QtGui import ( - QPainter, QWheelEvent + QPainter, + QWheelEvent ) from gui.ALSeatFrame import ALSeatFrame @@ -35,18 +40,6 @@ class ALSeatMapView(QGraphicsView): self.setupUi() - @staticmethod - def formatSeatNumber( - seat_number: str - ) -> str: - - if seat_number and not seat_number[-1].isdigit(): - digits = seat_number[:-1] - letter = seat_number[-1] - return digits.zfill(3) + letter - return seat_number.zfill(3) - - def eventFilter( self, watched, @@ -61,7 +54,6 @@ class ALSeatMapView(QGraphicsView): return True return super().eventFilter(watched, event) - def zoomGraphicsView( self, event: QWheelEvent @@ -80,7 +72,6 @@ class ALSeatMapView(QGraphicsView): self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) self.scale(zoom_factor, zoom_factor) - def setupUi( self ): @@ -100,7 +91,6 @@ class ALSeatMapView(QGraphicsView): self.ContainerProxy = self.SeatMapGraphicsScene.addWidget(self.SeatsContainerWidget) self.ContainerProxy.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, False) - def setupSeatMap( self ): @@ -125,7 +115,6 @@ class ALSeatMapView(QGraphicsView): self.SeatsContainerLayout.setContentsMargins(20, 20, 20, 20) self.SeatsContainerWidget.adjustSize() - def selectSeat( self, seat_number: str @@ -142,7 +131,6 @@ class ALSeatMapView(QGraphicsView): widget.toggleSelection() self.__selected_seats.append(seat_number) - def selectSeats( self, selected_seats: list @@ -152,14 +140,12 @@ class ALSeatMapView(QGraphicsView): for seat_number in selected_seats: self.selectSeat(seat_number) - def getSelectedSeats( self ) -> list[str]: return self.__selected_seats - def clearSelections( self ): @@ -185,4 +171,15 @@ class ALSeatMapView(QGraphicsView): if len(self.__selected_seats) < 1: self.__selected_seats.append(seat_number) else: - self.__seat_frames[seat_number].toggleSelection() \ No newline at end of file + self.__seat_frames[seat_number].toggleSelection() + + @staticmethod + def formatSeatNumber( + seat_number: str + ) -> str: + + if seat_number and not seat_number[-1].isdigit(): + digits = seat_number[:-1] + letter = seat_number[-1] + return digits.zfill(3) + letter + return seat_number.zfill(3) diff --git a/src/gui/ALStatusLabel.py b/src/gui/ALStatusLabel.py index 56791f3..aae5809 100644 --- a/src/gui/ALStatusLabel.py +++ b/src/gui/ALStatusLabel.py @@ -9,14 +9,20 @@ See the LICENSE file for details. """ from enum import Enum -from PySide6.QtWidgets import ( - QLabel -) from PySide6.QtCore import ( - Qt, Property, QPropertyAnimation, QEasingCurve + Property, + QEasingCurve, + QPropertyAnimation, + Qt ) from PySide6.QtGui import ( - QPainter, QColor, QConicalGradient, QPalette + QColor, + QConicalGradient, + QPainter, + QPalette +) +from PySide6.QtWidgets import ( + QLabel ) @@ -44,7 +50,6 @@ class ALStatusLabel(QLabel): self.setupUi() - def setupUi( self ): @@ -59,14 +64,12 @@ class ALStatusLabel(QLabel): self.RunningAnimation.setLoopCount(-1) self.RunningAnimation.setEasingCurve(QEasingCurve.Type.Linear) - def isDarkMode( self ) -> bool: return self.palette().color(QPalette.ColorRole.Window).value() < 128 - def getMarkColor( self ) -> QColor: @@ -111,7 +114,6 @@ class ALStatusLabel(QLabel): self.__icon_angle = value self.update() - def paintEvent( self, event diff --git a/src/gui/ALTimerTaskAddDialog.py b/src/gui/ALTimerTaskAddDialog.py index 019de61..1d0b600 100644 --- a/src/gui/ALTimerTaskAddDialog.py +++ b/src/gui/ALTimerTaskAddDialog.py @@ -49,7 +49,6 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog): if self.__edit_timer_task: self.loadTask(self.__edit_timer_task) - def modifyUi( self ): @@ -101,10 +100,10 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog): self.AutoScriptLayout.setContentsMargins(3, 3, 3, 3) self.AutoScriptLayout.setSpacing(3) autoScriptBtnLayout = QHBoxLayout() - self.AutoScriptPreviewButton = QPushButton("编辑") - self.AutoScriptPreviewButton.setMinimumHeight(25) - self.AutoScriptPreviewButton.setFixedWidth(60) - autoScriptBtnLayout.addWidget(self.AutoScriptPreviewButton) + self.AutoScriptEditButton = QPushButton("编辑") + self.AutoScriptEditButton.setMinimumHeight(25) + self.AutoScriptEditButton.setFixedWidth(80) + autoScriptBtnLayout.addWidget(self.AutoScriptEditButton) autoScriptBtnLayout.addStretch() self.AutoScriptHelpButton = QPushButton("?") self.AutoScriptHelpButton.setFixedSize(20, 20) @@ -133,7 +132,6 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog): self.__auto_script = "" self.__mock_target_data = None - def loadTask( self, task: dict @@ -174,7 +172,6 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog): self.__mock_target_data = mock_data self.ConfirmButton.setText("保存") - def connectSignals( self ): @@ -183,10 +180,9 @@ class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog): self.ConfirmButton.clicked.connect(self.accept) self.TimerTypeComboBox.currentIndexChanged.connect(self.onTimerTypeComboBoxIndexChanged) self.RepeatCheckBox.toggled.connect(self.onRepeatCheckBoxToggled) - self.AutoScriptPreviewButton.clicked.connect(self.onPreviewAutoScript) + self.AutoScriptEditButton.clicked.connect(self.onPreviewAutoScript) self.AutoScriptHelpButton.clicked.connect(self.onAutoScriptHelp) - def getTimerTask( self ) -> dict: diff --git a/src/gui/ALTimerTaskHistoryDialog.py b/src/gui/ALTimerTaskHistoryDialog.py index e656c47..03bba0d 100644 --- a/src/gui/ALTimerTaskHistoryDialog.py +++ b/src/gui/ALTimerTaskHistoryDialog.py @@ -34,7 +34,6 @@ class ALTimerTaskHistoryDialog(QDialog): self.setupUi() self.connectSignals() - def setupUi( self ): @@ -82,7 +81,6 @@ class ALTimerTaskHistoryDialog(QDialog): ButtonLayout.addWidget(self.CloseButton) MainLayout.addLayout(ButtonLayout) - def connectSignals( self ): @@ -90,7 +88,6 @@ class ALTimerTaskHistoryDialog(QDialog): self.CloseButton.clicked.connect(self.accept) self.ClearHistoryButton.clicked.connect(self.onClearHistoryButtonClicked) - def loadHistory( self ): @@ -99,6 +96,11 @@ class ALTimerTaskHistoryDialog(QDialog): for row, record in enumerate(self.__history): self.addHistoryRow(row, record) + def getHistory( + self + ) -> list: + + return self.__history def addHistoryRow( self, @@ -129,13 +131,6 @@ class ALTimerTaskHistoryDialog(QDialog): self.HistoryTableWidget.setItem(row, 2, DurationItem) self.HistoryTableWidget.setRowHeight(row, 25) - - def getHistory( - self - ) -> list: - - return self.__history - @Slot() def onClearHistoryButtonClicked( self diff --git a/src/gui/ALTimerTaskManageWidget.py b/src/gui/ALTimerTaskManageWidget.py index 4aef755..f2df449 100644 --- a/src/gui/ALTimerTaskManageWidget.py +++ b/src/gui/ALTimerTaskManageWidget.py @@ -15,24 +15,40 @@ from enum import Enum from datetime import datetime, timedelta from PySide6.QtCore import ( - Qt, Signal, Slot, QTimer -) -from PySide6.QtWidgets import ( - QDialog, QWidget, QListWidgetItem, QMessageBox, - QHBoxLayout, QVBoxLayout, QLabel, QPushButton, QMenu + QTimer, + Qt, + Signal, + Slot ) from PySide6.QtGui import ( - QCloseEvent, QAction + QAction, + QCloseEvent +) +from PySide6.QtWidgets import ( + QDialog, + QHBoxLayout, + QLabel, + QListWidgetItem, + QMenu, + QMessageBox, + QPushButton, + QVBoxLayout, + QWidget ) import managers.config.ConfigManager as ConfigManager -from utils.TimerUtils import TimerUtils -from interfaces.ConfigProvider import ConfigProvider, CfgKey - -from gui.resources.ui.Ui_ALTimerTaskManageWidget import Ui_ALTimerTaskManageWidget -from gui.ALTimerTaskAddDialog import ALTimerTaskAddDialog, ALTimerTaskStatus +from gui.ALTimerTaskAddDialog import ( + ALTimerTaskAddDialog, + ALTimerTaskStatus +) from gui.ALTimerTaskHistoryDialog import ALTimerTaskHistoryDialog +from gui.resources.ui.Ui_ALTimerTaskManageWidget import Ui_ALTimerTaskManageWidget +from interfaces.ConfigProvider import ( + CfgKey, + ConfigProvider +) +from utils.TimerUtils import TimerUtils class ALTimerTaskItemWidget(QWidget): @@ -53,7 +69,6 @@ class ALTimerTaskItemWidget(QWidget): self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.customContextMenuRequested.connect(self.showContextMenu) - def modifyUi( self ): @@ -204,7 +219,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): if not self.initializeTimerTasks(): raise Exception("定时任务配置文件初始化失败 !") - def connectSignals( self ): @@ -215,7 +229,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): self.TimerTaskSortOrderToggleButton.clicked.connect(self.onSortOrderToggleButtonClicked) self.timerTasksChanged.connect(self.onTimerTasksChanged) - def setupTimer( self ): @@ -224,7 +237,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): self.__check_timer.timeout.connect(self.checkTasks) self.__check_timer.start(500) - def initializeTimerTasks( self ) -> bool: @@ -240,7 +252,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): return True return False - def getTimerTasks( self ) -> list: @@ -265,7 +276,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): ) return None - def setTimerTasks( self, timer_tasks: list @@ -289,7 +299,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): ) return False - def showEvent( self, event @@ -313,7 +322,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): return result - def closeEvent( self, event: QCloseEvent @@ -323,7 +331,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): self.timerTaskManageWidgetIsClosed.emit() event.ignore() - def sortTimerTasks( self, policy: SortPolicy = SortPolicy.BY_EXECUTE_TIME, @@ -346,7 +353,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): reverse = order is Qt.SortOrder.DescendingOrder ) - def updateStat( self ): @@ -373,7 +379,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): self.ExecutedTaskLabel.setText(f"已执行:{executed}") self.InvalidTaskLabel.setText(f"无效的:{invalid}") - def updateTimerTaskList( self ): @@ -396,7 +401,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): self.TimerTasksListWidget.addItem(item) self.TimerTasksListWidget.setItemWidget(item, widget) - def addTask( self ): @@ -407,7 +411,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): self.__timer_tasks.append(timer_task) self.timerTasksChanged.emit() - def editTask( self, timer_task: dict @@ -440,7 +443,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): f"已记录次数:{history_count}" ) - def deleteTask( self, timer_task: dict @@ -469,7 +471,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): ] self.timerTasksChanged.emit() - def clearAllTasks( self ): @@ -531,7 +532,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): self.__timer_tasks.clear() self.timerTasksChanged.emit() - def showTaskHistory( self, task: dict @@ -541,7 +541,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): if dialog.exec() == QDialog.DialogCode.Accepted: self.timerTasksChanged.emit() - def checkTasks( self ): @@ -615,7 +614,6 @@ class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget): break self.timerTasksChanged.emit() - def onRepeatTimerTaskIs( self, status: ALTimerTaskStatus, diff --git a/src/gui/ALUserTreeWidget.py b/src/gui/ALUserTreeWidget.py index 15bdd2c..fdb39e6 100644 --- a/src/gui/ALUserTreeWidget.py +++ b/src/gui/ALUserTreeWidget.py @@ -10,14 +10,22 @@ See the LICENSE file for details. from enum import Enum from PySide6.QtCore import ( - Qt, QSize, QCoreApplication, QRect, QPoint + Qt, + QSize, + QCoreApplication, + QRect, + QPoint ) from PySide6.QtWidgets import ( - QAbstractScrollArea, QAbstractItemView, - QTreeWidget, QTreeWidgetItem + QAbstractScrollArea, + QAbstractItemView, + QTreeWidget, + QTreeWidgetItem ) from PySide6.QtGui import ( - QDragEnterEvent, QDragMoveEvent, QDropEvent + QDragEnterEvent, + QDragMoveEvent, + QDropEvent ) @@ -39,7 +47,6 @@ class ALUserTreeWidget(QTreeWidget): self.setupUi() self.translateUi() - def setupUi( self ): @@ -70,7 +77,6 @@ class ALUserTreeWidget(QTreeWidget): self.header().setHighlightSections(False) self.header().setProperty(u"showSortIndicator", True) - def translateUi( self ): @@ -78,7 +84,6 @@ class ALUserTreeWidget(QTreeWidget): ___qtreewidgetitem = self.headerItem() ___qtreewidgetitem.setText(1, QCoreApplication.translate("ALConfigWidget", u"\u72b6\u6001", None)); - @staticmethod def isDragPositionValid( target_rect: QRect, @@ -90,7 +95,6 @@ class ALUserTreeWidget(QTreeWidget): y_offset < target_rect.height()*0.8) return valid - def dragEnterEvent( self, event: QDragEnterEvent @@ -98,7 +102,6 @@ class ALUserTreeWidget(QTreeWidget): super().dragEnterEvent(event) - def dragMoveEvent( self, event: QDragMoveEvent @@ -136,7 +139,6 @@ class ALUserTreeWidget(QTreeWidget): return event.acceptProposedAction() - def dropEvent( self, event: QDropEvent diff --git a/src/gui/ALWebDriverDownloadDialog.py b/src/gui/ALWebDriverDownloadDialog.py index 59571c5..e8cb0d9 100644 --- a/src/gui/ALWebDriverDownloadDialog.py +++ b/src/gui/ALWebDriverDownloadDialog.py @@ -11,20 +11,30 @@ import threading from typing import Optional from PySide6.QtCore import ( - Qt, Slot, QThread, Signal + Qt, + Slot, + QThread, + Signal ) from PySide6.QtWidgets import ( - QDialog, QLabel, QComboBox, QProgressBar, - QPushButton, QVBoxLayout, QHBoxLayout, - QMessageBox, QFrame, QLineEdit -) -from PySide6.QtGui import ( - QCloseEvent + QDialog, + QLabel, + QComboBox, + QProgressBar, + QPushButton, + QVBoxLayout, + QHBoxLayout, + QMessageBox, + QFrame, + QLineEdit ) +from PySide6.QtGui import QCloseEvent from managers.driver.WebDriverManager import ( - instance as webdriver_manager_instance, - WebDriverManager, WebDriverInfo, WebDriverType, + instance as webdriverManagerInstance, + WebDriverManager, + WebDriverInfo, + WebDriverType, WebDriverStatus ) from gui.ALStatusLabel import ALStatusLabel @@ -142,7 +152,6 @@ class ALWebDriverDownloadDialog(QDialog): self.initializeDriverManager() self.refreshDriverList() - def showEvent( self, event @@ -165,7 +174,6 @@ class ALWebDriverDownloadDialog(QDialog): self.move(target_pos) return result - def setupUi( self ): @@ -237,7 +245,6 @@ class ALWebDriverDownloadDialog(QDialog): self.ControlLayout.addWidget(self.ConfirmButton) self.MainLayout.addLayout(self.ControlLayout) - def connectSignals( self ): @@ -249,18 +256,16 @@ class ALWebDriverDownloadDialog(QDialog): self.ConfirmButton.clicked.connect(self.onConfirmButtonClicked) self.DriverComboBox.currentIndexChanged.connect(self.onDriverComboBoxChanged) - def initializeDriverManager( self ): try: - self.__driver_manager = webdriver_manager_instance(self.__driver_dir) + self.__driver_manager = webdriverManagerInstance(self.__driver_dir) except ValueError as e: QMessageBox.warning(self, "初始化失败", f"WebDriverManager 初始化失败:\n{str(e)}") self.reject() - def refreshDriverList( self ): @@ -270,18 +275,20 @@ class ALWebDriverDownloadDialog(QDialog): self.__driver_manager.refresh() self.__driver_infos = self.__driver_manager.getDriverInfos() self.DriverComboBox.clear() + installed = 0 installed_idx = 0 for i, driver_info in enumerate(self.__driver_infos): - if driver_info.driver_status == WebDriverStatus.INSTALLED: - installed_idx = i # get the installed driver index display_text = f"{driver_info.driver_type.value} - {driver_info.browser_version}" + if driver_info.driver_status == WebDriverStatus.INSTALLED: + installed += 1 + installed_idx = i # get the installed driver index + display_text += " : 已安装" self.DriverComboBox.addItem(display_text) count = len(self.__driver_infos) - self.BrowserCountLabel.setText(f"检测到 {count} 个可用浏览器:") + self.BrowserCountLabel.setText(f"检测到 {count} 个可用浏览器,{installed} 个已安装驱动:") if self.__driver_infos: self.DriverComboBox.setCurrentIndex(installed_idx) - def onDriverComboBoxChanged( self, index: int @@ -294,6 +301,116 @@ class ALWebDriverDownloadDialog(QDialog): self.updateProgressBarStates(driver_info) self.updateButtonStates(driver_info) + def closeEvent( + self, + event: QCloseEvent + ): + + if self.__download_thread and self.__download_thread.isRunning(): + reply = QMessageBox.question( + self, + "确认关闭 - AutoLibrary", + "驱动正在下载中, 确定要取消并关闭对话框吗 ?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No + ) + if reply == QMessageBox.StandardButton.No: + event.ignore() + return + self.__download_thread.stop() + if not self.__confirmed: + self.__selected_driver_info = None + event.accept() + super().closeEvent(event) + + def onThreadFinished( + self + ): + + if self.__download_thread: + self.__download_thread.deleteLater() + self.__download_thread = None + + def getSelectedDriverInfo( + self + ) -> Optional[WebDriverInfo]: + + return self.__selected_driver_info + + def updateDriverInfoDisplay( + self, + driver_info: WebDriverInfo + ): + + if driver_info.driver_type == WebDriverType.CHROME: + driver_type = "Google Chrome" + elif driver_info.driver_type == WebDriverType.FIREFOX: + driver_type = "Mozilla Firefox" + elif driver_info.driver_type == WebDriverType.EDGE: + driver_type = "Microsoft Edge" + else: + driver_type = "未知" + self.BrowserTypeLabel.setText(f"类型:{driver_type}") + self.VersionLabel.setText(f"版本:{driver_info.driver_version}") + if driver_info.driver_path: + self.PathLabel.setText(str(driver_info.driver_path)) + else: + self.PathLabel.setText("未安装") + match driver_info.driver_status: + case WebDriverStatus.NOT_INSTALLED: + self.StatusLabel.status = ALStatusLabel.Status.WAITING + case WebDriverStatus.INSTALLED: + self.StatusLabel.status = ALStatusLabel.Status.SUCCESS + case WebDriverStatus.DOWNLOADING: + self.StatusLabel.status = ALStatusLabel.Status.RUNNING + case WebDriverStatus.ERROR: + self.StatusLabel.status = ALStatusLabel.Status.FAILURE + + def updateProgressBarStates( + self, + driver_info: WebDriverInfo + ): + + if driver_info.driver_status == WebDriverStatus.NOT_INSTALLED: + self.ProgressBar.setValue(0) + self.ProgressText.setText("未安装") + elif driver_info.driver_status == WebDriverStatus.INSTALLED: + self.ProgressBar.setValue(100) + self.ProgressText.setText("已安装") + elif driver_info.driver_status == WebDriverStatus.DOWNLOADING: + pass # update by worker thread + elif driver_info.driver_status == WebDriverStatus.ERROR: + self.ProgressBar.setValue(0) + self.ProgressText.setText("下载失败") + + def updateButtonStates( + self, + driver_info: WebDriverInfo + ): + + if driver_info.driver_status == WebDriverStatus.NOT_INSTALLED: + self.RefreshButton.setEnabled(True) + self.DeleteButton.setEnabled(False) + self.DownloadButton.setEnabled(True) + self.CancelButton.setEnabled(True) + self.ConfirmButton.setEnabled(False) + elif driver_info.driver_status == WebDriverStatus.INSTALLED: + self.RefreshButton.setEnabled(True) + self.DownloadButton.setEnabled(False) + self.DeleteButton.setEnabled(True) + self.CancelButton.setEnabled(True) + self.ConfirmButton.setEnabled(True) + elif driver_info.driver_status == WebDriverStatus.DOWNLOADING: + self.RefreshButton.setEnabled(False) + self.DownloadButton.setEnabled(False) + self.DeleteButton.setEnabled(False) + self.CancelButton.setEnabled(True) + self.ConfirmButton.setEnabled(False) + elif driver_info.driver_status == WebDriverStatus.ERROR: + self.RefreshButton.setEnabled(True) + self.DownloadButton.setEnabled(True) + self.DeleteButton.setEnabled(False) + self.CancelButton.setEnabled(True) + self.ConfirmButton.setEnabled(False) @Slot() def onRefreshButtonClicked( @@ -302,7 +419,6 @@ class ALWebDriverDownloadDialog(QDialog): self.refreshDriverList() - @Slot() def onDeleteButtonClicked( self @@ -355,7 +471,7 @@ class ALWebDriverDownloadDialog(QDialog): self.__download_thread.downloadFinished.connect(self.onDownloadFinished) self.__download_thread.downloadError.connect(self.onDownloadError) self.__download_thread.downloadCancelled.connect(self.onDownloadCancelled) - self.__download_thread.finished.connect(self.__onThreadFinished) + self.__download_thread.finished.connect(self.onThreadFinished) self.__download_thread.start() @Slot() @@ -406,7 +522,6 @@ class ALWebDriverDownloadDialog(QDialog): self.updateButtonStates(driver_info) QMessageBox.critical(self, "下载失败 - AutoLibrary", error_message) - @Slot() def onDownloadCancelled( self @@ -423,7 +538,6 @@ class ALWebDriverDownloadDialog(QDialog): self.updateButtonStates(driver_info) self.ProgressText.setText("下载已取消") - @Slot() def onConfirmButtonClicked( self @@ -439,7 +553,6 @@ class ALWebDriverDownloadDialog(QDialog): self.__confirmed = True self.accept() - @Slot() def onCancelButtonClicked( self @@ -458,119 +571,3 @@ class ALWebDriverDownloadDialog(QDialog): self.__confirmed = False self.__selected_driver_info = None self.reject() - - - def closeEvent( - self, - event: QCloseEvent - ): - - if self.__download_thread and self.__download_thread.isRunning(): - reply = QMessageBox.question( - self, - "确认关闭 - AutoLibrary", - "驱动正在下载中, 确定要取消并关闭对话框吗 ?", - QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No - ) - if reply == QMessageBox.StandardButton.No: - event.ignore() - return - self.__download_thread.stop() - if not self.__confirmed: - self.__selected_driver_info = None - event.accept() - super().closeEvent(event) - - def __onThreadFinished( - self - ): - - if self.__download_thread: - self.__download_thread.deleteLater() - self.__download_thread = None - - - def getSelectedDriverInfo( - self - ) -> Optional[WebDriverInfo]: - - return self.__selected_driver_info - - - def updateDriverInfoDisplay( - self, - driver_info: WebDriverInfo - ): - - if driver_info.driver_type == WebDriverType.CHROME: - driver_type = "Google Chrome" - elif driver_info.driver_type == WebDriverType.FIREFOX: - driver_type = "Mozilla Firefox" - elif driver_info.driver_type == WebDriverType.EDGE: - driver_type = "Microsoft Edge" - else: - driver_type = "未知" - self.BrowserTypeLabel.setText(f"类型:{driver_type}") - self.VersionLabel.setText(f"版本:{driver_info.driver_version}") - if driver_info.driver_path: - self.PathLabel.setText(str(driver_info.driver_path)) - else: - self.PathLabel.setText("未安装") - match driver_info.driver_status: - case WebDriverStatus.NOT_INSTALLED: - self.StatusLabel.status = ALStatusLabel.Status.WAITING - case WebDriverStatus.INSTALLED: - self.StatusLabel.status = ALStatusLabel.Status.SUCCESS - case WebDriverStatus.DOWNLOADING: - self.StatusLabel.status = ALStatusLabel.Status.RUNNING - case WebDriverStatus.ERROR: - self.StatusLabel.status = ALStatusLabel.Status.FAILURE - - - def updateProgressBarStates( - self, - driver_info: WebDriverInfo - ): - - if driver_info.driver_status == WebDriverStatus.NOT_INSTALLED: - self.ProgressBar.setValue(0) - self.ProgressText.setText("未安装") - elif driver_info.driver_status == WebDriverStatus.INSTALLED: - self.ProgressBar.setValue(100) - self.ProgressText.setText("已安装") - elif driver_info.driver_status == WebDriverStatus.DOWNLOADING: - pass # update by worker thread - elif driver_info.driver_status == WebDriverStatus.ERROR: - self.ProgressBar.setValue(0) - self.ProgressText.setText("下载失败") - - - def updateButtonStates( - self, - driver_info: WebDriverInfo - ): - - if driver_info.driver_status == WebDriverStatus.NOT_INSTALLED: - self.RefreshButton.setEnabled(True) - self.DeleteButton.setEnabled(False) - self.DownloadButton.setEnabled(True) - self.CancelButton.setEnabled(True) - self.ConfirmButton.setEnabled(False) - elif driver_info.driver_status == WebDriverStatus.INSTALLED: - self.RefreshButton.setEnabled(True) - self.DownloadButton.setEnabled(False) - self.DeleteButton.setEnabled(True) - self.CancelButton.setEnabled(True) - self.ConfirmButton.setEnabled(True) - elif driver_info.driver_status == WebDriverStatus.DOWNLOADING: - self.RefreshButton.setEnabled(False) - self.DownloadButton.setEnabled(False) - self.DeleteButton.setEnabled(False) - self.CancelButton.setEnabled(True) - self.ConfirmButton.setEnabled(False) - elif driver_info.driver_status == WebDriverStatus.ERROR: - self.RefreshButton.setEnabled(True) - self.DownloadButton.setEnabled(True) - self.DeleteButton.setEnabled(False) - self.CancelButton.setEnabled(True) - self.ConfirmButton.setEnabled(False) diff --git a/src/interfaces/ConfigProvider.py b/src/interfaces/ConfigProvider.py index bf5ebb3..9ee5bb4 100644 --- a/src/interfaces/ConfigProvider.py +++ b/src/interfaces/ConfigProvider.py @@ -16,6 +16,7 @@ class ConfigType(Enum): """ Config type enum. Values represent the default filename. """ + GLOBAL = "autolibrary.json" BULLETIN = "bulletin.json" TIMERTASK = "timer_task.json" @@ -30,6 +31,7 @@ class ConfigPath: Consumers pass this directly to ConfigProvider.get/set, eliminating the need to import ConfigType separately. """ + config_type: ConfigType key: str = "" diff --git a/src/managers/config/ConfigManager.py b/src/managers/config/ConfigManager.py index 1dc108f..4f89eb4 100644 --- a/src/managers/config/ConfigManager.py +++ b/src/managers/config/ConfigManager.py @@ -33,7 +33,6 @@ class ConfigTemplate: self.__config_type = config_type - def template( self ) -> dict: @@ -83,7 +82,6 @@ class ConfigManager: self.initialize() - def initialize( self ): @@ -91,7 +89,6 @@ class ConfigManager: for config_type in ConfigType: self.load(config_type) - def load( self, config_type: ConfigType @@ -108,7 +105,6 @@ class ConfigManager: self.__config_data[config_type.value] = ConfigTemplate(config_type).template() JSONWriter(config_path, self.__config_data[config_type.value]) - def get( self, key: ConfigPath, @@ -126,7 +122,6 @@ class ConfigManager: return default return config_data.get(keys[-1], default) - def set( self, key: ConfigPath, @@ -147,7 +142,6 @@ class ConfigManager: config_data[keys[-1]] = value self.save(key.config_type) - def save( self, config_type: ConfigType @@ -156,7 +150,6 @@ class ConfigManager: config_path = os.path.join(self.__config_dir, config_type.value) JSONWriter(config_path, self.__config_data[config_type.value]) - def configDir( self ) -> str: @@ -169,6 +162,7 @@ _config_manager_instance : ConfigManager | None = None # Singleton instance of ConfigManager. _instance_lock = threading.Lock() + def instance( config_dir: str = "" ) -> ConfigManager: diff --git a/src/managers/driver/WebBrowserDetector.py b/src/managers/driver/WebBrowserDetector.py index 26f246a..bcf6a64 100644 --- a/src/managers/driver/WebBrowserDetector.py +++ b/src/managers/driver/WebBrowserDetector.py @@ -41,6 +41,7 @@ class WebBrowserArch(Enum): MACX86_64 = 6 MACARM = 7 + @dataclass class WebBrowserInfo: """ @@ -70,7 +71,6 @@ class WebBrowserArchDetector: pass - def detect( self ) -> WebBrowserArch: @@ -123,7 +123,6 @@ class WebBrowserDetector: self.browser_arch = WebBrowserArchDetector().detect() self.browser_infos : list[WebBrowserInfo] = [] - def detect( self ) -> list[WebBrowserInfo]: diff --git a/src/managers/driver/WebDriverDownloader.py b/src/managers/driver/WebDriverDownloader.py index 945b103..e8189ae 100644 --- a/src/managers/driver/WebDriverDownloader.py +++ b/src/managers/driver/WebDriverDownloader.py @@ -95,7 +95,6 @@ class WebDriverName: self.driver_type = driver_type - def __str__( self ) -> str: @@ -125,7 +124,6 @@ class WebDriverExecName: self.driver_type = driver_type self.arch = arch - def __str__( self ) -> str: @@ -200,7 +198,6 @@ class WebDriverURL: self.arch = arch self.file_name = str(WebDriverFileName(self.version, self.driver_type, self.arch)) - def __str__( self ) -> str: @@ -250,31 +247,6 @@ class WebDriverDownloader: self.download_dir.mkdir(mode=0o0755, parents=True, exist_ok=True) self.download_path = self.download_dir/str(WebDriverFileName(self.version, self.driver_type, self.arch)) - - def download( - self, - progress_callback: Optional[Callable[[float, int, float, str], None]] = None, - cancel_event: Optional[threading.Event] = None - ) -> Optional[Path]: - - try: - # downlaod file : 0% - 98% - if not self._download(progress_callback, cancel_event=cancel_event): - return None - # verify file : 98% - 99% - if not self._verify(progress_callback): - progress_callback(0, 100, 0.0, "验证失败") - return None - # extract file : 99% - 100% - driver_path = self._extract(progress_callback) - if not driver_path: - progress_callback(0, 100, 0.0, "解压失败") - return None - return driver_path - except Exception as e: - raise e - - def _download( self, progress_callback: Optional[Callable[[float, int, float, str], None]] = None, @@ -352,7 +324,6 @@ class WebDriverDownloader: continue raise e - def _verify( self, progress_callback: Optional[Callable[[float, int, float, str], None]] = None @@ -361,7 +332,6 @@ class WebDriverDownloader: progress_callback(98, 100, 0.0, "验证完成") return True - def _extract( self, progress_callback: Optional[Callable[[float, int, float, str], None]] = None @@ -397,7 +367,6 @@ class WebDriverDownloader: except Exception: return None - def _cleanup( self, driver_file: Path @@ -410,6 +379,29 @@ class WebDriverDownloader: else: item.unlink() + def download( + self, + progress_callback: Optional[Callable[[float, int, float, str], None]] = None, + cancel_event: Optional[threading.Event] = None + ) -> Optional[Path]: + + try: + # downlaod file : 0% - 98% + if not self._download(progress_callback, cancel_event=cancel_event): + return None + # verify file : 98% - 99% + if not self._verify(progress_callback): + progress_callback(0, 100, 0.0, "验证失败") + return None + # extract file : 99% - 100% + driver_path = self._extract(progress_callback) + if not driver_path: + progress_callback(0, 100, 0.0, "解压失败") + return None + return driver_path + except Exception as e: + raise e + class ChromeDriverDownloader(WebDriverDownloader): """ diff --git a/src/managers/driver/WebDriverManager.py b/src/managers/driver/WebDriverManager.py index 7846560..30b3645 100644 --- a/src/managers/driver/WebDriverManager.py +++ b/src/managers/driver/WebDriverManager.py @@ -81,7 +81,6 @@ class WebDriverManager: self.initialize() - def initialize( self ): @@ -93,7 +92,6 @@ class WebDriverManager: self._checkDriverStatus() self.__initialized = True - def _detectBrowsers( self ): @@ -105,7 +103,6 @@ class WebDriverManager: for info in browser_infos ] - def _checkDriverStatus( self ): @@ -117,7 +114,6 @@ class WebDriverManager: driver_info.driver_path = driver_path driver_info.driver_status = WebDriverStatus.INSTALLED - def _mapWebBrowserTypeToDriver( self, browser_type: WebBrowserType @@ -132,7 +128,6 @@ class WebDriverManager: else: raise ValueError(f"不支持的 Web 浏览器类型 : {browser_type}") - def _mapWebBrowserArchToDriver( self, browser_type: WebBrowserType, @@ -199,7 +194,6 @@ class WebDriverManager: else: raise ValueError(f"不支持的 Web 浏览器类型 : {browser_type}") - def _mapFirefoxDriverVersion( self, version: str @@ -240,7 +234,6 @@ class WebDriverManager: except Exception as e: raise ValueError(f"无效的 Firefox 版本格式 : {version}") from e - def _getDriverInfo( self, browser_info: WebBrowserInfo @@ -256,7 +249,6 @@ class WebDriverManager: driver_info.browser_version = browser_info.browser_version return driver_info - def _getDriverPath( self, driver_info: WebDriverInfo @@ -286,7 +278,6 @@ class WebDriverManager: driver_path = driver_dir/exe_name return driver_path - def refresh( self ): @@ -294,7 +285,6 @@ class WebDriverManager: self._detectBrowsers() self._checkDriverStatus() - def getDriverInfos( self ) -> list[WebDriverInfo]: @@ -302,7 +292,6 @@ class WebDriverManager: with self.__lock: return self.__driver_infos.copy() - def getDriverInfo( self, driver_type: WebDriverType @@ -315,7 +304,6 @@ class WebDriverManager: if info.driver_type == driver_type ] - def getDriverPath( self, driver_info: WebDriverInfo @@ -325,7 +313,6 @@ class WebDriverManager: return driver_info.driver_path return None - def installDriver( self, driver_info: WebDriverInfo, @@ -390,7 +377,6 @@ class WebDriverManager: driver_info.driver_status = WebDriverStatus.ERROR raise e - def cancelDriverDownload( self, driver_info: WebDriverInfo @@ -411,7 +397,6 @@ class WebDriverManager: except Exception: return False - def uninstallDriver( self, driver_info: WebDriverInfo, @@ -441,7 +426,6 @@ class WebDriverManager: driver_info.driver_status = WebDriverStatus.ERROR raise - def driverDir( self ) -> str: diff --git a/src/managers/log/LogManager.py b/src/managers/log/LogManager.py index 21d9022..767a951 100644 --- a/src/managers/log/LogManager.py +++ b/src/managers/log/LogManager.py @@ -89,7 +89,6 @@ class LogManager: self.initialize() - def initialize( self ): @@ -139,7 +138,6 @@ class LogManager: self.__initialized = True - def getLogger( self, name: Optional[str] = None @@ -149,7 +147,6 @@ class LogManager: return self.__logger.getChild(name) return self.__logger - def setLevel( self, level: int @@ -158,7 +155,6 @@ class LogManager: if self.__logger: self.__logger.setLevel(level) - def logDir( self ) -> str: @@ -171,6 +167,7 @@ _log_manager_instance = None # Singleton instance lock. _instance_lock = threading.Lock() + def instance( log_dir: str = "" ) -> LogManager: diff --git a/src/operators/AutoLib.py b/src/operators/AutoLib.py index 1972349..d3a4a85 100644 --- a/src/operators/AutoLib.py +++ b/src/operators/AutoLib.py @@ -49,7 +49,6 @@ class AutoLib(MsgBase): raise Exception("浏览器驱动URL初始化失败 !") self.__initLibOperators() - def __initBrowserDriver( self ) -> bool: @@ -142,7 +141,6 @@ class AutoLib(MsgBase): self._showTrace(f"浏览器驱动已初始化, 类型: {self.__driver_type}, 路径: {self.__driver_path}") return True - def __initLibOperators( self ): @@ -157,7 +155,6 @@ class AutoLib(MsgBase): self.__lib_checkin = LibCheckin(self._input_queue, self._output_queue, self.__driver) self.__lib_renew = LibRenew(self._input_queue, self._output_queue, self.__driver) - def __waitResponseLoad( self ) -> bool: @@ -184,7 +181,6 @@ class AutoLib(MsgBase): self._showTrace(f"登录页面加载失败 !", self.TraceLevel.ERROR) return False - def __initDriverUrl( self, ) -> bool: @@ -207,7 +203,6 @@ class AutoLib(MsgBase): return False return True - def __run( self, username: str, @@ -292,7 +287,6 @@ class AutoLib(MsgBase): return -1 return result - def run( self, user_config: dict @@ -339,7 +333,6 @@ class AutoLib(MsgBase): ) return - def close( self ) -> bool: diff --git a/src/operators/LibChecker.py b/src/operators/LibChecker.py index 7a8d52d..3dc944b 100644 --- a/src/operators/LibChecker.py +++ b/src/operators/LibChecker.py @@ -33,7 +33,6 @@ class LibChecker(LibOperator): self.__driver = driver - def _waitResponseLoad( self ) -> bool: @@ -50,7 +49,6 @@ class LibChecker(LibOperator): seconds = int(seconds%60) return f"{hours} 时 {minutes} 分 {seconds} 秒" - def __navigateToReserveRecordPage( self ) -> bool: @@ -67,7 +65,6 @@ class LibChecker(LibOperator): return False return True - def __decodeReserveTime( self, time_element @@ -105,7 +102,6 @@ class LibChecker(LibOperator): } } - def __decodeReserveInfo( self, info_elements @@ -133,7 +129,6 @@ class LibChecker(LibOperator): "status": status, } - def __decodeReserveRecord( self, reservation @@ -160,7 +155,6 @@ class LibChecker(LibOperator): "info": info } - def __loadReserveRecords( self ) -> list: @@ -177,7 +171,6 @@ class LibChecker(LibOperator): self._showTrace("加载预约记录失败 !", self.TraceLevel.ERROR) return None - def __showMoreReserveRecords( self ) -> bool: @@ -203,7 +196,6 @@ class LibChecker(LibOperator): self._showTrace("加载更多预约记录失败 !", self.TraceLevel.ERROR) return False - def __getReserveRecord( self, wanted_date: str, @@ -253,7 +245,6 @@ class LibChecker(LibOperator): break return None - def canReserve( self, date: str @@ -270,7 +261,6 @@ class LibChecker(LibOperator): self._showTrace(f"用户在 {date} 已存在有效预约, 无法预约") return False - def canCheckin( self ) -> bool: @@ -307,7 +297,6 @@ class LibChecker(LibOperator): self._showTrace(f"用户在 {date} 没有有效预约记录, 无法签到") return False - def canRenew( self ) -> tuple[bool, dict]: @@ -335,7 +324,6 @@ class LibChecker(LibOperator): self._showTrace(f"用户在 {date} 没有有效预约记录, 无法续约") return False, None - def postRenewCheck( self, record: dict diff --git a/src/operators/LibCheckin.py b/src/operators/LibCheckin.py index beff718..5adb21e 100644 --- a/src/operators/LibCheckin.py +++ b/src/operators/LibCheckin.py @@ -31,7 +31,6 @@ class LibCheckin(LibOperator): self.__driver = driver - def _waitResponseLoad( self ) -> bool: @@ -87,7 +86,6 @@ class LibCheckin(LibOperator): ok_btn.click() return False - def __enableCheckinBtn( self ) -> bool: @@ -112,7 +110,6 @@ class LibCheckin(LibOperator): self._showTrace("签到按钮启用失败", self.TraceLevel.WARNING) return result - def checkin( self, username: str diff --git a/src/operators/LibCheckout.py b/src/operators/LibCheckout.py index 4e8e7a8..f55ccc8 100644 --- a/src/operators/LibCheckout.py +++ b/src/operators/LibCheckout.py @@ -33,7 +33,6 @@ class LibCheckout(LibOperator): self.__driver = driver - def _waitResponseLoad( self ) -> bool: diff --git a/src/operators/LibLogin.py b/src/operators/LibLogin.py index b5d0f7b..9fd233b 100644 --- a/src/operators/LibLogin.py +++ b/src/operators/LibLogin.py @@ -34,7 +34,6 @@ class LibLogin(LibOperator): self.__driver = driver self.__ddddocr = ddddocr.DdddOcr() - def _waitResponseLoad( self ) -> bool: @@ -58,7 +57,6 @@ class LibLogin(LibOperator): ) return False - def __fillLogInElements( self, username: str, @@ -78,7 +76,6 @@ class LibLogin(LibOperator): return False return True - def __autoRecognizeCaptcha( self ) -> str: @@ -100,7 +97,6 @@ class LibLogin(LibOperator): self._showTrace(f"验证码识别失败 ! : {e}", self.TraceLevel.ERROR) return "" - def __manualRecognizeCaptcha( self ) -> str: @@ -118,7 +114,6 @@ class LibLogin(LibOperator): self._showTrace(f"输入验证码失败 ! : {e}", self.TraceLevel.ERROR) return "" - def __refreshCaptcha( self ): @@ -134,7 +129,6 @@ class LibLogin(LibOperator): self._showTrace(f"刷新验证码失败 ! : {e}", self.TraceLevel.ERROR) return False - def __solveCaptcha( self, auto_captcha: bool = True @@ -158,7 +152,6 @@ class LibLogin(LibOperator): ) return "" - def __fillCaptchaElement( self, captcha_text: str @@ -173,7 +166,6 @@ class LibLogin(LibOperator): self._showTrace(f"验证码填写失败 ! : {e}", self.TraceLevel.ERROR) return False - def login( self, username: str, diff --git a/src/operators/LibLogout.py b/src/operators/LibLogout.py index 63cc63e..14bd02d 100644 --- a/src/operators/LibLogout.py +++ b/src/operators/LibLogout.py @@ -28,14 +28,12 @@ class LibLogout(LibOperator): self.__driver = driver - def _waitResponseLoad( self ) -> bool: return True - def logout( self, username: str diff --git a/src/operators/LibRenew.py b/src/operators/LibRenew.py index 4213168..2d9eec6 100644 --- a/src/operators/LibRenew.py +++ b/src/operators/LibRenew.py @@ -14,7 +14,7 @@ from selenium.webdriver.chrome.webdriver import WebDriver from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC -from base.LibTimeSelector import LibTimeSelector +from operators.abs.LibTimeSelector import LibTimeSelector class LibRenew(LibTimeSelector): @@ -30,7 +30,6 @@ class LibRenew(LibTimeSelector): self.__driver = driver - def _waitResponseLoad( self ) -> bool: @@ -38,7 +37,6 @@ class LibRenew(LibTimeSelector): self.__driver.refresh() return True - def __waitRenewDialog( self ) -> bool: @@ -77,7 +75,6 @@ class LibRenew(LibTimeSelector): return False return True - def __selectNearestTime( self, record: dict, @@ -116,7 +113,6 @@ class LibRenew(LibTimeSelector): self._showTrace(f"当前可供续约的时间有: {free_times}") return False - def __validateAndAdjustRenewTime( self, end_time: str, @@ -139,7 +135,6 @@ class LibRenew(LibTimeSelector): return True return True - def __confirmRenewal( self, best_opt, @@ -167,7 +162,6 @@ class LibRenew(LibTimeSelector): self._showTrace("确认续约时发生错误 !", self.TraceLevel.ERROR) return False - def renew( self, username: str, diff --git a/src/operators/LibReserve.py b/src/operators/LibReserve.py index 23f84d8..930db0a 100644 --- a/src/operators/LibReserve.py +++ b/src/operators/LibReserve.py @@ -15,7 +15,7 @@ from selenium.webdriver.chrome.webdriver import WebDriver from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC -from base.LibTimeSelector import LibTimeSelector +from operators.abs.LibTimeSelector import LibTimeSelector class LibReserve(LibTimeSelector): @@ -48,7 +48,6 @@ class LibReserve(LibTimeSelector): "8": "五层考研" } - def _waitResponseLoad( self, ) -> bool: @@ -99,7 +98,6 @@ class LibReserve(LibTimeSelector): self._showTrace(f"预约结果加载失败 !", self.TraceLevel.ERROR) return False - def __containRequiredInfo( self, reserve_info: dict @@ -135,7 +133,6 @@ class LibReserve(LibTimeSelector): ) return False - def __isValidDate( self, reserve_info: dict @@ -157,7 +154,6 @@ class LibReserve(LibTimeSelector): reserve_info["date"] = cur_date_str return True - def __isValidBeginTime( self, reserve_info: dict @@ -177,7 +173,6 @@ class LibReserve(LibTimeSelector): self._showTrace(f"是否优先选择更早开始时间未指定, 自动设置为 True") return True - def __isValidExpectDuration( self, reserve_info: dict @@ -192,7 +187,6 @@ class LibReserve(LibTimeSelector): self._showTrace("需要满足预约持续时间, 但未指定, 使用默认时长为 4 小时") return True - def __isValidEndTime( self, reserve_info: dict @@ -222,7 +216,6 @@ class LibReserve(LibTimeSelector): self._showTrace(f"是否优先选择较晚结束时间未指定, 自动设置为 True") return True - def __finalCheck( self, reserve_info: dict @@ -275,7 +268,6 @@ class LibReserve(LibTimeSelector): reserve_info["end_time"]["time"] = self._minsToTimeStr(begin_mins + 8*60) return True - def __checkReserveInfo( self, reserve_info: dict @@ -305,7 +297,6 @@ class LibReserve(LibTimeSelector): ) return True - def __clickElement( self, trigger_locator: tuple, @@ -330,7 +321,6 @@ class LibReserve(LibTimeSelector): self._showTrace(fail_msg) return False - def __clickElementByJS( self, trigger_locator_id: str, @@ -364,7 +354,6 @@ class LibReserve(LibTimeSelector): self._showTrace(fail_msg) return result - def __selectDate( self, date_str: str @@ -384,7 +373,6 @@ class LibReserve(LibTimeSelector): fail_msg=f"选择日期失败 ! : {date_str} 不可用" ) - def __selectPlace( self, place: str @@ -406,7 +394,6 @@ class LibReserve(LibTimeSelector): fail_msg=f"选择预约场所失败 ! : {display_place} 不可用" ) - def __selectFloor( self, floor: str @@ -427,7 +414,6 @@ class LibReserve(LibTimeSelector): fail_msg=f"选择楼层失败 ! : {display_floor} 不可用" ) - def __selectRoom( self, room: str @@ -453,7 +439,6 @@ class LibReserve(LibTimeSelector): self._showTrace(f"选择房间失败 ! : {display_room} 不可用", self.TraceLevel.ERROR) return False - def __selectSeat( self, seat_id: str @@ -492,7 +477,6 @@ class LibReserve(LibTimeSelector): self._showTrace(f"座位选择失败 !", self.TraceLevel.ERROR) return False - def __selectNearestTime( self, time_id: str, @@ -547,7 +531,6 @@ class LibReserve(LibTimeSelector): self._showTrace(f"当前可供预约的 {time_type} 有: {free_times}") return -1 - def __selectSeatTime( self, begin_time: dict, @@ -583,7 +566,7 @@ class LibReserve(LibTimeSelector): # If 'satisfy_duration' is True, select end time based on actual begin time if satisfy_duration: - exp_end_mins = int(self.validateAndAdjustEndTime(act_beg_mins, expect_duration)) + exp_end_mins = int(self.__validateAndAdjustEndTime(act_beg_mins, expect_duration)) exp_end_tm_str = self._minsToTimeStr(exp_end_mins) self._showTrace( f"需要满足期望预约持续时间: {expect_duration} 小时, " @@ -607,8 +590,7 @@ class LibReserve(LibTimeSelector): ) return True - - def validateAndAdjustEndTime( + def __validateAndAdjustEndTime( self, begin_mins: int, duration: int @@ -627,7 +609,6 @@ class LibReserve(LibTimeSelector): ) return expect_end_mins - def reserve( self, username: str, diff --git a/src/base/LibTimeSelector.py b/src/operators/abs/LibTimeSelector.py similarity index 98% rename from src/base/LibTimeSelector.py rename to src/operators/abs/LibTimeSelector.py index 737a708..472d6e4 100644 --- a/src/base/LibTimeSelector.py +++ b/src/operators/abs/LibTimeSelector.py @@ -16,7 +16,7 @@ from base.LibOperator import LibOperator class LibTimeSelector(LibOperator): """ - Base class for time selection operations. + Abstract base class for time selection operations. This class provides common time selection logic for reservation and renewal operations, including time conversion utilities and best time option finding. @@ -60,7 +60,6 @@ class LibTimeSelector(LibOperator): hour, minute = divmod(int(mins), 60) return f"{hour:02d}:{minute:02d}" - def _formatTimeRelation( self, abs_diff: int, @@ -78,7 +77,6 @@ class LibTimeSelector(LibOperator): else: return f"正好等于 {time_type}" - def _findBestTimeOption( self, time_options: list, diff --git a/src/operators/abs/__init__.py b/src/operators/abs/__init__.py new file mode 100644 index 0000000..e6f47db --- /dev/null +++ b/src/operators/abs/__init__.py @@ -0,0 +1,6 @@ +""" + Abstract layer class of the LibOperator + + Here are the classes and modules in this package: + - LibTimeSelector: Abstract base class for time selection operations. +""" \ No newline at end of file diff --git a/src/utils/JSONReader.py b/src/utils/JSONReader.py index 04677a3..f8f918e 100644 --- a/src/utils/JSONReader.py +++ b/src/utils/JSONReader.py @@ -42,7 +42,6 @@ class JSONReader: self.__json_data = None self.__read() - def __read( self ): @@ -59,7 +58,6 @@ class JSONReader: except Exception as e: raise Exception(f"读取文件时发生未知错误: {e}") from e - def read( self ) -> bool: @@ -70,14 +68,12 @@ class JSONReader: return False return True - def data( self ) -> dict: return self.__json_data.copy() - def path( self ) -> str: diff --git a/src/utils/JSONWriter.py b/src/utils/JSONWriter.py index baa4d27..8767b2f 100644 --- a/src/utils/JSONWriter.py +++ b/src/utils/JSONWriter.py @@ -46,7 +46,6 @@ class JSONWriter: self.__json_data = json_data.copy() if json_data is not None else {} self.__write() - def __write( self ): @@ -63,7 +62,6 @@ class JSONWriter: except Exception as e: raise Exception(f"写入文件时发生未知错误: {e}") from e - def write( self ) -> bool: @@ -74,7 +72,6 @@ class JSONWriter: return False return True - def path( self ) -> str: