第二次提交,添加本地文件管理模块
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.sync_orchestrator import SyncOrchestrator
|
||||
from ..utils.logger import get_logger
|
||||
from ..utils.folder_manager import get_folder_manager
|
||||
from .quick_auth import create_quick_auth_panel
|
||||
|
||||
|
||||
@ -18,85 +19,173 @@ class SyncTab:
|
||||
self.parent = parent
|
||||
self.clouds = clouds
|
||||
self.logger = get_logger(__name__)
|
||||
self.folder_manager = get_folder_manager()
|
||||
self.orchestrator = None
|
||||
self.is_syncing = False
|
||||
self.log_queue = queue.Queue()
|
||||
|
||||
|
||||
self.frame = ttk.Frame(parent)
|
||||
self._setup_ui()
|
||||
self._start_log_monitor()
|
||||
|
||||
# 加载历史文件夹
|
||||
self._load_folder_history()
|
||||
|
||||
def _setup_ui(self):
|
||||
main_frame = ttk.Frame(self.frame, padding="10")
|
||||
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||
|
||||
|
||||
self.frame.columnconfigure(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)
|
||||
|
||||
ttk.Label(main_frame, text="本地目录:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5))
|
||||
|
||||
dir_frame = ttk.Frame(main_frame)
|
||||
dir_frame.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=(0, 5))
|
||||
dir_frame.columnconfigure(0, weight=1)
|
||||
|
||||
self.local_dir_var = tk.StringVar()
|
||||
self.local_dir_entry = ttk.Entry(dir_frame, textvariable=self.local_dir_var)
|
||||
self.local_dir_entry.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 5))
|
||||
|
||||
|
||||
# 文件夹管理区域
|
||||
folder_mgmt_frame = ttk.LabelFrame(main_frame, text="📁 本地文件夹管理", padding="10")
|
||||
folder_mgmt_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||
folder_mgmt_frame.columnconfigure(0, weight=1)
|
||||
|
||||
# 当前选中的文件夹显示
|
||||
current_folder_frame = ttk.Frame(folder_mgmt_frame)
|
||||
current_folder_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||
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(
|
||||
dir_frame,
|
||||
text="浏览",
|
||||
command=self._browse_directory
|
||||
).grid(row=0, column=1, sticky=tk.E)
|
||||
|
||||
ttk.Label(main_frame, text="远程目录:").grid(row=1, column=0, sticky=tk.W, pady=(0, 5))
|
||||
|
||||
current_folder_frame,
|
||||
text="浏览",
|
||||
command=self._browse_and_add_folder
|
||||
).grid(row=0, column=2)
|
||||
|
||||
# 远程目录设置
|
||||
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_entry = ttk.Entry(main_frame, textvariable=self.remote_dir_var)
|
||||
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)
|
||||
paned_window.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(10, 0))
|
||||
|
||||
local_frame = ttk.LabelFrame(paned_window, text="本地文件", padding="5")
|
||||
paned_window.add(local_frame, weight=1)
|
||||
|
||||
ttk.Entry(remote_dir_frame, textvariable=self.remote_dir_var).grid(row=0, column=1, sticky=(tk.W, tk.E))
|
||||
|
||||
# 文件夹列表
|
||||
list_frame = ttk.Frame(folder_mgmt_frame)
|
||||
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)
|
||||
list_frame.rowconfigure(0, 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")
|
||||
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.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_frame.columnconfigure(0, weight=1)
|
||||
local_frame.rowconfigure(0, weight=1)
|
||||
|
||||
remote_frame = ttk.LabelFrame(paned_window, text="远程文件", padding="5")
|
||||
paned_window.add(remote_frame, weight=1)
|
||||
|
||||
|
||||
remote_frame = ttk.LabelFrame(preview_paned, text="远程文件", padding="5")
|
||||
preview_paned.add(remote_frame, weight=1)
|
||||
|
||||
self.remote_tree = ttk.Treeview(remote_frame, show="tree")
|
||||
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.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_frame.columnconfigure(0, weight=1)
|
||||
remote_frame.rowconfigure(0, weight=1)
|
||||
|
||||
|
||||
# 控制区域
|
||||
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.columnconfigure(1, weight=1)
|
||||
|
||||
control_frame.grid(row=3, column=0, sticky=(tk.W, tk.E), pady=(10, 0))
|
||||
control_frame.columnconfigure(2, weight=1)
|
||||
|
||||
self.sync_button = ttk.Button(
|
||||
control_frame,
|
||||
text="开始同步",
|
||||
control_frame,
|
||||
text="开始同步",
|
||||
command=self._start_sync
|
||||
)
|
||||
self.sync_button.grid(row=0, column=0, sticky=tk.W)
|
||||
|
||||
|
||||
self.watch_var = tk.BooleanVar()
|
||||
self.watch_checkbox = ttk.Checkbutton(
|
||||
control_frame,
|
||||
@ -105,50 +194,200 @@ class SyncTab:
|
||||
command=self._toggle_watch
|
||||
)
|
||||
self.watch_checkbox.grid(row=0, column=1, sticky=tk.W, padx=(10, 0))
|
||||
|
||||
|
||||
self.progress_var = tk.DoubleVar()
|
||||
self.progress_bar = ttk.Progressbar(
|
||||
control_frame,
|
||||
variable=self.progress_var,
|
||||
maximum=100
|
||||
maximum=100,
|
||||
length=200
|
||||
)
|
||||
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.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)
|
||||
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.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_frame.columnconfigure(0, weight=1)
|
||||
log_frame.rowconfigure(0, weight=1)
|
||||
main_frame.rowconfigure(4, weight=1)
|
||||
|
||||
|
||||
# 授权面板(仅在未授权时显示)
|
||||
self.auth_panel = None
|
||||
self._check_and_show_auth_panel()
|
||||
|
||||
def _browse_directory(self):
|
||||
directory = filedialog.askdirectory()
|
||||
def _load_folder_history(self):
|
||||
"""加载历史文件夹"""
|
||||
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:
|
||||
self.local_dir_var.set(directory)
|
||||
self._refresh_local_tree()
|
||||
|
||||
def _refresh_local_tree(self):
|
||||
local_dir = self.local_dir_var.get()
|
||||
# 添加到文件夹管理器
|
||||
remote_path = self.remote_dir_var.get()
|
||||
if self.folder_manager.add_folder(directory, remote_path):
|
||||
self._refresh_folder_list()
|
||||
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):
|
||||
return
|
||||
|
||||
|
||||
self.local_tree.delete(*self.local_tree.get_children())
|
||||
|
||||
|
||||
try:
|
||||
self._populate_tree(self.local_tree, local_dir, "")
|
||||
except Exception as 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):
|
||||
try:
|
||||
@ -166,53 +405,68 @@ class SyncTab:
|
||||
def _start_sync(self):
|
||||
if self.is_syncing:
|
||||
return
|
||||
|
||||
local_dir = self.local_dir_var.get()
|
||||
remote_dir = self.remote_dir_var.get()
|
||||
|
||||
if not local_dir:
|
||||
messagebox.showerror("错误", "请选择本地目录")
|
||||
|
||||
# 获取所有启用的文件夹
|
||||
enabled_folders = self.folder_manager.get_all_folders(enabled_only=True)
|
||||
|
||||
if not enabled_folders:
|
||||
messagebox.showerror("错误", "没有可同步的文件夹\n\n请先添加并启用至少一个文件夹")
|
||||
return
|
||||
|
||||
if not os.path.exists(local_dir):
|
||||
messagebox.showerror("错误", "本地目录不存在")
|
||||
return
|
||||
|
||||
|
||||
self.is_syncing = True
|
||||
self.sync_button.config(text="同步中...", state="disabled")
|
||||
|
||||
|
||||
def sync_worker():
|
||||
try:
|
||||
self.orchestrator = SyncOrchestrator(
|
||||
self.clouds,
|
||||
local_dir,
|
||||
remote_dir
|
||||
)
|
||||
|
||||
def progress_callback(message, current, total):
|
||||
progress = (current / total) * 100 if total > 0 else 0
|
||||
self.log_queue.put(f"进度: {message} ({current}/{total})")
|
||||
self.progress_var.set(progress)
|
||||
|
||||
self.log_queue.put("开始全量同步...")
|
||||
sync_results = self.orchestrator.full_sync(progress_callback)
|
||||
|
||||
self.log_queue.put("同步完成!")
|
||||
for cloud_name, files in sync_results.items():
|
||||
self.log_queue.put(f"{cloud_name}: 同步 {len(files)} 个文件")
|
||||
|
||||
if self.watch_var.get():
|
||||
total_folders = len(enabled_folders)
|
||||
for idx, folder_info in enumerate(enabled_folders, 1):
|
||||
local_dir = folder_info['path']
|
||||
remote_dir = folder_info['remote_path']
|
||||
|
||||
# 检查文件夹是否存在
|
||||
if not os.path.exists(local_dir):
|
||||
self.log_queue.put(f"⚠️ 跳过不存在的文件夹: {local_dir}")
|
||||
continue
|
||||
|
||||
self.log_queue.put(f"📁 同步文件夹 ({idx}/{total_folders}): {local_dir}")
|
||||
|
||||
self.orchestrator = SyncOrchestrator(
|
||||
self.clouds,
|
||||
local_dir,
|
||||
remote_dir
|
||||
)
|
||||
|
||||
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.log_queue.put("开始实时监控...")
|
||||
|
||||
self.log_queue.put("👁️ 开始实时监控...")
|
||||
|
||||
except Exception as e:
|
||||
self.log_queue.put(f"同步失败: {e}")
|
||||
self.log_queue.put(f"❌ 同步失败: {e}")
|
||||
self.logger.error(f"Sync failed: {e}")
|
||||
finally:
|
||||
self.is_syncing = False
|
||||
self.sync_button.config(text="开始同步", state="normal")
|
||||
self.progress_var.set(0)
|
||||
|
||||
|
||||
threading.Thread(target=sync_worker, daemon=True).start()
|
||||
|
||||
def _toggle_watch(self):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user