add new button for refresh log disable

This commit is contained in:
2025-09-10 14:38:24 +02:00
parent 444650f9f0
commit 9fd032e9b4
13 changed files with 150 additions and 95 deletions

View File

@@ -1,11 +1,8 @@
# pyimage/core/data_processing.py
import os
import fnmatch
import shutil
import re
import subprocess
from queue import Empty
from core.pbp_app_config import AppConfig, Msg
from core.pbp_app_config import AppConfig
from shared_libs.logger import app_logger
@@ -114,4 +111,4 @@ class DataProcessing:
pass
if not stop_event.is_set():
self.app.queue.put((button_text, total_size, mode))
self.app.queue.put((button_text, total_size, mode))

View File

@@ -5,11 +5,8 @@ from keyring.backends import SecretService
import os
import shutil
import subprocess
import tempfile
import stat
import re
import math
from typing import Optional, List, Tuple
from typing import Optional, Tuple
from core.pbp_app_config import AppConfig
from pyimage_ui.password_dialog import PasswordDialog
@@ -50,7 +47,8 @@ class EncryptionManager:
def remove_from_lock_file(self, base_path):
locks = self._read_lock_file()
updated_locks = [lock for lock in locks if lock['base_path'] != base_path]
updated_locks = [
lock for lock in locks if lock['base_path'] != base_path]
self._write_lock_file(updated_locks)
def get_password_from_keyring(self, username: str) -> Optional[str]:
@@ -123,10 +121,12 @@ class EncryptionManager:
def _get_password_or_key_cmd(self, base_dest_path: str, username: str) -> Tuple[str, Optional[str]]:
# 1. Check cache and keyring (without triggering dialog)
password = self.password_cache.get(username) or self.get_password_from_keyring(username)
password = self.password_cache.get(
username) or self.get_password_from_keyring(username)
if password:
self.logger.log("Using password from cache or keyring for LUKS operation.")
self.password_cache[username] = password # ensure it's cached
self.logger.log(
"Using password from cache or keyring for LUKS operation.")
self.password_cache[username] = password # ensure it's cached
return "-", password
# 2. Check for key file
@@ -137,8 +137,10 @@ class EncryptionManager:
return f'--key-file "{key_file_path}"'
# 3. If nothing found, prompt for password
self.logger.log("No password in keyring and no keyfile found. Prompting user.")
password = self.get_password(username, confirm=False) # This will now definitely open the dialog
self.logger.log(
"No password in keyring and no keyfile found. Prompting user.")
# This will now definitely open the dialog
password = self.get_password(username, confirm=False)
if not password:
return "", None
return "-", password

View File

@@ -255,7 +255,7 @@ class Msg:
"log": _("Log"),
"full_backup": _("Full backup"),
"incremental": _("Incremental"),
"incremental_backup": _("Incremental backup"), # New
"incremental_backup": _("Incremental backup"), # New
"test_run": _("Test run"),
"start": _("Start"),
"cancel_backup": _("Cancel"),
@@ -346,10 +346,11 @@ class Msg:
"header_subtitle": _("Simple GUI for rsync"),
"encrypted_backup_content": _("Encrypted Backups"),
"compressed": _("Compressed"),
"compression": _("Compression"), # New
"compression": _("Compression"), # New
"encrypted": _("Encrypted"),
"encryption": _("Encryption"), # New
"encryption": _("Encryption"), # New
"bypass_security": _("Bypass security"),
"refresh_log": _("Refresh log"),
"comment": _("Kommentar"),
"force_full_backup": _("Always force full backup"),
"force_incremental_backup": _("Always force incremental backup (except first)"),
@@ -367,5 +368,5 @@ class Msg:
"automation_settings_title": _("Automation Settings"), # New
"create_add_key_file": _("Create/Add Key File"), # New
"key_file_not_created": _("Key file not created."), # New
"backup_options": _("Backup Options"), # New
"backup_options": _("Backup Options"), # New
}

View File

@@ -104,6 +104,7 @@ class MainApplication(tk.Tk):
self.compressed_var = tk.BooleanVar()
self.encrypted_var = tk.BooleanVar()
self.bypass_security_var = tk.BooleanVar()
self.refresh_log_var = tk.BooleanVar(value=True)
self.mode = "backup" # Default mode
self.backup_is_running = False
@@ -177,6 +178,9 @@ class MainApplication(tk.Tk):
self.bypass_security_cb = ttk.Checkbutton(self.sidebar_buttons_frame, text=Msg.STR["bypass_security"],
variable=self.bypass_security_var, style="Switch2.TCheckbutton")
self.bypass_security_cb.pack(fill=tk.X, pady=10)
self.refresh_log_cb = ttk.Checkbutton(self.sidebar_buttons_frame, text=Msg.STR["refresh_log"],
variable=self.refresh_log_var, style="Switch2.TCheckbutton")
self.refresh_log_cb.pack(fill=tk.X, pady=10)
self.header_frame = HeaderFrame(
self.content_frame, self.image_manager, self.backup_manager.encryption_manager, self)
@@ -333,6 +337,8 @@ class MainApplication(tk.Tk):
def _load_state_and_initialize(self):
# self.log_window.clear_log()
last_mode = self.config_manager.get_setting("last_mode", "backup")
refresh_log = self.config_manager.get_setting("refresh_log", True)
self.refresh_log_var.set(refresh_log)
backup_source_path = self.config_manager.get_setting(
"backup_source_path")
@@ -529,6 +535,7 @@ class MainApplication(tk.Tk):
self.start_pause_button.grid(row=0, column=2, rowspan=2, padx=5)
def on_closing(self):
self.config_manager.set_setting("refresh_log", self.refresh_log_var.get())
self.backup_manager.encryption_manager.unmount_all()
self.config_manager.set_setting("last_mode", self.mode)

View File

@@ -10,23 +10,35 @@ from shared_libs.logger import app_logger
from core.pbp_app_config import AppConfig
# A simple logger for the CLI that just prints to the console
class CliLogger:
def log(self, message):
print(f"[CLI] {message}")
def init_logger(self, log_method):
pass # Not needed for CLI
pass # Not needed for CLI
def main():
parser = argparse.ArgumentParser(description="Py-Backup Command-Line Interface.")
parser.add_argument("--backup-type", choices=['user', 'system'], required=True, help="Type of backup to perform.")
parser.add_argument("--destination", required=True, help="Destination directory for the backup.")
parser.add_argument("--source", help="Source directory for user backup. Required for --backup-type user.")
parser.add_argument("--mode", choices=['full', 'incremental'], default='incremental', help="Mode for system backup.")
parser.add_argument("--encrypted", action='store_true', help="Flag to indicate the backup should be encrypted.")
parser.add_argument("--key-file", help="Path to the key file for unlocking an encrypted container.")
parser.add_argument("--password", help="Password for the encrypted container (use with caution). If --key-file is not provided, this will be used.")
parser.add_argument("--compressed", action='store_true', help="Flag to indicate the backup should be compressed.")
parser = argparse.ArgumentParser(
description="Py-Backup Command-Line Interface.")
parser.add_argument(
"--backup-type", choices=['user', 'system'], required=True, help="Type of backup to perform.")
parser.add_argument("--destination", required=True,
help="Destination directory for the backup.")
parser.add_argument(
"--source", help="Source directory for user backup. Required for --backup-type user.")
parser.add_argument("--mode", choices=['full', 'incremental'],
default='incremental', help="Mode for system backup.")
parser.add_argument("--encrypted", action='store_true',
help="Flag to indicate the backup should be encrypted.")
parser.add_argument(
"--key-file", help="Path to the key file for unlocking an encrypted container.")
parser.add_argument(
"--password", help="Password for the encrypted container (use with caution). If --key-file is not provided, this will be used.")
parser.add_argument("--compressed", action='store_true',
help="Flag to indicate the backup should be compressed.")
args = parser.parse_args()
@@ -34,17 +46,19 @@ def main():
parser.error("--source is required for --backup-type 'user'.")
if args.encrypted and not (args.key_file or args.password):
parser.error("For encrypted backups, either --key-file or --password must be provided.")
parser.error(
"For encrypted backups, either --key-file or --password must be provided.")
cli_logger = CliLogger()
backup_manager = BackupManager(cli_logger)
queue = Queue() # Dummy queue for now, might be used for progress later
queue = Queue() # Dummy queue for now, might be used for progress later
source_path = "/" # Default for system backup
source_path = "/" # Default for system backup
if args.backup_type == 'user':
source_path = args.source
if not os.path.isdir(source_path):
cli_logger.log(f"Error: Source path '{source_path}' does not exist or is not a directory.")
cli_logger.log(
f"Error: Source path '{source_path}' does not exist or is not a directory.")
sys.exit(1)
# Determine password or key_file to pass
@@ -54,7 +68,8 @@ def main():
if args.key_file:
auth_key_file = args.key_file
if not os.path.exists(auth_key_file):
cli_logger.log(f"Error: Key file '{auth_key_file}' does not exist.")
cli_logger.log(
f"Error: Key file '{auth_key_file}' does not exist.")
sys.exit(1)
elif args.password:
auth_password = args.password
@@ -78,8 +93,8 @@ def main():
dest_path=args.destination,
is_system=(args.backup_type == 'system'),
is_dry_run=False,
exclude_files=None, # Excludes are handled by AppConfig.MANUAL_EXCLUDE_LIST_PATH
source_size=0, # Not accurately calculable in CLI without scanning, set to 0
exclude_files=None, # Excludes are handled by AppConfig.MANUAL_EXCLUDE_LIST_PATH
source_size=0, # Not accurately calculable in CLI without scanning, set to 0
is_compressed=args.compressed,
is_encrypted=args.encrypted,
mode=args.mode,
@@ -92,5 +107,6 @@ def main():
cli_logger.log("CLI backup process finished.")
if __name__ == "__main__":
main()
main()

View File

@@ -157,12 +157,13 @@ class Actions:
is_encrypted = self.app.encrypted_var.get()
exclude_files = []
if AppConfig.GENERATED_EXCLUDE_LIST_PATH.exists():
exclude_files.append(AppConfig.GENERATED_EXCLUDE_LIST_PATH)
if AppConfig.USER_EXCLUDE_LIST_PATH.exists():
exclude_files.append(AppConfig.USER_EXCLUDE_LIST_PATH)
if AppConfig.MANUAL_EXCLUDE_LIST_PATH.exists():
exclude_files.append(AppConfig.MANUAL_EXCLUDE_LIST_PATH)
if is_system:
if AppConfig.GENERATED_EXCLUDE_LIST_PATH.exists():
exclude_files.append(AppConfig.GENERATED_EXCLUDE_LIST_PATH)
if AppConfig.USER_EXCLUDE_LIST_PATH.exists():
exclude_files.append(AppConfig.USER_EXCLUDE_LIST_PATH)
if AppConfig.MANUAL_EXCLUDE_LIST_PATH.exists():
exclude_files.append(AppConfig.MANUAL_EXCLUDE_LIST_PATH)
size = self.app.backup_manager.estimate_incremental_size(
source_path=folder_path,

View File

@@ -6,7 +6,6 @@ from core.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):
@@ -149,7 +148,7 @@ class BackupContentFrame(ttk.Frame):
# Check if the destination is encrypted and trigger mount if necessary
is_encrypted = self.backup_manager.encryption_manager.is_encrypted(
backup_path)
self.viewing_encrypted = is_encrypted # Set this flag for remembering the view
self.viewing_encrypted = is_encrypted # Set this flag for remembering the view
pybackup_dir = os.path.join(backup_path, "pybackup")
@@ -161,7 +160,8 @@ class BackupContentFrame(ttk.Frame):
self.user_backups_frame.show(backup_path, [])
return
all_backups = self.backup_manager.list_all_backups(backup_path, mount_if_needed=True)
all_backups = self.backup_manager.list_all_backups(
backup_path, mount_if_needed=True)
if all_backups:
system_backups, user_backups = all_backups
self.system_backups_frame.show(backup_path, system_backups)

View File

@@ -1,6 +1,7 @@
import tkinter as tk
from tkinter import ttk
class CommentEditorDialog(tk.Toplevel):
def __init__(self, master, info_file_path, backup_manager):
super().__init__(master)
@@ -13,17 +14,20 @@ class CommentEditorDialog(tk.Toplevel):
main_frame = ttk.Frame(self, padding=10)
main_frame.pack(fill=tk.BOTH, expand=True)
self.text_widget = tk.Text(main_frame, wrap="word", height=10, width=40)
self.text_widget = tk.Text(
main_frame, wrap="word", height=10, width=40)
self.text_widget.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
button_frame = ttk.Frame(main_frame)
button_frame.pack(fill=tk.X)
ttk.Button(button_frame, text="Speichern & Schließen", command=self._save_and_close).pack(side=tk.RIGHT)
ttk.Button(button_frame, text="Abbrechen", command=self.destroy).pack(side=tk.RIGHT, padx=5)
ttk.Button(button_frame, text="Speichern & Schließen",
command=self._save_and_close).pack(side=tk.RIGHT)
ttk.Button(button_frame, text="Abbrechen",
command=self.destroy).pack(side=tk.RIGHT, padx=5)
self._load_comment()
self.transient(master)
self.grab_set()
self.wait_window(self)

View File

@@ -1,6 +1,5 @@
import tkinter as tk
from tkinter import ttk
from shared_libs.message import MessageDialog
import keyring
@@ -20,9 +19,10 @@ class EncryptionFrame(ttk.Frame):
self.keyring_status_label = ttk.Label(self, text="")
self.keyring_status_label.grid(
row=1, column=0, sticky="ew", padx=10, pady=5)
self.keyring_usage_label = ttk.Label(self, text="")
self.keyring_usage_label.grid(row=4, column=0, sticky="ew", padx=10, pady=5)
self.keyring_usage_label.grid(
row=4, column=0, sticky="ew", padx=10, pady=5)
self.check_keyring_availability()
@@ -51,7 +51,8 @@ class EncryptionFrame(ttk.Frame):
clear_password_button.grid(row=2, column=1, padx=5, pady=5, sticky="w")
self.status_message_label = ttk.Label(self, text="", foreground="blue")
self.status_message_label.grid(row=3, column=0, sticky="ew", padx=10, pady=5)
self.status_message_label.grid(
row=3, column=0, sticky="ew", padx=10, pady=5)
def set_context(self, username):
self.username = username
@@ -75,39 +76,47 @@ class EncryptionFrame(ttk.Frame):
def set_session_password(self):
password = self.password_entry.get()
if not password:
self.status_message_label.config(text="Password cannot be empty.", foreground="red")
self.status_message_label.config(
text="Password cannot be empty.", foreground="red")
return
self.encryption_manager.set_session_password(password, self.save_to_keyring_var.get())
self.encryption_manager.set_session_password(
password, self.save_to_keyring_var.get())
if self.save_to_keyring_var.get():
if not self.username:
self.status_message_label.config(text="Please select a backup destination first.", foreground="orange")
self.status_message_label.config(
text="Please select a backup destination first.", foreground="orange")
return
if self.encryption_manager.set_password_in_keyring(self.username, password):
self.status_message_label.config(text="Password set for this session and saved to keyring.", foreground="green")
self.status_message_label.config(
text="Password set for this session and saved to keyring.", foreground="green")
self.update_keyring_status()
else:
self.status_message_label.config(text="Password set for this session, but failed to save to keyring.", foreground="orange")
self.status_message_label.config(
text="Password set for this session, but failed to save to keyring.", foreground="orange")
else:
self.status_message_label.config(text="Password set for this session.", foreground="green")
self.status_message_label.config(
text="Password set for this session.", foreground="green")
def clear_session_password(self):
self.encryption_manager.clear_session_password()
self.password_entry.delete(0, tk.END)
self.status_message_label.config(text="Session password cleared.", foreground="green")
self.status_message_label.config(
text="Session password cleared.", foreground="green")
if self.username:
self.encryption_manager.delete_password_from_keyring(self.username)
self.update_keyring_status()
def update_keyring_status(self):
if not self.username:
self.keyring_usage_label.config(text="Select a backup destination to see keyring status.", foreground="blue")
self.keyring_usage_label.config(
text="Select a backup destination to see keyring status.", foreground="blue")
return
if self.encryption_manager.get_password_from_keyring(self.username):
self.keyring_usage_label.config(text=f'Password for "{self.username}" is stored in the keyring.', foreground="green")
self.keyring_usage_label.config(
text=f'Password for "{self.username}" is stored in the keyring.', foreground="green")
else:
self.keyring_usage_label.config(text=f'No password for "{self.username}" found in the keyring.', foreground="orange")
self.keyring_usage_label.config(
text=f'No password for "{self.username}" found in the keyring.', foreground="orange")

View File

@@ -2,9 +2,9 @@ import tkinter as tk
import os
from core.pbp_app_config import Msg
from shared_libs.common_tools import IconManager
from shared_libs.logger import app_logger
class HeaderFrame(tk.Frame):
def __init__(self, container, image_manager, encryption_manager, app, **kwargs):
super().__init__(container, bg="#455A64", **kwargs)
@@ -63,8 +63,9 @@ class HeaderFrame(tk.Frame):
font=("Helvetica", 10, "bold"),
bg="#455A64",
)
self.keyring_status_label.grid(row=0, column=0, sticky="ne", padx=(10, 10), pady=(10, 0))
self.keyring_status_label.grid(
row=0, column=0, sticky="ne", padx=(10, 10), pady=(10, 0))
self.refresh_status()
def refresh_status(self):
@@ -74,10 +75,12 @@ class HeaderFrame(tk.Frame):
app_logger.log(f"HeaderFrame: Destination path is '{dest_path}'")
if not dest_path or not self.encryption_manager.is_encrypted(dest_path):
app_logger.log("HeaderFrame: No destination path or not encrypted. Clearing status.")
self.keyring_status_label.config(text="") # Clear status if not encrypted
app_logger.log(
"HeaderFrame: No destination path or not encrypted. Clearing status.")
# Clear status if not encrypted
self.keyring_status_label.config(text="")
return
app_logger.log("HeaderFrame: Destination is encrypted.")
username = os.path.basename(dest_path.rstrip('/'))
app_logger.log(f"HeaderFrame: Username is '{username}'")
@@ -97,9 +100,11 @@ class HeaderFrame(tk.Frame):
fg="#2E8B57" # SeaGreen
)
else:
key_in_keyring = self.encryption_manager.is_key_in_keyring(username)
key_in_keyring = self.encryption_manager.is_key_in_keyring(
username)
app_logger.log(f"HeaderFrame: Key in keyring? {key_in_keyring}")
key_file_exists = os.path.exists(self.encryption_manager.get_key_file_path(dest_path))
key_file_exists = os.path.exists(
self.encryption_manager.get_key_file_path(dest_path))
app_logger.log(f"HeaderFrame: Key file exists? {key_file_exists}")
if key_in_keyring:
@@ -117,4 +122,4 @@ class HeaderFrame(tk.Frame):
text="Key: Not Available",
fg="#A9A9A9" # DarkGray
)
app_logger.log("HeaderFrame: Status refresh complete.")
app_logger.log("HeaderFrame: Status refresh complete.")

View File

@@ -133,6 +133,8 @@ class Navigation:
self.app.drawing.redraw_right_canvas()
def toggle_mode(self, mode=None, active_index=None, trigger_calculation=True):
if self.app.refresh_log_var.get():
self.app.log_window.clear_log()
if self.app.backup_is_running:
# If a backup is running, we only want to switch the view to the main backup screen.
# We don't reset anything.
@@ -140,7 +142,6 @@ class Navigation:
self.app.scheduler_frame.hide()
self.app.settings_frame.hide()
self.app.backup_content_frame.grid_remove()
# Show the main content frames
self.app.canvas_frame.grid()
@@ -185,7 +186,7 @@ class Navigation:
self.app.scheduler_frame.hide()
self.app.settings_frame.hide()
self.app.backup_content_frame.grid_remove()
self.app.canvas_frame.grid()
self.app.source_size_frame.grid()
self.app.target_size_frame.grid()
@@ -232,6 +233,8 @@ class Navigation:
self._update_task_bar_visibility("log")
def toggle_scheduler_frame(self, active_index=None):
if self.app.refresh_log_var.get():
self.app.log_window.clear_log()
self._cancel_calculation()
if active_index is not None:
self.app.drawing.update_nav_buttons(active_index)
@@ -240,7 +243,7 @@ class Navigation:
self.app.log_frame.grid_remove()
self.app.settings_frame.hide()
self.app.backup_content_frame.grid_remove()
self.app.source_size_frame.grid_remove()
self.app.target_size_frame.grid_remove()
self.app.restore_size_frame_before.grid_remove()
@@ -250,6 +253,8 @@ class Navigation:
self._update_task_bar_visibility("scheduler")
def toggle_settings_frame(self, active_index=None):
if self.app.refresh_log_var.get():
self.app.log_window.clear_log()
self._cancel_calculation()
if active_index is not None:
self.app.drawing.update_nav_buttons(active_index)
@@ -258,7 +263,7 @@ class Navigation:
self.app.log_frame.grid_remove()
self.app.backup_content_frame.grid_remove()
self.app.scheduler_frame.hide()
self.app.source_size_frame.grid_remove()
self.app.target_size_frame.grid_remove()
self.app.restore_size_frame_before.grid_remove()
@@ -267,7 +272,9 @@ class Navigation:
self.app.top_bar.grid()
self._update_task_bar_visibility("settings")
def toggle_backup_content_frame(self, _=None): # Accept argument but ignore it
def toggle_backup_content_frame(self, _=None): # Accept argument but ignore it
if self.app.refresh_log_var.get():
self.app.log_window.clear_log()
self._cancel_calculation()
self.app.drawing.update_nav_buttons(2) # Index 2 for Backup Content
@@ -283,7 +290,7 @@ class Navigation:
self.app.log_frame.grid_remove()
self.app.scheduler_frame.hide()
self.app.settings_frame.hide()
self.app.source_size_frame.grid_remove()
self.app.target_size_frame.grid_remove()
self.app.restore_size_frame_before.grid_remove()
@@ -301,4 +308,4 @@ class Navigation:
self.app.next_backup_content_view = 'system'
self.app.top_bar.grid()
self._update_task_bar_visibility("scheduler")
self._update_task_bar_visibility("scheduler")

View File

@@ -18,7 +18,8 @@ class SchedulerFrame(ttk.Frame):
self, text=Msg.STR["scheduled_jobs"], padding=10)
self.jobs_frame.pack(fill=tk.BOTH, expand=True)
columns = ("active", "type", "frequency", "destination", "sources", "options")
columns = ("active", "type", "frequency",
"destination", "sources", "options")
self.jobs_tree = ttk.Treeview(
self.jobs_frame, columns=columns, show="headings")
self.jobs_tree.heading("active", text=Msg.STR["active"])
@@ -55,7 +56,7 @@ class SchedulerFrame(ttk.Frame):
self.backup_type_system_var = tk.BooleanVar(value=True)
self.backup_type_user_var = tk.BooleanVar(value=False)
self.freq_daily_var = tk.BooleanVar(value=True)
self.freq_weekly_var = tk.BooleanVar(value=False)
self.freq_monthly_var = tk.BooleanVar(value=False)
@@ -76,7 +77,8 @@ class SchedulerFrame(ttk.Frame):
self.user_sources_frame = ttk.LabelFrame(
source_options_container, text=Msg.STR["source_folders"], padding=10)
self.user_sources_frame.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
self.user_sources_frame.grid(
row=0, column=0, sticky="nsew", padx=5, pady=5)
for name, var in self.user_sources.items():
ttk.Checkbutton(self.user_sources_frame, text=name,
variable=var, style="Switch.TCheckbutton").pack(anchor=tk.W)
@@ -90,13 +92,17 @@ class SchedulerFrame(ttk.Frame):
self.compress_var = tk.BooleanVar(value=False)
self.encrypt_var = tk.BooleanVar(value=False)
self.full_checkbutton = ttk.Checkbutton(options_frame, text=Msg.STR["full_backup"], variable=self.full_var, command=lambda: enforce_backup_type_exclusivity(self.full_var, self.incremental_var, self.full_var.get()), style="Switch.TCheckbutton")
self.full_checkbutton = ttk.Checkbutton(options_frame, text=Msg.STR["full_backup"], variable=self.full_var, command=lambda: enforce_backup_type_exclusivity(
self.full_var, self.incremental_var, self.full_var.get()), style="Switch.TCheckbutton")
self.full_checkbutton.pack(anchor=tk.W)
self.incremental_checkbutton = ttk.Checkbutton(options_frame, text=Msg.STR["incremental_backup"], variable=self.incremental_var, command=lambda: enforce_backup_type_exclusivity(self.incremental_var, self.full_var, self.incremental_var.get()), style="Switch.TCheckbutton")
self.incremental_checkbutton = ttk.Checkbutton(options_frame, text=Msg.STR["incremental_backup"], variable=self.incremental_var, command=lambda: enforce_backup_type_exclusivity(
self.incremental_var, self.full_var, self.incremental_var.get()), style="Switch.TCheckbutton")
self.incremental_checkbutton.pack(anchor=tk.W)
self.compress_checkbutton = ttk.Checkbutton(options_frame, text=Msg.STR["compression"], variable=self.compress_var, command=self._on_compression_toggle_scheduler, style="Switch.TCheckbutton")
self.compress_checkbutton = ttk.Checkbutton(
options_frame, text=Msg.STR["compression"], variable=self.compress_var, command=self._on_compression_toggle_scheduler, style="Switch.TCheckbutton")
self.compress_checkbutton.pack(anchor=tk.W)
self.encrypt_checkbutton = ttk.Checkbutton(options_frame, text=Msg.STR["encryption"], variable=self.encrypt_var, style="Switch.TCheckbutton")
self.encrypt_checkbutton = ttk.Checkbutton(
options_frame, text=Msg.STR["encryption"], variable=self.encrypt_var, style="Switch.TCheckbutton")
self.encrypt_checkbutton.pack(anchor=tk.W)
dest_frame = ttk.LabelFrame(
@@ -147,7 +153,8 @@ class SchedulerFrame(ttk.Frame):
self._toggle_user_sources()
def _handle_freq_switch(self, changed_var):
vars = {"daily": self.freq_daily_var, "weekly": self.freq_weekly_var, "monthly": self.freq_monthly_var}
vars = {"daily": self.freq_daily_var,
"weekly": self.freq_weekly_var, "monthly": self.freq_monthly_var}
if vars[changed_var].get():
self.frequency.set(changed_var)
for var_name, var_obj in vars.items():
@@ -187,7 +194,7 @@ class SchedulerFrame(ttk.Frame):
self.destination.set("")
for var in self.user_sources.values():
var.set(False)
self._on_compression_toggle_scheduler() # Update state of incremental checkbox
self._on_compression_toggle_scheduler() # Update state of incremental checkbox
else:
self.add_job_frame.pack_forget()
self.jobs_frame.pack(fill=tk.BOTH, expand=True)
@@ -240,7 +247,7 @@ class SchedulerFrame(ttk.Frame):
if job_type == "user":
command += f" --sources "
for s in job_sources:
command += f'\"{s}\" '
command += f'\"{s}\" '
comment = f"{self.backup_manager.app_tag}; type:{job_type}; freq:{job_frequency}; dest:{dest}"
if job_type == "user":
@@ -289,4 +296,4 @@ class SchedulerFrame(ttk.Frame):
job_id = self.jobs_tree.item(selected_item)["values"][0]
self.backup_manager.remove_scheduled_job(job_id)
self._load_scheduled_jobs()
self._load_scheduled_jobs()

View File

@@ -1,7 +1,6 @@
import tkinter as tk
from tkinter import ttk
import os
import shutil
from core.pbp_app_config import Msg
from pyimage_ui.comment_editor_dialog import CommentEditorDialog