251 lines
9.3 KiB
Python
251 lines
9.3 KiB
Python
# 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("<<TreeviewSelect>>", 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
|