第二次提交,添加本地文件管理模块
This commit is contained in:
parent
27f4ce314a
commit
8231566051
@ -9,6 +9,7 @@ from typing import List
|
|||||||
from ..core.base_cloud import BaseCloud
|
from ..core.base_cloud import BaseCloud
|
||||||
from ..core.sync_orchestrator import SyncOrchestrator
|
from ..core.sync_orchestrator import SyncOrchestrator
|
||||||
from ..utils.logger import get_logger
|
from ..utils.logger import get_logger
|
||||||
|
from ..utils.folder_manager import get_folder_manager
|
||||||
from .quick_auth import create_quick_auth_panel
|
from .quick_auth import create_quick_auth_panel
|
||||||
|
|
||||||
|
|
||||||
@ -18,85 +19,173 @@ class SyncTab:
|
|||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.clouds = clouds
|
self.clouds = clouds
|
||||||
self.logger = get_logger(__name__)
|
self.logger = get_logger(__name__)
|
||||||
|
self.folder_manager = get_folder_manager()
|
||||||
self.orchestrator = None
|
self.orchestrator = None
|
||||||
self.is_syncing = False
|
self.is_syncing = False
|
||||||
self.log_queue = queue.Queue()
|
self.log_queue = queue.Queue()
|
||||||
|
|
||||||
self.frame = ttk.Frame(parent)
|
self.frame = ttk.Frame(parent)
|
||||||
self._setup_ui()
|
self._setup_ui()
|
||||||
self._start_log_monitor()
|
self._start_log_monitor()
|
||||||
|
|
||||||
|
# 加载历史文件夹
|
||||||
|
self._load_folder_history()
|
||||||
|
|
||||||
def _setup_ui(self):
|
def _setup_ui(self):
|
||||||
main_frame = ttk.Frame(self.frame, padding="10")
|
main_frame = ttk.Frame(self.frame, padding="10")
|
||||||
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
|
||||||
self.frame.columnconfigure(0, weight=1)
|
self.frame.columnconfigure(0, weight=1)
|
||||||
self.frame.rowconfigure(0, weight=1)
|
self.frame.rowconfigure(0, weight=1)
|
||||||
main_frame.columnconfigure(1, weight=1)
|
main_frame.columnconfigure(0, weight=1)
|
||||||
main_frame.rowconfigure(2, weight=1)
|
main_frame.rowconfigure(2, weight=1)
|
||||||
|
|
||||||
ttk.Label(main_frame, text="本地目录:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5))
|
# 文件夹管理区域
|
||||||
|
folder_mgmt_frame = ttk.LabelFrame(main_frame, text="📁 本地文件夹管理", padding="10")
|
||||||
dir_frame = ttk.Frame(main_frame)
|
folder_mgmt_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||||
dir_frame.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=(0, 5))
|
folder_mgmt_frame.columnconfigure(0, weight=1)
|
||||||
dir_frame.columnconfigure(0, weight=1)
|
|
||||||
|
# 当前选中的文件夹显示
|
||||||
self.local_dir_var = tk.StringVar()
|
current_folder_frame = ttk.Frame(folder_mgmt_frame)
|
||||||
self.local_dir_entry = ttk.Entry(dir_frame, textvariable=self.local_dir_var)
|
current_folder_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||||
self.local_dir_entry.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
|
current_folder_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
ttk.Label(current_folder_frame, text="当前文件夹:").grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
|
||||||
|
|
||||||
|
self.current_folder_var = tk.StringVar()
|
||||||
|
current_folder_entry = ttk.Entry(current_folder_frame, textvariable=self.current_folder_var, state='readonly')
|
||||||
|
current_folder_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 5))
|
||||||
|
|
||||||
ttk.Button(
|
ttk.Button(
|
||||||
dir_frame,
|
current_folder_frame,
|
||||||
text="浏览",
|
text="浏览",
|
||||||
command=self._browse_directory
|
command=self._browse_and_add_folder
|
||||||
).grid(row=0, column=1, sticky=tk.E)
|
).grid(row=0, column=2)
|
||||||
|
|
||||||
ttk.Label(main_frame, text="远程目录:").grid(row=1, column=0, sticky=tk.W, pady=(0, 5))
|
# 远程目录设置
|
||||||
|
remote_dir_frame = ttk.Frame(folder_mgmt_frame)
|
||||||
|
remote_dir_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||||
|
remote_dir_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
ttk.Label(remote_dir_frame, text="远程目录:").grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
|
||||||
|
|
||||||
self.remote_dir_var = tk.StringVar(value="/CloudSync")
|
self.remote_dir_var = tk.StringVar(value="/CloudSync")
|
||||||
self.remote_dir_entry = ttk.Entry(main_frame, textvariable=self.remote_dir_var)
|
ttk.Entry(remote_dir_frame, textvariable=self.remote_dir_var).grid(row=0, column=1, sticky=(tk.W, tk.E))
|
||||||
self.remote_dir_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=(0, 5))
|
|
||||||
|
# 文件夹列表
|
||||||
paned_window = ttk.PanedWindow(main_frame, orient=tk.HORIZONTAL)
|
list_frame = ttk.Frame(folder_mgmt_frame)
|
||||||
paned_window.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(10, 0))
|
list_frame.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
|
||||||
|
list_frame.columnconfigure(0, weight=1)
|
||||||
local_frame = ttk.LabelFrame(paned_window, text="本地文件", padding="5")
|
list_frame.rowconfigure(0, weight=1)
|
||||||
paned_window.add(local_frame, weight=1)
|
|
||||||
|
# 创建Treeview显示文件夹列表
|
||||||
|
columns = ('path', 'remote', 'enabled', 'last_accessed')
|
||||||
|
self.folder_tree = ttk.Treeview(list_frame, columns=columns, show='headings', height=6)
|
||||||
|
|
||||||
|
# 设置列标题
|
||||||
|
self.folder_tree.heading('path', text='本地路径')
|
||||||
|
self.folder_tree.heading('remote', text='远程路径')
|
||||||
|
self.folder_tree.heading('enabled', text='状态')
|
||||||
|
self.folder_tree.heading('last_accessed', text='最后访问')
|
||||||
|
|
||||||
|
# 设置列宽
|
||||||
|
self.folder_tree.column('path', width=300)
|
||||||
|
self.folder_tree.column('remote', width=150)
|
||||||
|
self.folder_tree.column('enabled', width=60)
|
||||||
|
self.folder_tree.column('last_accessed', width=150)
|
||||||
|
|
||||||
|
# 滚动条
|
||||||
|
folder_scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.folder_tree.yview)
|
||||||
|
self.folder_tree.configure(yscrollcommand=folder_scrollbar.set)
|
||||||
|
|
||||||
|
self.folder_tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
|
folder_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
||||||
|
|
||||||
|
# 绑定选择事件
|
||||||
|
self.folder_tree.bind('<<TreeviewSelect>>', self._on_folder_select)
|
||||||
|
|
||||||
|
# 文件夹操作按钮
|
||||||
|
folder_btn_frame = ttk.Frame(folder_mgmt_frame)
|
||||||
|
folder_btn_frame.grid(row=3, column=0, sticky=tk.W, pady=(0, 5))
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
folder_btn_frame,
|
||||||
|
text="➕ 添加文件夹",
|
||||||
|
command=self._browse_and_add_folder
|
||||||
|
).pack(side=tk.LEFT, padx=(0, 5))
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
folder_btn_frame,
|
||||||
|
text="➖ 移除选中",
|
||||||
|
command=self._remove_selected_folder
|
||||||
|
).pack(side=tk.LEFT, padx=(0, 5))
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
folder_btn_frame,
|
||||||
|
text="🔄 刷新列表",
|
||||||
|
command=self._refresh_folder_list
|
||||||
|
).pack(side=tk.LEFT, padx=(0, 5))
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
folder_btn_frame,
|
||||||
|
text="🧹 清理无效",
|
||||||
|
command=self._clean_invalid_folders
|
||||||
|
).pack(side=tk.LEFT, padx=(0, 5))
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
folder_btn_frame,
|
||||||
|
text="📤 导出配置",
|
||||||
|
command=self._export_folder_config
|
||||||
|
).pack(side=tk.LEFT, padx=(0, 5))
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
folder_btn_frame,
|
||||||
|
text="📥 导入配置",
|
||||||
|
command=self._import_folder_config
|
||||||
|
).pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
# 文件预览区域
|
||||||
|
preview_paned = ttk.PanedWindow(main_frame, orient=tk.HORIZONTAL)
|
||||||
|
preview_paned.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(10, 0))
|
||||||
|
|
||||||
|
local_frame = ttk.LabelFrame(preview_paned, text="本地文件", padding="5")
|
||||||
|
preview_paned.add(local_frame, weight=1)
|
||||||
|
|
||||||
self.local_tree = ttk.Treeview(local_frame, show="tree")
|
self.local_tree = ttk.Treeview(local_frame, show="tree")
|
||||||
local_scrollbar = ttk.Scrollbar(local_frame, orient=tk.VERTICAL, command=self.local_tree.yview)
|
local_scrollbar = ttk.Scrollbar(local_frame, orient=tk.VERTICAL, command=self.local_tree.yview)
|
||||||
self.local_tree.configure(yscrollcommand=local_scrollbar.set)
|
self.local_tree.configure(yscrollcommand=local_scrollbar.set)
|
||||||
|
|
||||||
self.local_tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
self.local_tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
local_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
local_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
||||||
|
|
||||||
local_frame.columnconfigure(0, weight=1)
|
local_frame.columnconfigure(0, weight=1)
|
||||||
local_frame.rowconfigure(0, weight=1)
|
local_frame.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
remote_frame = ttk.LabelFrame(paned_window, text="远程文件", padding="5")
|
remote_frame = ttk.LabelFrame(preview_paned, text="远程文件", padding="5")
|
||||||
paned_window.add(remote_frame, weight=1)
|
preview_paned.add(remote_frame, weight=1)
|
||||||
|
|
||||||
self.remote_tree = ttk.Treeview(remote_frame, show="tree")
|
self.remote_tree = ttk.Treeview(remote_frame, show="tree")
|
||||||
remote_scrollbar = ttk.Scrollbar(remote_frame, orient=tk.VERTICAL, command=self.remote_tree.yview)
|
remote_scrollbar = ttk.Scrollbar(remote_frame, orient=tk.VERTICAL, command=self.remote_tree.yview)
|
||||||
self.remote_tree.configure(yscrollcommand=remote_scrollbar.set)
|
self.remote_tree.configure(yscrollcommand=remote_scrollbar.set)
|
||||||
|
|
||||||
self.remote_tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
self.remote_tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
remote_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
remote_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
||||||
|
|
||||||
remote_frame.columnconfigure(0, weight=1)
|
remote_frame.columnconfigure(0, weight=1)
|
||||||
remote_frame.rowconfigure(0, weight=1)
|
remote_frame.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
# 控制区域
|
||||||
control_frame = ttk.Frame(main_frame)
|
control_frame = ttk.Frame(main_frame)
|
||||||
control_frame.grid(row=3, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(10, 0))
|
control_frame.grid(row=3, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
|
||||||
control_frame.columnconfigure(1, weight=1)
|
control_frame.columnconfigure(2, weight=1)
|
||||||
|
|
||||||
self.sync_button = ttk.Button(
|
self.sync_button = ttk.Button(
|
||||||
control_frame,
|
control_frame,
|
||||||
text="开始同步",
|
text="开始同步",
|
||||||
command=self._start_sync
|
command=self._start_sync
|
||||||
)
|
)
|
||||||
self.sync_button.grid(row=0, column=0, sticky=tk.W)
|
self.sync_button.grid(row=0, column=0, sticky=tk.W)
|
||||||
|
|
||||||
self.watch_var = tk.BooleanVar()
|
self.watch_var = tk.BooleanVar()
|
||||||
self.watch_checkbox = ttk.Checkbutton(
|
self.watch_checkbox = ttk.Checkbutton(
|
||||||
control_frame,
|
control_frame,
|
||||||
@ -105,50 +194,200 @@ class SyncTab:
|
|||||||
command=self._toggle_watch
|
command=self._toggle_watch
|
||||||
)
|
)
|
||||||
self.watch_checkbox.grid(row=0, column=1, sticky=tk.W, padx=(10, 0))
|
self.watch_checkbox.grid(row=0, column=1, sticky=tk.W, padx=(10, 0))
|
||||||
|
|
||||||
self.progress_var = tk.DoubleVar()
|
self.progress_var = tk.DoubleVar()
|
||||||
self.progress_bar = ttk.Progressbar(
|
self.progress_bar = ttk.Progressbar(
|
||||||
control_frame,
|
control_frame,
|
||||||
variable=self.progress_var,
|
variable=self.progress_var,
|
||||||
maximum=100
|
maximum=100,
|
||||||
|
length=200
|
||||||
)
|
)
|
||||||
self.progress_bar.grid(row=0, column=2, sticky=tk.E, padx=(10, 0))
|
self.progress_bar.grid(row=0, column=2, sticky=tk.E, padx=(10, 0))
|
||||||
|
|
||||||
|
# 日志区域
|
||||||
log_frame = ttk.LabelFrame(main_frame, text="同步日志", padding="5")
|
log_frame = ttk.LabelFrame(main_frame, text="同步日志", padding="5")
|
||||||
log_frame.grid(row=4, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(10, 0))
|
log_frame.grid(row=4, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(10, 0))
|
||||||
|
|
||||||
self.log_text = tk.Text(log_frame, height=8, state=tk.DISABLED)
|
self.log_text = tk.Text(log_frame, height=8, state=tk.DISABLED)
|
||||||
log_scrollbar = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=self.log_text.yview)
|
log_scrollbar = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=self.log_text.yview)
|
||||||
self.log_text.configure(yscrollcommand=log_scrollbar.set)
|
self.log_text.configure(yscrollcommand=log_scrollbar.set)
|
||||||
|
|
||||||
self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||||
log_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
log_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
|
||||||
|
|
||||||
log_frame.columnconfigure(0, weight=1)
|
log_frame.columnconfigure(0, weight=1)
|
||||||
log_frame.rowconfigure(0, weight=1)
|
log_frame.rowconfigure(0, weight=1)
|
||||||
main_frame.rowconfigure(4, weight=1)
|
main_frame.rowconfigure(4, weight=1)
|
||||||
|
|
||||||
# 授权面板(仅在未授权时显示)
|
# 授权面板(仅在未授权时显示)
|
||||||
self.auth_panel = None
|
self.auth_panel = None
|
||||||
self._check_and_show_auth_panel()
|
self._check_and_show_auth_panel()
|
||||||
|
|
||||||
def _browse_directory(self):
|
def _load_folder_history(self):
|
||||||
directory = filedialog.askdirectory()
|
"""加载历史文件夹"""
|
||||||
|
self._refresh_folder_list()
|
||||||
|
|
||||||
|
# 如果有文件夹,选中最近使用的
|
||||||
|
recent_folders = self.folder_manager.get_recent_folders(1)
|
||||||
|
if recent_folders:
|
||||||
|
self.current_folder_var.set(recent_folders[0]['path'])
|
||||||
|
self.remote_dir_var.set(recent_folders[0]['remote_path'])
|
||||||
|
self._refresh_local_tree(recent_folders[0]['path'])
|
||||||
|
|
||||||
|
def _refresh_folder_list(self):
|
||||||
|
"""刷新文件夹列表"""
|
||||||
|
# 清空现有项目
|
||||||
|
for item in self.folder_tree.get_children():
|
||||||
|
self.folder_tree.delete(item)
|
||||||
|
|
||||||
|
# 获取所有文件夹
|
||||||
|
folders = self.folder_manager.get_all_folders()
|
||||||
|
|
||||||
|
# 添加到树形视图
|
||||||
|
for folder in folders:
|
||||||
|
enabled_text = "✅ 启用" if folder.get('enabled', True) else "❌ 禁用"
|
||||||
|
last_accessed = folder.get('last_accessed', 'N/A')
|
||||||
|
if last_accessed != 'N/A':
|
||||||
|
try:
|
||||||
|
from datetime import datetime
|
||||||
|
dt = datetime.fromisoformat(last_accessed)
|
||||||
|
last_accessed = dt.strftime("%Y-%m-%d %H:%M")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.folder_tree.insert('', 'end', values=(
|
||||||
|
folder['path'],
|
||||||
|
folder['remote_path'],
|
||||||
|
enabled_text,
|
||||||
|
last_accessed
|
||||||
|
))
|
||||||
|
|
||||||
|
def _on_folder_select(self, event):
|
||||||
|
"""文件夹选择事件"""
|
||||||
|
selection = self.folder_tree.selection()
|
||||||
|
if selection:
|
||||||
|
item = self.folder_tree.item(selection[0])
|
||||||
|
values = item['values']
|
||||||
|
if values:
|
||||||
|
folder_path = values[0]
|
||||||
|
remote_path = values[1]
|
||||||
|
|
||||||
|
# 更新当前选中的文件夹
|
||||||
|
self.current_folder_var.set(folder_path)
|
||||||
|
self.remote_dir_var.set(remote_path)
|
||||||
|
|
||||||
|
# 刷新文件预览
|
||||||
|
self._refresh_local_tree(folder_path)
|
||||||
|
|
||||||
|
# 更新访问记录
|
||||||
|
self.folder_manager.update_folder_access(folder_path)
|
||||||
|
|
||||||
|
def _browse_and_add_folder(self):
|
||||||
|
"""浏览并添加文件夹"""
|
||||||
|
directory = filedialog.askdirectory(title="选择本地同步文件夹")
|
||||||
if directory:
|
if directory:
|
||||||
self.local_dir_var.set(directory)
|
# 添加到文件夹管理器
|
||||||
self._refresh_local_tree()
|
remote_path = self.remote_dir_var.get()
|
||||||
|
if self.folder_manager.add_folder(directory, remote_path):
|
||||||
def _refresh_local_tree(self):
|
self._refresh_folder_list()
|
||||||
local_dir = self.local_dir_var.get()
|
self.current_folder_var.set(directory)
|
||||||
|
self._refresh_local_tree(directory)
|
||||||
|
self.logger.info(f"Added folder: {directory}")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("错误", f"添加文件夹失败: {directory}")
|
||||||
|
|
||||||
|
def _remove_selected_folder(self):
|
||||||
|
"""移除选中的文件夹"""
|
||||||
|
selection = self.folder_tree.selection()
|
||||||
|
if not selection:
|
||||||
|
messagebox.showwarning("提示", "请先选择要移除的文件夹")
|
||||||
|
return
|
||||||
|
|
||||||
|
item = self.folder_tree.item(selection[0])
|
||||||
|
folder_path = item['values'][0]
|
||||||
|
|
||||||
|
# 确认删除
|
||||||
|
result = messagebox.askyesno(
|
||||||
|
"确认移除",
|
||||||
|
f"确定要从同步列表中移除此文件夹吗?\n\n{folder_path}\n\n注意: 这不会删除本地文件"
|
||||||
|
)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
if self.folder_manager.remove_folder(folder_path):
|
||||||
|
self._refresh_folder_list()
|
||||||
|
self.current_folder_var.set("")
|
||||||
|
self.local_tree.delete(*self.local_tree.get_children())
|
||||||
|
self.logger.info(f"Removed folder: {folder_path}")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("错误", f"移除文件夹失败: {folder_path}")
|
||||||
|
|
||||||
|
def _clean_invalid_folders(self):
|
||||||
|
"""清理无效的文件夹"""
|
||||||
|
removed_count = self.folder_manager.clean_invalid_folders()
|
||||||
|
|
||||||
|
if removed_count > 0:
|
||||||
|
self._refresh_folder_list()
|
||||||
|
messagebox.showinfo("清理完成", f"已清理 {removed_count} 个无效文件夹")
|
||||||
|
else:
|
||||||
|
messagebox.showinfo("清理完成", "没有发现无效文件夹")
|
||||||
|
|
||||||
|
def _export_folder_config(self):
|
||||||
|
"""导出文件夹配置"""
|
||||||
|
export_path = filedialog.asksaveasfilename(
|
||||||
|
title="导出文件夹配置",
|
||||||
|
defaultextension=".json",
|
||||||
|
filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
|
||||||
|
)
|
||||||
|
|
||||||
|
if export_path:
|
||||||
|
if self.folder_manager.export_config(export_path):
|
||||||
|
messagebox.showinfo("导出成功", f"配置已导出到:\n{export_path}")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("导出失败", "导出配置时出错")
|
||||||
|
|
||||||
|
def _import_folder_config(self):
|
||||||
|
"""导入文件夹配置"""
|
||||||
|
import_path = filedialog.askopenfilename(
|
||||||
|
title="导入文件夹配置",
|
||||||
|
filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
|
||||||
|
)
|
||||||
|
|
||||||
|
if import_path:
|
||||||
|
# 询问是合并还是替换
|
||||||
|
result = messagebox.askyesnocancel(
|
||||||
|
"导入选项",
|
||||||
|
"是否合并到现有配置?\n\n" +
|
||||||
|
"点击 '是': 合并配置(保留现有文件夹)\n" +
|
||||||
|
"点击 '否': 替换配置(清空现有文件夹)\n" +
|
||||||
|
"点击 '取消': 取消导入"
|
||||||
|
)
|
||||||
|
|
||||||
|
if result is not None:
|
||||||
|
merge = result
|
||||||
|
if self.folder_manager.import_config(import_path, merge=merge):
|
||||||
|
self._refresh_folder_list()
|
||||||
|
messagebox.showinfo("导入成功", "配置已导入")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("导入失败", "导入配置时出错")
|
||||||
|
|
||||||
|
def _refresh_local_tree(self, local_dir=None):
|
||||||
|
"""刷新本地文件树"""
|
||||||
|
if local_dir is None:
|
||||||
|
local_dir = self.current_folder_var.get()
|
||||||
|
|
||||||
if not local_dir or not os.path.exists(local_dir):
|
if not local_dir or not os.path.exists(local_dir):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.local_tree.delete(*self.local_tree.get_children())
|
self.local_tree.delete(*self.local_tree.get_children())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._populate_tree(self.local_tree, local_dir, "")
|
self._populate_tree(self.local_tree, local_dir, "")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error refreshing local tree: {e}")
|
self.logger.error(f"Error refreshing local tree: {e}")
|
||||||
|
|
||||||
|
def _browse_directory(self):
|
||||||
|
"""向后兼容的方法"""
|
||||||
|
self._browse_and_add_folder()
|
||||||
|
|
||||||
def _populate_tree(self, tree, path, parent):
|
def _populate_tree(self, tree, path, parent):
|
||||||
try:
|
try:
|
||||||
@ -166,53 +405,68 @@ class SyncTab:
|
|||||||
def _start_sync(self):
|
def _start_sync(self):
|
||||||
if self.is_syncing:
|
if self.is_syncing:
|
||||||
return
|
return
|
||||||
|
|
||||||
local_dir = self.local_dir_var.get()
|
# 获取所有启用的文件夹
|
||||||
remote_dir = self.remote_dir_var.get()
|
enabled_folders = self.folder_manager.get_all_folders(enabled_only=True)
|
||||||
|
|
||||||
if not local_dir:
|
if not enabled_folders:
|
||||||
messagebox.showerror("错误", "请选择本地目录")
|
messagebox.showerror("错误", "没有可同步的文件夹\n\n请先添加并启用至少一个文件夹")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not os.path.exists(local_dir):
|
|
||||||
messagebox.showerror("错误", "本地目录不存在")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.is_syncing = True
|
self.is_syncing = True
|
||||||
self.sync_button.config(text="同步中...", state="disabled")
|
self.sync_button.config(text="同步中...", state="disabled")
|
||||||
|
|
||||||
def sync_worker():
|
def sync_worker():
|
||||||
try:
|
try:
|
||||||
self.orchestrator = SyncOrchestrator(
|
total_folders = len(enabled_folders)
|
||||||
self.clouds,
|
for idx, folder_info in enumerate(enabled_folders, 1):
|
||||||
local_dir,
|
local_dir = folder_info['path']
|
||||||
remote_dir
|
remote_dir = folder_info['remote_path']
|
||||||
)
|
|
||||||
|
# 检查文件夹是否存在
|
||||||
def progress_callback(message, current, total):
|
if not os.path.exists(local_dir):
|
||||||
progress = (current / total) * 100 if total > 0 else 0
|
self.log_queue.put(f"⚠️ 跳过不存在的文件夹: {local_dir}")
|
||||||
self.log_queue.put(f"进度: {message} ({current}/{total})")
|
continue
|
||||||
self.progress_var.set(progress)
|
|
||||||
|
self.log_queue.put(f"📁 同步文件夹 ({idx}/{total_folders}): {local_dir}")
|
||||||
self.log_queue.put("开始全量同步...")
|
|
||||||
sync_results = self.orchestrator.full_sync(progress_callback)
|
self.orchestrator = SyncOrchestrator(
|
||||||
|
self.clouds,
|
||||||
self.log_queue.put("同步完成!")
|
local_dir,
|
||||||
for cloud_name, files in sync_results.items():
|
remote_dir
|
||||||
self.log_queue.put(f"{cloud_name}: 同步 {len(files)} 个文件")
|
)
|
||||||
|
|
||||||
if self.watch_var.get():
|
def progress_callback(message, current, total):
|
||||||
|
folder_progress = ((idx - 1) / total_folders) * 100
|
||||||
|
sync_progress = (current / total) * (100 / total_folders) if total > 0 else 0
|
||||||
|
total_progress = folder_progress + sync_progress
|
||||||
|
|
||||||
|
self.log_queue.put(f" {message} ({current}/{total})")
|
||||||
|
self.progress_var.set(total_progress)
|
||||||
|
|
||||||
|
sync_results = self.orchestrator.full_sync(progress_callback)
|
||||||
|
|
||||||
|
for cloud_name, files in sync_results.items():
|
||||||
|
self.log_queue.put(f" ✅ {cloud_name}: 同步 {len(files)} 个文件")
|
||||||
|
|
||||||
|
# 更新访问记录
|
||||||
|
self.folder_manager.update_folder_access(local_dir)
|
||||||
|
|
||||||
|
self.log_queue.put("✅ 所有文件夹同步完成!")
|
||||||
|
|
||||||
|
# 如果启用了实时监控
|
||||||
|
if self.watch_var.get() and self.orchestrator:
|
||||||
self.orchestrator.start_watching()
|
self.orchestrator.start_watching()
|
||||||
self.log_queue.put("开始实时监控...")
|
self.log_queue.put("👁️ 开始实时监控...")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log_queue.put(f"同步失败: {e}")
|
self.log_queue.put(f"❌ 同步失败: {e}")
|
||||||
self.logger.error(f"Sync failed: {e}")
|
self.logger.error(f"Sync failed: {e}")
|
||||||
finally:
|
finally:
|
||||||
self.is_syncing = False
|
self.is_syncing = False
|
||||||
self.sync_button.config(text="开始同步", state="normal")
|
self.sync_button.config(text="开始同步", state="normal")
|
||||||
self.progress_var.set(0)
|
self.progress_var.set(0)
|
||||||
|
|
||||||
threading.Thread(target=sync_worker, daemon=True).start()
|
threading.Thread(target=sync_worker, daemon=True).start()
|
||||||
|
|
||||||
def _toggle_watch(self):
|
def _toggle_watch(self):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user