1
1
mirror of https://github.com/KenanZhu/AutoLibrary.git synced 2026-06-17 23:13:03 +08:00
Files
AutoLibrary/src/gui/ALWebDriverDownloadDialog.py
T
KenanZhu bb63ee6f03 refactor(gui): 统一 Qt 控件变量命名风格为 PascalCase
将所有 self.xxx 形式的 Qt 控件属性名以及 Qt 对象局部变量由 snake_case
重命名为 PascalCase,提升代码可读性和一致性。涉及 14 个文件,涵盖:
- AutoScript 编排/编辑对话框子模块
- 配置/主窗口/用户树/座位图等核心界面组件
- 定时任务管理相关界面
- 状态标签/浏览器驱动下载对话框

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 19:35:03 +08:00

568 lines
19 KiB
Python

# -*- coding: utf-8 -*-
"""
Copyright (c) 2026 KenanZhu.
All rights reserved.
This software is provided "as is", without any warranty of any kind.
You may use, modify, and distribute this file under the terms of the MIT License.
See the LICENSE file for details.
"""
import threading
from typing import Optional
from PySide6.QtCore import (
Qt,
Slot,
QThread,
Signal
)
from PySide6.QtWidgets import (
QDialog,
QLabel,
QComboBox,
QProgressBar,
QPushButton,
QVBoxLayout,
QHBoxLayout,
QMessageBox,
QFrame,
QLineEdit
)
from PySide6.QtGui import QCloseEvent
from managers.driver.WebDriverManager import (
instance as webdriverInstance,
WebDriverManager,
WebDriverInfo,
WebDriverType,
WebDriverStatus
)
from gui.ALStatusLabel import ALStatusLabel
class DownloadWorker(QThread):
"""
Worker thread for downloading web drivers.
"""
progress = Signal(float, int, float, str)
downloadFinished = Signal(object, str)
downloadError = Signal(str)
downloadCancelled = Signal()
def __init__(
self,
driver_manager: WebDriverManager,
driver_info: WebDriverInfo
):
super().__init__()
self.__driver_manager = driver_manager
self.__driver_info = driver_info
self.__driver_path = None
self.__cancelled = False
self.__cancel_event = threading.Event()
def cancel(
self
):
"""
Cancel the download operation.
"""
self.__cancelled = True
self.__cancel_event.set()
def run(
self
):
try:
if self.__cancelled:
self.downloadCancelled.emit()
return
self.__driver_path = self.__driver_manager.installDriver(
self.__driver_info,
progress_callback=self.onProgress,
cancel_event=self.__cancel_event
)
if self.__cancelled:
self.downloadCancelled.emit()
return
if self.__driver_path:
self.downloadFinished.emit(self.__driver_path, "")
else:
self.downloadError.emit("下载失败: 未返回有效路径")
except Exception as e:
if not self.__cancelled:
self.downloadError.emit(f"下载失败: {str(e)}")
def onProgress(
self,
downloaded: float,
total: int,
speed: float,
message: str
):
if self.__cancel_event.is_set():
self.__cancelled = True
if not self.__cancelled:
self.progress.emit(downloaded, total, speed, message)
def stop(
self
):
"""
Cancel and wait for the thread to finish.
Must only be called from the main thread.
"""
self.cancel()
if not self.isFinished():
if not self.wait(5000):
self.terminate()
self.wait()
class ALWebDriverDownloadDialog(QDialog):
def __init__(
self,
parent: Optional[QDialog] = None,
driver_dir: str = ""
):
"""
Web driver download dialog.
Args:
parent: Parent widget.
driver_dir: Driver directory path.
"""
super().__init__(parent)
self.__driver_dir = driver_dir
self.__driver_manager: Optional[WebDriverManager] = None
self.__confirmed = False
self.__selected_driver_info: Optional[WebDriverInfo] = None
self.__driver_infos: list[WebDriverInfo] = []
self.__download_thread: Optional[DownloadWorker] = None
self.setupUi()
self.connectSignals()
self.initializeDriverManager()
self.refreshDriverList()
def showEvent(
self,
event
):
result = super().showEvent(event)
if self.parent():
screen_rect = self.screen().geometry()
target_pos = self.parent().geometry().center()
target_pos.setX(target_pos.x() - self.width()//2)
target_pos.setY(target_pos.y() - self.height()//2)
if target_pos.x() < 0:
target_pos.setX(0)
if target_pos.x() + self.width() > screen_rect.width():
target_pos.setX(screen_rect.width() - self.width())
if target_pos.y() < 0:
target_pos.setY(0)
if target_pos.y() + self.height() > screen_rect.height():
target_pos.setY(screen_rect.height() - self.height())
self.move(target_pos)
return result
def setupUi(
self
):
self.setModal(True)
self.setMaximumHeight(240)
self.setMinimumHeight(240)
self.setWindowTitle("浏览器驱动下载 - AutoLibrary")
self.MainLayout = QVBoxLayout(self)
self.MainLayout.setContentsMargins(5, 5, 5, 5)
self.MainLayout.setSpacing(5)
self.BrowserCountLabel = QLabel("检测到 0 个可用浏览器:")
self.MainLayout.addWidget(self.BrowserCountLabel)
self.DriverInfoLayout = QHBoxLayout()
self.DriverInfoLayout.setSpacing(5)
self.DriverComboBox = QComboBox()
self.DriverInfoLayout.addWidget(self.DriverComboBox)
self.StatusLabel = ALStatusLabel()
self.StatusLabel.setFixedSize(32, 32)
self.DriverInfoLayout.addWidget(self.StatusLabel)
self.MainLayout.addLayout(self.DriverInfoLayout)
self.DetailLayout = QVBoxLayout()
self.DetailLayout.setSpacing(5)
self.DetailLayout.setContentsMargins(5, 5, 5, 5)
self.BrowserTypeLabel = QLabel("类型:")
self.DetailLayout.addWidget(self.BrowserTypeLabel)
self.VersionLabel = QLabel("版本:")
self.DetailLayout.addWidget(self.VersionLabel)
self.PathLabel = QLineEdit()
self.PathLabel.setReadOnly(True)
self.PathLabel.setText("路径:未安装")
self.DetailLayout.addWidget(self.PathLabel)
self.MainLayout.addLayout(self.DetailLayout)
self.Line = QFrame()
self.Line.setFrameShape(QFrame.Shape.HLine)
self.Line.setFrameShadow(QFrame.Shadow.Sunken)
self.MainLayout.addWidget(self.Line)
self.ProgressBar = QProgressBar()
self.ProgressBar.setValue(0)
self.ProgressBar.setTextVisible(False)
self.MainLayout.addWidget(self.ProgressBar)
self.ProgressText = QLabel("")
self.ProgressText.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.MainLayout.addWidget(self.ProgressText)
self.ControlLayout = QHBoxLayout()
self.ControlLayout.setSpacing(8)
self.ControlLayout.setAlignment(Qt.AlignmentFlag.AlignRight)
self.RefreshButton = QPushButton("刷新")
self.RefreshButton.setFixedSize(80, 25)
self.DownloadButton = QPushButton("下载驱动")
self.DownloadButton.setFixedSize(80, 25)
self.DeleteButton = QPushButton("删除驱动")
self.DeleteButton.setFixedSize(80, 25)
self.CancelButton = QPushButton("取消")
self.CancelButton.setFixedSize(80, 25)
self.ConfirmButton = QPushButton("确认")
self.ConfirmButton.setFixedSize(80, 25)
self.ConfirmButton.setEnabled(False)
self.ControlLayout.addWidget(self.RefreshButton)
self.ControlLayout.addWidget(self.DownloadButton)
self.ControlLayout.addWidget(self.DeleteButton)
self.ControlLayout.addWidget(self.CancelButton)
self.ControlLayout.addWidget(self.ConfirmButton)
self.MainLayout.addLayout(self.ControlLayout)
def connectSignals(
self
):
self.RefreshButton.clicked.connect(self.onRefreshButtonClicked)
self.DownloadButton.clicked.connect(self.onDownloadButtonClicked)
self.DeleteButton.clicked.connect(self.onDeleteButtonClicked)
self.CancelButton.clicked.connect(self.onCancelButtonClicked)
self.ConfirmButton.clicked.connect(self.onConfirmButtonClicked)
self.DriverComboBox.currentIndexChanged.connect(self.onDriverComboBoxChanged)
def initializeDriverManager(
self
):
try:
self.__driver_manager = webdriverInstance(self.__driver_dir)
except ValueError as e:
QMessageBox.warning(self, "初始化失败", f"WebDriverManager 初始化失败:\n{str(e)}")
self.reject()
def refreshDriverList(
self
):
if not self.__driver_manager:
return
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):
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} 个可用浏览器,{installed} 个已安装驱动:")
if self.__driver_infos:
self.DriverComboBox.setCurrentIndex(installed_idx)
def onDriverComboBoxChanged(
self,
index: int
):
if not self.__driver_infos or index < 0 or index >= len(self.__driver_infos):
return
driver_info = self.__driver_infos[index]
self.updateDriverInfoDisplay(driver_info)
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(
self
):
self.refreshDriverList()
@Slot()
def onDeleteButtonClicked(
self
):
index = self.DriverComboBox.currentIndex()
if index < 0 or index >= len(self.__driver_infos):
return
driver_info = self.__driver_infos[index]
if driver_info.driver_status.name != "INSTALLED":
QMessageBox.information(self, "提示 - AutoLibrary", "该驱动未安装, 无需删除")
return
reply = QMessageBox.question(
self,
"确认删除 - AutoLibrary",
f"确定要删除 {driver_info.driver_type.value} 驱动吗 ?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.No:
return
try:
self.__driver_manager.uninstallDriver(driver_info)
self.refreshDriverList()
QMessageBox.information(self, "删除成功 - AutoLibrary", "驱动已成功删除")
except Exception as e:
QMessageBox.critical(self, "删除失败 - AutoLibrary", f"删除驱动时出错:\n{str(e)}")
@Slot()
def onDownloadButtonClicked(
self
):
self.DriverComboBox.setEnabled(False)
index = self.DriverComboBox.currentIndex()
if index < 0 or index >= len(self.__driver_infos):
return
driver_info = self.__driver_infos[index]
if driver_info.driver_status == WebDriverStatus.INSTALLED:
return
driver_info.driver_status = WebDriverStatus.DOWNLOADING # we set this only to update
# the display, and we will set to not installed in the download thread
self.updateDriverInfoDisplay(driver_info)
self.updateProgressBarStates(driver_info)
self.ProgressText.setText("准备开始下载...")
self.updateButtonStates(driver_info)
# set to not installed
driver_info.driver_status = WebDriverStatus.NOT_INSTALLED
self.__download_thread = DownloadWorker(self.__driver_manager, driver_info)
self.__download_thread.progress.connect(self.onDownloadProgress)
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.start()
@Slot()
def onDownloadProgress(
self,
downloaded: float,
total: int,
speed: float,
message: str
):
progress = downloaded
self.ProgressBar.setValue(progress)
if speed >= 1024:
speed_text = f"{speed/1024:.1f} MB/s"
else:
speed_text = f"{speed:.1f} KB/s"
progress_text = f"{message}... {downloaded:.1f}% - {speed_text}"
self.ProgressText.setText(progress_text)
@Slot()
def onDownloadFinished(
self
):
self.DriverComboBox.setEnabled(True)
index = self.DriverComboBox.currentIndex()
if 0 <= index < len(self.__driver_infos):
driver_info = self.__driver_infos[index]
driver_info.driver_status = WebDriverStatus.INSTALLED
self.updateDriverInfoDisplay(driver_info)
self.updateProgressBarStates(driver_info)
self.updateButtonStates(driver_info)
@Slot()
def onDownloadError(
self,
error_message: str
):
self.DriverComboBox.setEnabled(True)
index = self.DriverComboBox.currentIndex()
if 0 <= index < len(self.__driver_infos):
driver_info = self.__driver_infos[index]
driver_info.driver_status = WebDriverStatus.ERROR
self.updateDriverInfoDisplay(driver_info)
self.updateProgressBarStates(driver_info)
self.updateButtonStates(driver_info)
QMessageBox.critical(self, "下载失败 - AutoLibrary", error_message)
@Slot()
def onDownloadCancelled(
self
):
self.DriverComboBox.setEnabled(True)
index = self.DriverComboBox.currentIndex()
if 0 <= index < len(self.__driver_infos):
driver_info = self.__driver_infos[index]
self.__driver_manager.cancelDriverDownload(driver_info)
driver_info.driver_status = WebDriverStatus.NOT_INSTALLED
self.updateDriverInfoDisplay(driver_info)
self.updateProgressBarStates(driver_info)
self.updateButtonStates(driver_info)
self.ProgressText.setText("下载已取消")
@Slot()
def onConfirmButtonClicked(
self
):
index = self.DriverComboBox.currentIndex()
if index < 0 or index >= len(self.__driver_infos):
return
driver_info = self.__driver_infos[index]
if driver_info.driver_status != WebDriverStatus.INSTALLED:
return
self.__selected_driver_info = driver_info
self.__confirmed = True
self.accept()
@Slot()
def onCancelButtonClicked(
self
):
if self.__download_thread:
reply = QMessageBox.question(
self,
"确认取消 - AutoLibrary",
"正在下载中, 确定要取消下载吗 ?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.Yes:
self.__download_thread.cancel()
else:
self.__confirmed = False
self.__selected_driver_info = None
self.reject()