# dialogs_undo.py """撤销功能对话框""" import tkinter as tk from tkinter import ttk, messagebox from operation_history import OperationHistory, OperationStatus, OperationType from dialogs_history import PartitionBackupDialog class UndoDialog: """撤销操作对话框""" def __init__(self, parent, history, partition_backup): self.parent = parent self.history = history self.partition_backup = partition_backup self.result = False self.dialog = tk.Toplevel(parent) self.dialog.title("撤销操作") self.dialog.geometry("700x500") self.dialog.transient(parent) self._center_window() self._create_widgets() self._load_undoable_operations() def _center_window(self): self.dialog.update_idletasks() width = self.dialog.winfo_width() height = self.dialog.winfo_height() x = (self.dialog.winfo_screenwidth() // 2) - (width // 2) y = (self.dialog.winfo_screenheight() // 2) - (height // 2) self.dialog.geometry(f"{width}x{height}+{x}+{y}") def _create_widgets(self): main_frame = ttk.Frame(self.dialog, padding="15") main_frame.pack(fill=tk.BOTH, expand=True) # 标题 title_label = ttk.Label( main_frame, text="可撤销的操作", font=("Arial", 14, "bold") ) title_label.pack(pady=(0, 10)) # 说明 info_label = ttk.Label( main_frame, text="以下操作可以通过分区表备份进行回滚。选择操作查看详情并撤销。", wraplength=600 ) info_label.pack(pady=(0, 10)) # 操作列表 list_frame = ttk.Frame(main_frame) list_frame.pack(fill=tk.BOTH, expand=True) columns = ("时间", "操作类型", "设备", "状态") self.tree = ttk.Treeview(list_frame, columns=columns, show="headings") for col in columns: self.tree.heading(col, text=col) self.tree.column("时间", width=150) self.tree.column("操作类型", width=150) self.tree.column("设备", width=150) self.tree.column("状态", width=100) scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.tree.yview) self.tree.configure(yscrollcommand=scrollbar.set) self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) # 绑定选择事件 self.tree.bind("<>", self._on_select) # 详情区域 detail_frame = ttk.LabelFrame(main_frame, text="操作详情与撤销选项", padding="10") detail_frame.pack(fill=tk.X, pady=(10, 0)) self.detail_text = tk.Text(detail_frame, height=8, wrap=tk.WORD) self.detail_text.pack(fill=tk.BOTH, expand=True) self.detail_text.config(state=tk.DISABLED) # 按钮 btn_frame = ttk.Frame(main_frame) btn_frame.pack(fill=tk.X, pady=10) ttk.Button(btn_frame, text="关闭", command=self.dialog.destroy).pack(side=tk.RIGHT, padx=5) ttk.Button(btn_frame, text="刷新", command=self._load_undoable_operations).pack(side=tk.RIGHT, padx=5) self.undo_btn = ttk.Button(btn_frame, text="撤销选中操作", command=self._undo_selected, state=tk.DISABLED) self.undo_btn.pack(side=tk.RIGHT, padx=5) self.selected_record = None def _load_undoable_operations(self): """加载可撤销的操作""" # 清空列表 for item in self.tree.get_children(): self.tree.delete(item) # 获取所有成功完成的操作 records = self.history.get_recent_records(limit=50) self.undoable_records = [] for record in records: # 检查是否有回滚数据 if record.rollback_data and record.status == OperationStatus.SUCCESS.value: # 检查是否是可撤销的操作类型 if record.operation_type in [ OperationType.CREATE_PARTITION.value, OperationType.DELETE_PARTITION.value, OperationType.WIPE_PARTITION_TABLE.value ]: self.undoable_records.append(record) # 格式化时间 try: from datetime import datetime dt = datetime.fromisoformat(record.timestamp) time_str = dt.strftime("%Y-%m-%d %H:%M:%S") except: time_str = record.timestamp values = ( time_str, record.operation_type, record.device_path, record.status ) self.tree.insert("", tk.END, values=values) def _on_select(self, event): """选择操作""" selection = self.tree.selection() if not selection: self.undo_btn.config(state=tk.DISABLED) return index = self.tree.index(selection[0]) if index < len(self.undoable_records): self.selected_record = self.undoable_records[index] self._show_details() self.undo_btn.config(state=tk.NORMAL) else: self.selected_record = None self.undo_btn.config(state=tk.DISABLED) def _show_details(self): """显示详情""" if not self.selected_record: return self.detail_text.config(state=tk.NORMAL) self.detail_text.delete(1.0, tk.END) record = self.selected_record text = f"操作ID: {record.id}\n" text += f"时间: {record.timestamp}\n" text += f"类型: {record.operation_type}\n" text += f"设备: {record.device_path}\n\n" text += "操作详情:\n" text += f"{record.details}\n\n" if record.rollback_data: text += "可撤销数据:\n" if 'partition_table_backup' in record.rollback_data: backup_file = record.rollback_data['partition_table_backup'] text += f" 分区表备份: {backup_file}\n" if backup_file: import os if os.path.exists(backup_file): text += " 状态: 备份文件存在,可以恢复\n" else: text += " 状态: 备份文件不存在!无法恢复\n" self.undo_btn.config(state=tk.DISABLED) else: text += f"{record.rollback_data}\n" self.detail_text.insert(1.0, text) self.detail_text.config(state=tk.DISABLED) def _undo_selected(self): """撤销选中的操作""" if not self.selected_record: return record = self.selected_record # 确认对话框 result = messagebox.askyesno( "确认撤销", f"确定要撤销以下操作吗?\n\n" f"操作类型: {record.operation_type}\n" f"设备: {record.device_path}\n" f"时间: {record.timestamp}\n\n" f"警告:这将恢复分区表到操作前的状态!\n" f"此操作可能导致数据丢失!", icon=messagebox.WARNING ) if not result: return # 检查是否有备份 if not record.rollback_data or 'partition_table_backup' not in record.rollback_data: messagebox.showerror("错误", "没有找到可恢复的备份数据") return backup_file = record.rollback_data['partition_table_backup'] if not backup_file: messagebox.showerror("错误", "备份文件路径为空") return import os if not os.path.exists(backup_file): messagebox.showerror("错误", f"备份文件不存在:\n{backup_file}") return # 执行恢复 device_path = record.device_path # 对于分区操作,需要获取父磁盘 if record.operation_type == OperationType.DELETE_PARTITION.value: import re match = re.match(r'(/dev/[a-z]+)\d+', device_path) if match: device_path = match.group(1) if self.partition_backup.restore_partition_table(device_path, backup_file): # 更新操作状态 self.history.update_status( record.id, OperationStatus.ROLLED_BACK ) messagebox.showinfo("成功", f"操作已成功撤销!\n分区表已恢复到 {device_path}") self.result = True self.dialog.destroy() else: messagebox.showerror("错误", "撤销操作失败!\n请检查日志获取详细信息。") def wait_for_result(self): self.dialog.wait_window() return self.result