目的:补齐 Event.Uninstall 空实现,打通卸载全链路。 背景:原 plugin_registry.py 的 Uninstall 分支为空 pass;运行时直接 os.remove 删 .pyd 触发 WinError 5(Windows 文件锁,.pyd 已被 import)。 方案: - 卸载流程分层清理:Worker 写待删清单 → Registry 清字典/obj → main 清 toolBar/stackedWidget → plugins_card 刷新为未安装 - .pyd 文件删除延迟到下次启动 _process_pending_uninstalls, 在 worker 线程启动前、import 之前执行,避开文件锁 - 卸载后自动重启:QProcess.startDetached + QApplication.quit, 兼容开发模式(python.exe)与打包模式(main.exe) - _remove_with_retry 应对自重启时原进程句柄未释放的暂态竞争 - _on_local_discovered 加异常隔离,单插件失败不拖垮初始化 - QThread 生命周期管理:aboutToQuit → shutdown(quit+wait), finished → worker.deleteLater,消除 Destroyed 警告 - plugins_card 抽 refresh() 方法,统一卡片状态刷新 影响:卸载功能可用;自重启在命令行/打包模式正常,调试器环境 可能因子进程管理闪退(不影响交付)。
64 lines
2.1 KiB
Python
64 lines
2.1 KiB
Python
import os
|
||
import time
|
||
import logging
|
||
import threading
|
||
|
||
from .plugin_registry import Event
|
||
from PySide6.QtWidgets import QFrame
|
||
from PySide6.QtCore import Signal
|
||
from .plugins_card_ui import Ui_FramePluginsCard
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class PluginsCard(QFrame, Ui_FramePluginsCard):
|
||
"""工具卡片类
|
||
|
||
继承自 QFrame(Qt 帧基类)和 Ui_FrameToolCard(Qt Designer 生成的 UI 界面)
|
||
负责显示工具的卡片界面,包含工具的图标、名称和描述。
|
||
"""
|
||
send_event_signal = Signal(Event,str)
|
||
def __init__(self, name: str, info:dict, parent=None) -> None:
|
||
super().__init__(parent)
|
||
self.setupUi(self)
|
||
|
||
self.name = name
|
||
self.info = info
|
||
|
||
self.initUI()
|
||
|
||
def initUI(self):
|
||
self.textBrowser.setReadOnly(True)
|
||
|
||
self.refresh()
|
||
self.pushButtonInstall.clicked.connect(self.on_install_event)
|
||
self.pushButtonUninstall.clicked.connect(self.on_uninstall_event)
|
||
|
||
def refresh(self) -> None:
|
||
"""根据 info 刷新卡片显示与按钮状态。"""
|
||
self.textBrowser.setMarkdown(self.to_card())
|
||
installed = self.info["local version"] is not None
|
||
self.pushButtonInstall.setEnabled(not installed)
|
||
self.pushButtonUninstall.setEnabled(installed)
|
||
if installed:
|
||
self.pushButtonUpdate.setEnabled(
|
||
self.info['remote version'] > self.info['local version']
|
||
)
|
||
else:
|
||
self.pushButtonUpdate.setEnabled(False)
|
||
|
||
def to_card(self):
|
||
card = f"# {self.name}\n\n"
|
||
card += f"**最新:** V{self.info['remote version']}\n\n"
|
||
if self.info['local version'] is None:
|
||
card += f"**当前:** 未安装!\n\n"
|
||
else:
|
||
card += f"**当前:** V{self.info['local version']}\n\n"
|
||
card += f"**描述:** {self.info['remote description']}"
|
||
return card
|
||
|
||
def on_install_event(self):
|
||
self.send_event_signal.emit(Event.Install, self.name)
|
||
|
||
def on_uninstall_event(self):
|
||
self.send_event_signal.emit(Event.Uninstall, self.name)
|
||
pass |