Files
Py-Backup/pyimage_ui/backup_content_frame.py

221 lines
9.1 KiB
Python

import tkinter as tk
from tkinter import ttk
import os
from shared_libs.animated_icon import AnimatedIcon
from pbp_app_config import Msg
from pyimage_ui.system_backup_content_frame import SystemBackupContentFrame
from pyimage_ui.user_backup_content_frame import UserBackupContentFrame
from shared_libs.logger import app_logger
from shared_libs.message import MessageDialog
class BackupContentFrame(ttk.Frame):
def __init__(self, master, backup_manager, actions, app, **kwargs):
super().__init__(master, **kwargs)
app_logger.log("BackupContentFrame: __init__ called")
self.backup_manager = backup_manager
self.actions = actions
self.app = app
self.base_backup_path = None
self.current_view_index = 0
self.viewing_encrypted = False
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(0, weight=1)
header_frame = ttk.Frame(self)
header_frame.grid(row=0, column=0, sticky=tk.NSEW)
top_nav_frame = ttk.Frame(header_frame)
top_nav_frame.pack(side=tk.LEFT)
self.nav_buttons_defs = [
(Msg.STR["system_backup_info"], lambda: self._switch_view(0)),
(Msg.STR["user_backup_info"], lambda: self._switch_view(1)),
]
self.nav_buttons = []
self.nav_progress_bars = []
for i, (text, command) in enumerate(self.nav_buttons_defs):
button_frame = ttk.Frame(top_nav_frame)
button_frame.pack(side=tk.LEFT, padx=5)
button = ttk.Button(button_frame, text=text,
command=command, style="TButton.Borderless.Round")
button.pack(side=tk.TOP)
self.nav_buttons.append(button)
progress_bar = ttk.Progressbar(
button_frame, orient="horizontal", length=50, mode="determinate", style="Small.Horizontal.TProgressbar")
progress_bar.pack_forget()
self.nav_progress_bars.append(progress_bar)
if i < len(self.nav_buttons_defs) - 1:
ttk.Separator(top_nav_frame, orient=tk.VERTICAL).pack(
side=tk.LEFT, fill=tk.Y, padx=2)
# Deletion Status UI
self.deletion_status_frame = ttk.Frame(header_frame)
self.deletion_status_frame.pack(side=tk.LEFT, padx=15)
bg_color = self.winfo_toplevel().style.lookup('TFrame', 'background')
self.deletion_animated_icon = AnimatedIcon(
self.deletion_status_frame, width=20, height=20, use_pillow=True, bg=bg_color, animation_type="blink")
self.deletion_animated_icon.pack(side=tk.LEFT, padx=5)
self.deletion_animated_icon.stop("DISABLE")
self.deletion_status_label = ttk.Label(
self.deletion_status_frame, text="", font=("Ubuntu", 10, "bold"))
self.deletion_status_label.pack(side=tk.LEFT, padx=5)
content_container = ttk.Frame(self)
content_container.grid(row=1, column=0, sticky="nsew")
content_container.grid_rowconfigure(0, weight=1)
content_container.grid_columnconfigure(0, weight=1)
self.system_backups_frame = SystemBackupContentFrame(
content_container, backup_manager, actions, parent_view=self)
self.user_backups_frame = UserBackupContentFrame(
content_container, backup_manager, actions, parent_view=self)
self.system_backups_frame.grid(row=0, column=0, sticky=tk.NSEW)
self.user_backups_frame.grid(row=0, column=0, sticky=tk.NSEW)
action_button_frame = ttk.Frame(self, padding=10)
action_button_frame.grid(row=2, column=0, sticky="ew")
self.toggle_encrypted_button = ttk.Button(
action_button_frame, text=Msg.STR["show_encrypted_backups"], command=self._toggle_encrypted_view)
self.toggle_encrypted_button.pack(side=tk.LEFT, padx=5)
self.restore_button = ttk.Button(
action_button_frame, text=Msg.STR["restore"], command=self._restore_selected, state="disabled")
self.restore_button.pack(side=tk.LEFT, padx=5)
self.delete_button = ttk.Button(
action_button_frame, text=Msg.STR["delete"], command=self._delete_selected, state="disabled")
self.delete_button.pack(side=tk.LEFT, padx=5)
self.edit_comment_button = ttk.Button(
action_button_frame, text=Msg.STR["comment"], command=self._edit_comment, state="disabled")
self.edit_comment_button.pack(side=tk.LEFT, padx=5)
self._switch_view(0)
def update_button_state(self, is_selected):
self.restore_button.config(
state="normal" if is_selected else "disabled")
self.delete_button.config(
state="normal" if is_selected else "disabled")
self.edit_comment_button.config(
state="normal" if is_selected else "disabled")
def _get_active_subframe(self):
return self.system_backups_frame if self.current_view_index == 0 else self.user_backups_frame
def _restore_selected(self):
self._get_active_subframe()._restore_selected()
def _delete_selected(self):
self._get_active_subframe()._delete_selected()
def _edit_comment(self):
self._get_active_subframe()._edit_comment()
def _toggle_encrypted_view(self):
if not self.app.destination_path:
MessageDialog(master=self.app, message_type="info",
title=Msg.STR["info_menu"], text=Msg.STR["err_no_dest_folder"])
return
if not self.viewing_encrypted:
username = os.path.basename(self.app.destination_path.rstrip('/'))
password = self.app.backup_manager.encryption_manager.get_password(
username, confirm=False)
if not password:
return
mount_point = self.app.backup_manager.encryption_manager.unlock_container(
self.app.destination_path, password)
if mount_point:
self.viewing_encrypted = True
self.toggle_encrypted_button.config(
text=Msg.STR["show_normal_backups"])
self.show(mount_point)
self.app.header_frame.refresh_status()
else:
MessageDialog(master=self.app, message_type="error",
title=Msg.STR["error"], text=Msg.STR["err_unlock_failed"])
else:
self.app.backup_manager.encryption_manager.lock_container(
self.app.destination_path)
self.viewing_encrypted = False
self.toggle_encrypted_button.config(
text=Msg.STR["show_encrypted_backups"])
self.show(self.app.destination_path)
self.app.header_frame.refresh_status()
def _switch_view(self, index):
self.current_view_index = index
config_key = "last_encrypted_backup_content_view" if self.viewing_encrypted else "last_backup_content_view"
self.app.config_manager.set_setting(config_key, index)
self.update_nav_buttons(index)
if index == 0:
self.system_backups_frame.grid()
self.user_backups_frame.grid_remove()
else:
self.user_backups_frame.grid()
self.system_backups_frame.grid_remove()
self.update_button_state(False)
def update_nav_buttons(self, active_index):
for i, button in enumerate(self.nav_buttons):
if i == active_index:
button.configure(style="Toolbutton")
self.nav_progress_bars[i].pack(side=tk.BOTTOM, fill=tk.X)
self.nav_progress_bars[i]['value'] = 100
else:
button.configure(style="Gray.Toolbutton")
self.nav_progress_bars[i].pack_forget()
def show(self, backup_path):
app_logger.log(
f"BackupContentFrame: show called with path {backup_path}")
self.grid(row=2, column=0, sticky="nsew")
self.base_backup_path = backup_path
if self.viewing_encrypted:
actual_backup_path = backup_path
else:
actual_backup_path = os.path.join(backup_path, "pybackup")
if not os.path.isdir(actual_backup_path):
app_logger.log(
f"Backup path {actual_backup_path} does not exist or is not a directory.")
# Clear views if path is invalid
self.system_backups_frame.show(None)
self.user_backups_frame.show(None)
return
self.system_backups_frame.show(actual_backup_path)
self.user_backups_frame.show(actual_backup_path)
config_key = "last_encrypted_backup_content_view" if self.viewing_encrypted else "last_backup_content_view"
last_view = self.app.config_manager.get_setting(config_key, 0)
self._switch_view(last_view)
def hide(self):
self.grid_remove()
def show_deletion_status(self, text: str):
app_logger.log(f"Showing deletion status: {text}")
self.deletion_status_label.config(text=text)
self.deletion_animated_icon.start()
self.deletion_status_frame.pack(side=tk.LEFT, padx=15)
def hide_deletion_status(self):
app_logger.log("Hiding deletion status.")
self.deletion_animated_icon.stop("DISABLE")
self.deletion_status_frame.pack_forget()