fix: Improve backup detection and UI updates

- Ensure backup content view updates after backup completion or deletion.
- Fix "Please wait" animation not stopping after backup deletion.
- Refactor backup type selection logic for more reliable UI updates.
- Correct backup detection after resetting settings.
- Ensure correct size is written to info file for all backup types.
This commit is contained in:
2025-09-01 19:10:15 +02:00
parent 058dc1e951
commit 16b8a92a24
5 changed files with 101 additions and 43 deletions

View File

@@ -299,7 +299,7 @@ set -e
if status in ['success', 'warning'] and not is_dry_run:
info_filename_base = os.path.basename(dest_path)
final_size = transferred_size if latest_backup_path else source_size
final_size = transferred_size
if is_compressed:
self.logger.log(f"Compression requested for {dest_path}")

View File

@@ -577,6 +577,7 @@ class MainApplication(tk.Tk):
elif message_type == 'deletion_complete':
self.backup_content_frame.hide_deletion_status()
self.backup_content_frame.system_backups_frame._load_backup_content()
self.backup_content_frame.user_backups_frame._load_backup_content()
elif message_type == 'error':
self.animated_icon.stop("DISABLE")
self.start_pause_button["text"] = "Start"
@@ -622,6 +623,7 @@ class MainApplication(tk.Tk):
self.backup_is_running = False
self.actions._set_ui_state(True)
self.backup_content_frame.system_backups_frame._load_backup_content()
elif message_type == 'completion_accurate':
if value == 'success':
self.info_label.config(

View File

@@ -273,6 +273,8 @@ class Msg:
"exclude_dialog_text": _("Do you want to add a folder or a file?"),
"add_folder_button": _("Folder"),
"add_file_button": _("File"),
"system_excludes": _("System Excludes"),
"manual_excludes": _("Manual Excludes"),
# Menus
"file_menu": _("File"),

View File

@@ -18,6 +18,20 @@ class Actions:
def __init__(self, app):
self.app = app
def _set_backup_type(self, backup_type: str):
if backup_type == "full":
self.app.vollbackup_var.set(True)
self.app.inkrementell_var.set(False)
self.app.full_backup_cb.config(state="disabled")
self.app.incremental_cb.config(state="disabled")
self.app.accurate_size_cb.config(state="disabled")
elif backup_type == "incremental":
self.app.vollbackup_var.set(False)
self.app.inkrementell_var.set(True)
self.app.full_backup_cb.config(state="normal")
self.app.incremental_cb.config(state="normal")
self.app.accurate_size_cb.config(state="normal")
def _update_backup_type_controls(self):
"""
Updates the state of the Full/Incremental backup radio buttons based on
@@ -46,43 +60,17 @@ class Actions:
break
if full_backup_exists:
# Case 1: A full backup exists. Allow user to choose, default to incremental.
if not self.app.vollbackup_var.get(): # If user has not manually selected full backup
self.app.vollbackup_var.set(False)
self.app.inkrementell_var.set(True)
self.app.full_backup_cb.config(state="normal")
self.app.incremental_cb.config(state="normal")
self.app.accurate_size_cb.config(
state="normal") # Enable accurate calc
self._set_backup_type("incremental")
else:
# Case 2: No full backup exists. Force a full backup.
self.app.vollbackup_var.set(True)
self.app.inkrementell_var.set(False)
self.app.full_backup_cb.config(state="disabled")
self.app.incremental_cb.config(state="disabled")
self.app.accurate_size_cb.config(
state="disabled") # Disable accurate calc
self._set_backup_type("full")
def handle_backup_type_change(self, changed_var_name):
# This function is called when the user clicks on "Full" or "Incremental".
# It enforces that only one can be selected and updates the accurate size checkbox accordingly.
if changed_var_name == 'voll':
if self.app.vollbackup_var.get(): # if "Full" was checked
self.app.inkrementell_var.set(False)
if self.app.vollbackup_var.get():
self._set_backup_type("full")
elif changed_var_name == 'inkrementell':
if self.app.inkrementell_var.get(): # if "Incremental" was checked
self.app.vollbackup_var.set(False)
# Now, update the state of the accurate calculation checkbox
if self.app.vollbackup_var.get():
self.app.accurate_size_cb.config(state="disabled")
self.app.genaue_berechnung_var.set(False)
else:
# Only enable if it's an incremental backup of the system
if self.app.left_canvas_data.get('folder') == "Computer":
self.app.accurate_size_cb.config(state="normal")
else:
self.app.accurate_size_cb.config(state="disabled")
if self.app.inkrementell_var.get():
self._set_backup_type("incremental")
def handle_compression_change(self):
if self.app.compressed_var.get():
@@ -349,6 +337,8 @@ class Actions:
def _update_right_canvas_info(self, path):
try:
if self.app.mode == "backup":
self.app.destination_path = path
backup_root_to_exclude = f"/{path.strip('/').split('/')[0]}"
try:
with open(AppConfig.USER_EXCLUDE_LIST_PATH, 'r+') as f:
@@ -364,13 +354,12 @@ class Actions:
app_logger.log(f"Error updating exclusion list: {e}")
total, used, free = shutil.disk_usage(path)
self.app.destination_path = path
self.app.destination_total_bytes = total
self.app.destination_used_bytes = used
size_str = f"{used / (1024**3):.2f} GB / {total / (1024**3):.2f} GB"
self.app.right_canvas_data.update({
'folder': os.path.basename(path.rstrip('/')),
'folder': os.path.basename(path.rstrip('/')),
'path_display': path,
'size': size_str
})
@@ -386,7 +375,7 @@ class Actions:
elif self.app.mode == "restore":
self.app.right_canvas_data.update({
'folder': os.path.basename(path.rstrip('/')),
'folder': os.path.basename(path.rstrip('/')),
'path_display': path,
'size': ''
})
@@ -437,6 +426,13 @@ class Actions:
self.app.restore_left_canvas_data.clear()
self.app.restore_right_canvas_data.clear()
current_source = self.app.left_canvas_data.get('folder')
if current_source:
self.on_sidebar_button_click(current_source)
self.app.backup_content_frame.system_backups_frame._load_backup_content()
self.app.backup_content_frame.user_backups_frame._load_backup_content()
with message_box_animation(self.app.animated_icon):
MessageDialog(master=self.app, message_type="info",
title=Msg.STR["settings_reset_title"], text=Msg.STR["settings_reset_text"])
@@ -469,7 +465,7 @@ class Actions:
except ValueError:
return 0
def _set_ui_state(self, enable: bool, keep_cancel_enabled: bool = False):
def _set_ui_state(self, enable: bool, keep_cancel_enabled: bool = False, allow_log_and_backup_toggle: bool = False):
# Sidebar Buttons
for text, data in self.app.buttons_map.items():
# Find the actual button widget in the sidebar_buttons_frame
@@ -492,6 +488,8 @@ class Actions:
# Top Navigation Buttons
for i, button in enumerate(self.app.nav_buttons):
if allow_log_and_backup_toggle and self.app.nav_buttons_defs[i][0] in [Msg.STR["log"], Msg.STR["backup_menu"]]:
continue
button.config(state="normal" if enable else "disabled")
# Right Canvas (Destination/Restore Source)
@@ -622,21 +620,22 @@ class Actions:
self.app.update_idletasks()
self.app.log_window.clear_log()
self._set_ui_state(False)
self._set_ui_state(False, allow_log_and_backup_toggle=True)
self.app.animated_icon.start()
self.app._process_backup_queue()
if self.app.mode == "backup":
source_size_bytes = self.app.source_size_bytes
if self.app.vollbackup_var.get():
self._start_system_backup("full")
self._start_system_backup("full", source_size_bytes)
else:
self._start_system_backup("incremental")
self._start_system_backup("incremental", source_size_bytes)
else:
pass
def _start_system_backup(self, mode):
def _start_system_backup(self, mode, source_size_bytes):
base_dest = self.app.destination_path
if not base_dest:
MessageDialog(master=self.app, message_type="error",

View File

@@ -21,10 +21,25 @@ class AdvancedSettingsFrame(tk.Toplevel):
self, text=Msg.STR["advanced_settings_warning"], wraplength=780, justify="center")
warning_label.pack(pady=10, fill=tk.X, padx=10)
# --- View Toggle Buttons ---
toggle_frame = ttk.Frame(self)
toggle_frame.pack(pady=5)
self.system_excludes_button = ttk.Button(
toggle_frame, text=Msg.STR["system_excludes"], command=lambda: self._toggle_views("system"))
self.system_excludes_button.pack(side=tk.LEFT, padx=5)
self.manual_excludes_button = ttk.Button(
toggle_frame, text=Msg.STR["manual_excludes"], command=lambda: self._toggle_views("manual"))
self.manual_excludes_button.pack(side=tk.LEFT, padx=5)
# --- Container for the two views ---
view_container = ttk.Frame(self)
view_container.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# --- Treeview for system folder exclusion ---
self.tree_frame = ttk.LabelFrame(
self, text=Msg.STR["exclude_system_folders"], padding=10)
self.tree_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
view_container, text=Msg.STR["exclude_system_folders"], padding=10)
columns = ("included", "name", "path")
self.tree = ttk.Treeview(
@@ -41,6 +56,16 @@ class AdvancedSettingsFrame(tk.Toplevel):
self.tree.bind("<Button-1>", self._toggle_include_status)
# --- Manual Excludes Frame ---
self.manual_excludes_frame = ttk.LabelFrame(
view_container, text=Msg.STR["manual_excludes"], padding=10)
self.manual_excludes_listbox = tk.Listbox(self.manual_excludes_frame, selectmode=tk.MULTIPLE)
self.manual_excludes_listbox.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
delete_button = ttk.Button(self.manual_excludes_frame, text=Msg.STR["delete"], command=self._delete_manual_exclude)
delete_button.pack(pady=5)
# --- Animation Settings ---
animation_frame = ttk.LabelFrame(
self, text=Msg.STR["animation_settings_title"], padding=10)
@@ -106,6 +131,31 @@ class AdvancedSettingsFrame(tk.Toplevel):
self._load_system_folders()
self._load_animation_settings()
self._load_backup_defaults()
self._load_manual_excludes()
self._toggle_views("system")
def _toggle_views(self, view):
if view == "system":
self.manual_excludes_frame.pack_forget()
self.tree_frame.pack(fill=tk.BOTH, expand=True)
else:
self.tree_frame.pack_forget()
self.manual_excludes_frame.pack(fill=tk.BOTH, expand=True)
def _load_manual_excludes(self):
self.manual_excludes_listbox.delete(0, tk.END)
if AppConfig.MANUAL_EXCLUDE_LIST_PATH.exists():
with open(AppConfig.MANUAL_EXCLUDE_LIST_PATH, 'r') as f:
for line in f:
line = line.strip()
if line:
self.manual_excludes_listbox.insert(tk.END, line)
def _delete_manual_exclude(self):
selected_indices = self.manual_excludes_listbox.curselection()
for i in reversed(selected_indices):
self.manual_excludes_listbox.delete(i)
def _reset_animation_settings(self):
self.config_manager.remove_setting("backup_animation_type")
@@ -285,6 +335,11 @@ class AdvancedSettingsFrame(tk.Toplevel):
for path in final_excludes:
f.write(f"{path}\n")
# Save manual excludes
with open(AppConfig.MANUAL_EXCLUDE_LIST_PATH, 'w') as f:
for item in self.manual_excludes_listbox.get(0, tk.END):
f.write(f"{item}\n")
self.destroy()
if self.app_instance: