温馨提示:资源来源互联网搬运,遇到源码有授权加密以及后门,请放弃使用,本站不会添加任何后门。请勿相信源码里的广告QQ以及其他联系方式,谨慎被骗!
Rclone 单 WebDav 图形化挂载工具
功能简单, 托盘图标, 自定义图标(同目录app.ico)
本地磁盘和网络磁盘位置可选
自定义显示容量大小(只是显示大小,和实际容量无关)
需要安装winfsp,同目录需要rclone.exe。
欢迎修改打包分享!
![图片[1]-自用 python写的 Rclone WebDav 挂载工具 挂载本地磁盘-狗窝源码站](https://gwrj6.cn/wp-content/uploads/2026/06/20260627172909928-image.png)
![图片[2]-自用 python写的 Rclone WebDav 挂载工具 挂载本地磁盘-狗窝源码站](https://gwrj6.cn/wp-content/uploads/2026/06/20260627172915713-image.png)
import sys
import os
import json
import ctypes
import subprocess
import time
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
QListWidget, QListWidgetItem, QMessageBox, QSystemTrayIcon,
QMenu, QAction, QStyle, QDialog, QFormLayout, QLineEdit,
QComboBox, QSpinBox, QCheckBox, QLabel, QDialogButtonBox,
)
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QFont, QIcon
DEBUG = True
def debug_print(*args, **kwargs):
if DEBUG:
print("[DEBUG]", *args, **kwargs)
RCLONE_EXE = "rclone.exe"
CONFIG_FILE = "mounts.json"
# ---------- 工具函数 ----------
def get_used_drives():
drives = set()
try:
mask = ctypes.windll.kernel32.GetLogicalDrives()
for i in range(26):
if mask & (1 << i):
drives.add(chr(ord('A') + i))
except:
for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
if os.path.exists(f"{letter}:\"):
drives.add(letter)
return drives
def rclone_reveal(obscured: str) -> str:
cmd = [RCLONE_EXE, "reveal", obscured]
try:
proc = subprocess.run(cmd, capture_output=True, text=True,
encoding='utf-8', errors='replace',
creationflags=subprocess.CREATE_NO_WINDOW)
if proc.returncode == 0:
return proc.stdout.strip()
except:
pass
return ""
def get_remote_password(remote_name: str) -> str:
ret, out, err = rclone_cmd(["config", "dump"])
if ret != 0:
return ""
try:
config = json.loads(out)
remote_config = config.get(remote_name, {})
obscured = remote_config.get("pass", "")
if obscured:
return rclone_reveal(obscured)
except:
pass
return ""
def rclone_cmd(args, cwd=None):
cmd = [RCLONE_EXE] + args
debug_print("执行:", " ".join(cmd))
try:
proc = subprocess.run(
cmd, capture_output=True, text=True, encoding='utf-8',
errors='replace', cwd=cwd,
creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
)
stdout = proc.stdout.strip() if proc.stdout else ""
stderr = proc.stderr.strip() if proc.stderr else ""
debug_print("返回码:", proc.returncode)
if stdout:
debug_print("stdout:", stdout[:200])
if stderr:
debug_print("stderr:", stderr[:200])
return proc.returncode, stdout, stderr
except Exception as e:
debug_print("命令执行异常:", e)
return -1, "", str(e)
# ---------- 编辑对话框 ----------
class MountEditDialog(QDialog):
def __init__(self, parent=None, existing=None, existing_password=None):
super().__init__(parent)
self.existing = existing
self.setWindowTitle("编辑挂载配置" if existing else "添加挂载配置")
self.setFixedSize(420, 350)
self.setStyleSheet(parent.styleSheet())
layout = QFormLayout(self)
layout.setSpacing(12)
self.name_edit = QLineEdit()
self.name_edit.setPlaceholderText("显示名称(也是 remote 名)")
layout.addRow("配置名称:", self.name_edit)
self.url_edit = QLineEdit()
self.url_edit.setPlaceholderText("http://192.168.1.5:5244/alist/dav")
layout.addRow("DAV 地址:", self.url_edit)
self.user_edit = QLineEdit()
self.user_edit.setPlaceholderText("用户名")
layout.addRow("用户名:", self.user_edit)
self.pass_edit = QLineEdit()
self.pass_edit.setEchoMode(QLineEdit.Password)
self.pass_edit.setPlaceholderText("密码(留空则不修改)" if existing else "密码")
layout.addRow("密码:", self.pass_edit)
drive_layout = QHBoxLayout()
self.drive_combo = QComboBox()
self.drive_combo.setFixedWidth(80)
drive_layout.addWidget(self.drive_combo)
drive_layout.addStretch()
layout.addRow("盘符:", drive_layout)
size_layout = QHBoxLayout()
self.size_spin = QSpinBox()
self.size_spin.setRange(1, 99999)
self.size_spin.setValue(20)
self.size_spin.setFixedWidth(100)
size_layout.addWidget(self.size_spin)
self.unit_combo = QComboBox()
self.unit_combo.addItems(["M", "G", "T", "P", "E"])
self.unit_combo.setCurrentText("G")
self.unit_combo.setFixedWidth(80)
size_layout.addWidget(self.unit_combo)
size_layout.addStretch()
layout.addRow("磁盘容量:", size_layout)
self.network_check = QCheckBox("网络位置")
self.network_check.setChecked(False)
layout.addRow("", self.network_check)
self.auto_mount_check = QCheckBox("启动时自动挂载")
self.auto_mount_check.setChecked(False)
layout.addRow("", self.auto_mount_check)
# 自定义确认/取消按钮
btn_confirm = QPushButton("确认")
btn_confirm.setFixedSize(80, 30)
btn_confirm.setStyleSheet("""
QPushButton {
background-color: #3498DB; color: white; border-radius: 4px; font-weight: bold;
}
QPushButton:hover {
background-color: #2980B9;
}
""")
btn_confirm.clicked.connect(self.accept)
btn_cancel = QPushButton("取消")
btn_cancel.setFixedSize(80, 30)
btn_cancel.setStyleSheet("""
QPushButton {
background-color: #95A5A6; color: white; border-radius: 4px; font-weight: bold;
}
QPushButton:hover {
background-color: #7F8C8D;
}
""")
btn_cancel.clicked.connect(self.reject)
btn_layout = QHBoxLayout()
btn_layout.addStretch()
btn_layout.addWidget(btn_confirm)
btn_layout.addWidget(btn_cancel)
layout.addRow(btn_layout)
if existing:
self.name_edit.setText(existing.get("name", ""))
self.url_edit.setText(existing.get("url", ""))
self.user_edit.setText(existing.get("user", ""))
self.size_spin.setValue(existing.get("size_value", 20))
old_unit = existing.get("size_unit", "G")
if old_unit.endswith("B"): old_unit = old_unit[0]
self.unit_combo.setCurrentText(old_unit)
self.network_check.setChecked(existing.get("network_mode", True))
self.auto_mount_check.setChecked(existing.get("auto_mount", False))
if existing_password is not None:
self.pass_edit.setText(existing_password)
def populate_drives(self, used_letters, reserved_letters=set()):
self.drive_combo.clear()
all_letters = set(chr(i) for i in range(ord('D'), ord('Z')+1))
excluded = used_letters - reserved_letters
available = sorted(all_letters - excluded)
self.drive_combo.addItems([f"{d}:" for d in available])
if self.existing and self.existing.get("drive"):
idx = self.drive_combo.findText(self.existing["drive"])
if idx >= 0:
self.drive_combo.setCurrentIndex(idx)
def get_data(self):
return {
"name": self.name_edit.text().strip(),
"url": self.url_edit.text().strip(),
"user": self.user_edit.text().strip(),
"password": self.pass_edit.text(),
"drive": self.drive_combo.currentText(),
"size_value": self.size_spin.value(),
"size_unit": self.unit_combo.currentText(),
"network_mode": self.network_check.isChecked(),
"auto_mount": self.auto_mount_check.isChecked()
}
# ---------- 列表项部件 ----------
class MountItemWidget(QWidget):
BTN_OFFSET = "margin-top: -3px;"
@staticmethod
def _btn_style(bg_color, extra=""):
return f"{MountItemWidget.BTN_OFFSET} background-color: {bg_color}; color: white; border-radius:4px; font-weight:bold; {extra}"
def __init__(self, entry, parent_app):
super().__init__()
self.entry = entry
self.app = parent_app
self.is_mounted = False
layout = QHBoxLayout(self)
layout.setContentsMargins(8, 6, 8, 6)
layout.setSpacing(6)
layout.setAlignment(Qt.AlignVCenter)
drive = entry.get("drive", "") or "?"
self.drive_label = QLabel(drive)
self.drive_label.setFixedWidth(30)
self.drive_label.setStyleSheet("font-weight: bold; color: #2c3e50;")
mode = "网络" if entry.get("network_mode", True) else "本地"
self.mode_label = QLabel(mode)
self.mode_label.setFixedWidth(40)
color = "#2980b9" if mode == "网络" else "#27ae60"
self.mode_label.setStyleSheet(f"color: {color}; font-weight: bold;")
self.name_label = QLabel(entry["name"])
self.name_label.setFixedWidth(140)
self.name_label.setStyleSheet("font-weight: bold; color: #2c3e50;")
cap = f"{entry.get('size_value',20)}{entry.get('size_unit','G')}"
self.cap_label = QLabel(cap)
self.cap_label.setFixedWidth(60)
self.cap_label.setStyleSheet("color: #7f8c8d;")
self.btn_auto = QPushButton("自动挂载")
self.btn_auto.setFixedSize(64, 30)
self.btn_auto.setCheckable(True)
self.btn_auto.setChecked(entry.get("auto_mount", False))
self.btn_auto.clicked.connect(self.on_auto_toggled)
self.update_auto_style()
self.btn_action = QPushButton("启动")
self.btn_action.setFixedSize(64, 30)
self.btn_action.clicked.connect(self.on_action_clicked)
self.btn_action.setStyleSheet(self._btn_style("#3498DB"))
self.btn_edit = QPushButton("编辑")
self.btn_edit.setFixedSize(64, 30)
self.btn_edit.clicked.connect(self.edit_entry)
self.btn_edit.setStyleSheet(self._btn_style("#3498DB"))
self.btn_delete = QPushButton("删除")
self.btn_delete.setFixedSize(64, 30)
self.btn_delete.clicked.connect(self.delete_entry)
self.btn_delete.setStyleSheet(self._btn_style("#E74C3C"))
layout.addWidget(self.drive_label)
layout.addWidget(self.mode_label)
layout.addWidget(self.name_label)
layout.addWidget(self.cap_label)
layout.addWidget(self.btn_auto)
layout.addWidget(self.btn_action)
layout.addWidget(self.btn_edit)
layout.addWidget(self.btn_delete)
def on_auto_toggled(self):
checked = self.btn_auto.isChecked()
self.entry["auto_mount"] = checked
self.app.update_entry_metadata(self.entry)
self.update_auto_style()
def update_auto_style(self):
if self.btn_auto.isChecked():
self.btn_auto.setText("自动:开")
self.btn_auto.setStyleSheet(self._btn_style("#27AE60"))
else:
self.btn_auto.setText("自动:关")
self.btn_auto.setStyleSheet(self._btn_style("#95A5A6"))
def on_action_clicked(self):
if not self.entry.get("drive"):
QMessageBox.warning(self, "错误", "请先设置盘符!")
return
if self.is_mounted:
self.open_drive()
else:
self.app.mount_entry(self.entry)
def open_drive(self):
drive = self.entry.get("drive")
if not drive:
QMessageBox.warning(self, "错误", "盘符未设置!")
return
for _ in range(30):
if os.path.exists(drive + "\"):
os.startfile(drive + "\")
return
time.sleep(0.1)
QMessageBox.warning(self, "错误", f"盘符 {drive} 不可用,请等待挂载完成。")
def edit_entry(self):
self.app.edit_entry_dialog(self.entry)
def delete_entry(self):
self.app.delete_entry(self.entry)
# ---------- 主窗口 ----------
class RcloneTrayApp(QWidget):
def __init__(self):
super().__init__()
self.base_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
self.metadata = {}
self.active_mounts = {}
self.load_metadata()
self.init_ui()
self.init_tray()
self.sync_from_rclone()
self.refresh_list()
QTimer.singleShot(1000, self.auto_mount_selected)
debug_print("启动完成,程序目录:", self.base_dir)
def get_app_icon(self):
"""优先使用 app.ico,找不到则使用系统标准图标"""
# 1. 程序所在目录
ico_path = os.path.join(self.base_dir, "app.ico")
if os.path.exists(ico_path):
return QIcon(ico_path)
# 2. PyInstaller 打包后的释放目录
if getattr(sys, 'frozen', False):
meipass = getattr(sys, '_MEIPASS', None)
if meipass:
ico_path = os.path.join(meipass, "app.ico")
if os.path.exists(ico_path):
return QIcon(ico_path)
# 3. 兜底:Qt 内置图标
return self.style().standardIcon(QStyle.SP_DriveNetIcon)
def load_metadata(self):
try:
with open(CONFIG_FILE, "r", encoding="utf-8") as f:
self.metadata = json.load(f)
except:
self.metadata = {}
def save_metadata(self):
with open(CONFIG_FILE, "w", encoding="utf-8") as f:
json.dump(self.metadata, f, indent=2, ensure_ascii=False)
def update_entry_metadata(self, entry):
self.metadata[entry["id"]] = {
k: entry[k] for k in ("name", "url", "user", "drive",
"size_value", "size_unit", "network_mode",
"auto_mount")
}
self.save_metadata()
def sync_from_rclone(self):
ret, out, err = rclone_cmd(["listremotes"], cwd=self.base_dir)
if ret != 0:
debug_print("rclone listremotes 失败:", err)
return
remotes = [r.strip(":") for r in out.splitlines() if r.strip()]
debug_print("发现远程配置:", remotes)
for remote in remotes:
if remote not in self.metadata:
self.metadata[remote] = {
"name": remote, "url": "", "user": "",
"drive": "", "size_value": 20, "size_unit": "G",
"network_mode": True, "auto_mount": False
}
for remote in list(self.metadata.keys()):
if remote not in remotes:
del self.metadata[remote]
self.save_metadata()
def get_all_entries(self):
entries = []
for rid, meta in self.metadata.items():
entry = {"id": rid}
entry.update(meta)
entries.append(entry)
return entries
def init_ui(self):
self.setWindowTitle("Rclone WebDav 挂载管理")
self.setFixedSize(620, 480)
self.setWindowIcon(self.get_app_icon())
# 全局样式
self.setStyleSheet("""
QWidget {
background: #FFFFFF;
font-family: "Microsoft YaHei", "微软雅黑";
font-size: 13px;
color: #2c3e50;
}
QLineEdit, QComboBox, QSpinBox {
background: #FFFFFF;
border: 1px solid #BDC3C7;
border-radius: 4px;
padding: 5px 8px;
color: #2c3e50;
selection-background-color: #3498DB;
}
QLineEdit:focus, QComboBox:focus, QSpinBox:focus {
border-color: #3498DB;
}
QComboBox::drop-down {
width: 0px;
border: none;
}
QSpinBox::up-button, QSpinBox::down-button {
width: 0px;
border: none;
}
QCheckBox {
spacing: 6px;
color: #2c3e50;
}
QListWidget {
background: transparent;
border: none;
outline: none;
}
QListWidget::item {
background: #FFFFFF;
border: none;
border-bottom: 1px solid #E0E4E8;
margin: 0px;
padding: 0px;
}
QListWidget::item:selected {
background: #EBF5FB;
}
""")
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(10, 10, 10, 10)
main_layout.setSpacing(6)
top_layout = QHBoxLayout()
top_layout.setSpacing(10)
self.btn_add = QPushButton("+ 添加配置")
self.btn_add.setFixedSize(120, 30)
self.btn_add.setStyleSheet(
"QPushButton { background-color: #3498DB; color: white; border-radius: 4px; font-weight: bold; }"
"QPushButton:hover { background-color: #2980B9; }"
)
self.btn_add.clicked.connect(self.add_entry_dialog)
self.btn_disconnect = QPushButton("断开挂载")
self.btn_disconnect.setFixedSize(120, 30)
self.btn_disconnect.setStyleSheet(
"QPushButton { background-color: #E74C3C; color: white; border-radius: 4px; font-weight: bold; }"
"QPushButton:hover { background-color: #C0392B; }"
)
self.btn_disconnect.clicked.connect(self.disconnect_all_mounts)
top_layout.addWidget(self.btn_add)
top_layout.addWidget(self.btn_disconnect)
top_layout.addStretch()
main_layout.addLayout(top_layout)
self.list_widget = QListWidget()
self.list_widget.setSpacing(0)
self.list_widget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
main_layout.addWidget(self.list_widget)
def init_tray(self):
tray_icon = self.get_app_icon()
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(tray_icon)
tray_menu = QMenu()
show_act = QAction("显示窗口", self, triggered=self.show_window)
exit_act = QAction("退出", self, triggered=self.quit_app)
tray_menu.addAction(show_act)
tray_menu.addAction(exit_act)
self.tray_icon.setContextMenu(tray_menu)
self.tray_icon.activated.connect(self.on_tray_activated)
self.tray_icon.show()
def show_window(self):
self.showNormal()
self.activateWindow()
self.raise_()
def close_to_tray(self):
self.hide()
def quit_app(self):
subprocess.run("taskkill /IM rclone.exe /F", shell=True,
capture_output=True, creationflags=subprocess.CREATE_NO_WINDOW)
self.active_mounts.clear()
self.tray_icon.hide()
QApplication.quit()
def on_tray_activated(self, reason):
if reason == QSystemTrayIcon.DoubleClick:
self.show_window()
def closeEvent(self, event):
event.ignore()
self.hide()
def disconnect_all_mounts(self):
subprocess.run("taskkill /IM rclone.exe /F", shell=True,
capture_output=True, creationflags=subprocess.CREATE_NO_WINDOW)
self.active_mounts.clear()
self.refresh_list()
QMessageBox.information(self, "提示", "所有挂载已断开")
def refresh_list(self):
self.list_widget.clear()
entries = self.get_all_entries()
for entry in entries:
item = QListWidgetItem()
widget = MountItemWidget(entry, self)
item.setSizeHint(widget.sizeHint())
self.list_widget.addItem(item)
self.list_widget.setItemWidget(item, widget)
auto_on = entry.get("auto_mount", False)
widget.btn_auto.setChecked(auto_on)
widget.update_auto_style()
is_mounted = (entry["drive"] in self.active_mounts)
widget.is_mounted = is_mounted
if is_mounted:
widget.btn_action.setText("打开")
widget.btn_action.setStyleSheet(MountItemWidget._btn_style("#27AE60"))
else:
widget.btn_action.setText("启动")
widget.btn_action.setStyleSheet(MountItemWidget._btn_style("#3498DB"))
def get_used_drive_letters(self):
used = get_used_drives()
for entry in self.get_all_entries():
if entry.get("drive"):
used.add(entry["drive"][0])
return used
def auto_mount_selected(self):
auto_entries = [e for e in self.get_all_entries() if e.get("auto_mount") and e.get("drive")]
debug_print(f"自动挂载条目数: {len(auto_entries)}")
for entry in auto_entries:
debug_print(f"自动挂载: {entry['name']} -> {entry['drive']}")
self.mount_entry(entry)
# ---------- 增删改 ----------
def add_entry_dialog(self):
used = self.get_used_drive_letters()
dlg = MountEditDialog(self)
dlg.populate_drives(used)
if dlg.exec_() != QDialog.Accepted:
return
data = dlg.get_data()
if not data["name"] or not data["url"] or not data["user"] or not data["password"]:
QMessageBox.warning(self, "警告", "必填项不能为空")
return
remote_name = data["name"]
if remote_name in self.metadata:
QMessageBox.warning(self, "警告", "配置名称已存在")
return
ret, out, err = rclone_cmd([
"config", "create", remote_name, "webdav",
"vendor=other", f"url={data['url']}", f"user={data['user']}",
f"pass={data['password']}", "--obscure"
], cwd=self.base_dir)
if ret != 0:
QMessageBox.warning(self, "创建失败", err)
return
self.metadata[remote_name] = {
"name": data["name"], "url": data["url"], "user": data["user"],
"drive": data["drive"], "size_value": data["size_value"],
"size_unit": data["size_unit"], "network_mode": data["network_mode"],
"auto_mount": data["auto_mount"]
}
self.save_metadata()
self.refresh_list()
def edit_entry_dialog(self, entry):
used = self.get_used_drive_letters()
reserved = set(entry["drive"][0]) if entry["drive"] else set()
existing_password = get_remote_password(entry["id"])
dlg = MountEditDialog(self, existing=entry, existing_password=existing_password)
dlg.populate_drives(used, reserved)
if dlg.exec_() != QDialog.Accepted:
return
data = dlg.get_data()
if not data["name"] or not data["url"] or not data["user"]:
QMessageBox.warning(self, "警告", "名称、地址、用户名不能为空")
return
old_remote = entry["id"]
new_name = data["name"]
name_changed = (new_name != old_remote)
new_pass = data["password"]
need_recreate = name_changed or bool(new_pass)
if need_recreate:
if name_changed and new_name in self.metadata:
QMessageBox.warning(self, "警告", "新配置名称已存在")
return
actual_pass = new_pass if new_pass else existing_password
if not actual_pass:
QMessageBox.warning(self, "错误", "无法获取原始密码,请重新输入密码")
return
if name_changed:
rclone_cmd(["config", "delete", old_remote], cwd=self.base_dir)
ret, _, err = rclone_cmd([
"config", "create", new_name, "webdav",
"vendor=other", f"url={data['url']}", f"user={data['user']}",
f"pass={actual_pass}", "--obscure"
], cwd=self.base_dir)
if ret != 0:
QMessageBox.warning(self, "更新失败", err)
return
if name_changed:
del self.metadata[old_remote]
self.metadata[new_name] = {
"name": data["name"], "url": data["url"], "user": data["user"],
"drive": data["drive"], "size_value": data["size_value"],
"size_unit": data["size_unit"], "network_mode": data["network_mode"],
"auto_mount": data["auto_mount"]
}
self.save_metadata()
if entry["drive"] in self.active_mounts:
self.unmount_entry(entry)
self.refresh_list()
def delete_entry(self, entry):
if QMessageBox.question(self, "确认", f"删除 '{entry['name']}'?") != QMessageBox.Yes:
return
if entry["drive"] in self.active_mounts:
self.unmount_entry(entry)
rclone_cmd(["config", "delete", entry["id"]], cwd=self.base_dir)
del self.metadata[entry["id"]]
self.save_metadata()
self.refresh_list()
# ---------- 挂载/卸载 ----------
def mount_entry(self, entry):
drive = entry.get("drive")
if not drive:
QMessageBox.warning(self, "挂载失败", "未设置盘符")
return
if drive in self.active_mounts:
self.unmount_entry(entry)
cache_subdir = os.path.join(self.base_dir, "Temp", entry["name"])
os.makedirs(cache_subdir, exist_ok=True)
total_size = f"{entry.get('size_value',20)}{entry.get('size_unit','G')}"
args = [
"mount", f"{entry['id']}:", drive,
"--cache-dir", f"./Temp/{entry['name']}",
"--vfs-cache-mode", "full",
"--allow-non-empty",
"--dir-cache-time", "5s",
"--allow-other",
"--vfs-disk-space-total-size", total_size
]
if entry.get("network_mode", True):
args.append("--network-mode")
debug_print("挂载命令:", f'rclone {" ".join(args)}')
try:
proc = subprocess.Popen(
[RCLONE_EXE] + args,
cwd=self.base_dir,
creationflags=subprocess.CREATE_NO_WINDOW
)
self.active_mounts[drive] = proc
self.refresh_list()
debug_print(f"挂载成功: {drive}")
except Exception as e:
QMessageBox.warning(self, "挂载失败", str(e))
def unmount_entry(self, entry):
drive = entry["drive"]
if drive not in self.active_mounts:
return
proc = self.active_mounts.pop(drive)
rclone_cmd(["unmount", drive], cwd=self.base_dir)
if proc.poll() is None:
try:
proc.terminate()
proc.wait(timeout=5)
except:
proc.kill()
self.refresh_list()
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
app.setFont(QFont("Microsoft YaHei", 9))
window = RcloneTrayApp()
window.show()
sys.exit(app.exec_())
© 版权声明
THE END

















暂无评论内容