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:
@@ -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}")
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user