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 # pyimage/core/data_processing.py
import os import os
import fnmatch import fnmatch
import shutil
import re import re
import subprocess from core.pbp_app_config import AppConfig
from queue import Empty
from core.pbp_app_config import AppConfig, Msg
from shared_libs.logger import app_logger from shared_libs.logger import app_logger
@@ -114,4 +111,4 @@ class DataProcessing:
pass pass
if not stop_event.is_set(): 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 os
import shutil import shutil
import subprocess import subprocess
import tempfile
import stat
import re
import math import math
from typing import Optional, List, Tuple from typing import Optional, Tuple
from core.pbp_app_config import AppConfig from core.pbp_app_config import AppConfig
from pyimage_ui.password_dialog import PasswordDialog from pyimage_ui.password_dialog import PasswordDialog
@@ -50,7 +47,8 @@ class EncryptionManager:
def remove_from_lock_file(self, base_path): def remove_from_lock_file(self, base_path):
locks = self._read_lock_file() 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) self._write_lock_file(updated_locks)
def get_password_from_keyring(self, username: str) -> Optional[str]: 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]]: 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) # 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: if password:
self.logger.log("Using password from cache or keyring for LUKS operation.") self.logger.log(
self.password_cache[username] = password # ensure it's cached "Using password from cache or keyring for LUKS operation.")
self.password_cache[username] = password # ensure it's cached
return "-", password return "-", password
# 2. Check for key file # 2. Check for key file
@@ -137,8 +137,10 @@ class EncryptionManager:
return f'--key-file "{key_file_path}"' return f'--key-file "{key_file_path}"'
# 3. If nothing found, prompt for password # 3. If nothing found, prompt for password
self.logger.log("No password in keyring and no keyfile found. Prompting user.") self.logger.log(
password = self.get_password(username, confirm=False) # This will now definitely open the dialog "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: if not password:
return "", None return "", None
return "-", password return "-", password

View File

@@ -255,7 +255,7 @@ class Msg:
"log": _("Log"), "log": _("Log"),
"full_backup": _("Full backup"), "full_backup": _("Full backup"),
"incremental": _("Incremental"), "incremental": _("Incremental"),
"incremental_backup": _("Incremental backup"), # New "incremental_backup": _("Incremental backup"), # New
"test_run": _("Test run"), "test_run": _("Test run"),
"start": _("Start"), "start": _("Start"),
"cancel_backup": _("Cancel"), "cancel_backup": _("Cancel"),
@@ -346,10 +346,11 @@ class Msg:
"header_subtitle": _("Simple GUI for rsync"), "header_subtitle": _("Simple GUI for rsync"),
"encrypted_backup_content": _("Encrypted Backups"), "encrypted_backup_content": _("Encrypted Backups"),
"compressed": _("Compressed"), "compressed": _("Compressed"),
"compression": _("Compression"), # New "compression": _("Compression"), # New
"encrypted": _("Encrypted"), "encrypted": _("Encrypted"),
"encryption": _("Encryption"), # New "encryption": _("Encryption"), # New
"bypass_security": _("Bypass security"), "bypass_security": _("Bypass security"),
"refresh_log": _("Refresh log"),
"comment": _("Kommentar"), "comment": _("Kommentar"),
"force_full_backup": _("Always force full backup"), "force_full_backup": _("Always force full backup"),
"force_incremental_backup": _("Always force incremental backup (except first)"), "force_incremental_backup": _("Always force incremental backup (except first)"),
@@ -367,5 +368,5 @@ class Msg:
"automation_settings_title": _("Automation Settings"), # New "automation_settings_title": _("Automation Settings"), # New
"create_add_key_file": _("Create/Add Key File"), # New "create_add_key_file": _("Create/Add Key File"), # New
"key_file_not_created": _("Key file not created."), # 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.compressed_var = tk.BooleanVar()
self.encrypted_var = tk.BooleanVar() self.encrypted_var = tk.BooleanVar()
self.bypass_security_var = tk.BooleanVar() self.bypass_security_var = tk.BooleanVar()
self.refresh_log_var = tk.BooleanVar(value=True)
self.mode = "backup" # Default mode self.mode = "backup" # Default mode
self.backup_is_running = False 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"], self.bypass_security_cb = ttk.Checkbutton(self.sidebar_buttons_frame, text=Msg.STR["bypass_security"],
variable=self.bypass_security_var, style="Switch2.TCheckbutton") variable=self.bypass_security_var, style="Switch2.TCheckbutton")
self.bypass_security_cb.pack(fill=tk.X, pady=10) 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.header_frame = HeaderFrame(
self.content_frame, self.image_manager, self.backup_manager.encryption_manager, self) 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): def _load_state_and_initialize(self):
# self.log_window.clear_log() # self.log_window.clear_log()
last_mode = self.config_manager.get_setting("last_mode", "backup") 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 = self.config_manager.get_setting(
"backup_source_path") "backup_source_path")
@@ -529,6 +535,7 @@ class MainApplication(tk.Tk):
self.start_pause_button.grid(row=0, column=2, rowspan=2, padx=5) self.start_pause_button.grid(row=0, column=2, rowspan=2, padx=5)
def on_closing(self): def on_closing(self):
self.config_manager.set_setting("refresh_log", self.refresh_log_var.get())
self.backup_manager.encryption_manager.unmount_all() self.backup_manager.encryption_manager.unmount_all()
self.config_manager.set_setting("last_mode", self.mode) 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 from core.pbp_app_config import AppConfig
# A simple logger for the CLI that just prints to the console # A simple logger for the CLI that just prints to the console
class CliLogger: class CliLogger:
def log(self, message): def log(self, message):
print(f"[CLI] {message}") print(f"[CLI] {message}")
def init_logger(self, log_method): def init_logger(self, log_method):
pass # Not needed for CLI pass # Not needed for CLI
def main(): def main():
parser = argparse.ArgumentParser(description="Py-Backup Command-Line Interface.") parser = argparse.ArgumentParser(
parser.add_argument("--backup-type", choices=['user', 'system'], required=True, help="Type of backup to perform.") description="Py-Backup Command-Line Interface.")
parser.add_argument("--destination", required=True, help="Destination directory for the backup.") parser.add_argument(
parser.add_argument("--source", help="Source directory for user backup. Required for --backup-type user.") "--backup-type", choices=['user', 'system'], required=True, help="Type of backup to perform.")
parser.add_argument("--mode", choices=['full', 'incremental'], default='incremental', help="Mode for system backup.") parser.add_argument("--destination", required=True,
parser.add_argument("--encrypted", action='store_true', help="Flag to indicate the backup should be encrypted.") help="Destination directory for the backup.")
parser.add_argument("--key-file", help="Path to the key file for unlocking an encrypted container.") parser.add_argument(
parser.add_argument("--password", help="Password for the encrypted container (use with caution). If --key-file is not provided, this will be used.") "--source", help="Source directory for user backup. Required for --backup-type user.")
parser.add_argument("--compressed", action='store_true', help="Flag to indicate the backup should be compressed.") 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() args = parser.parse_args()
@@ -34,17 +46,19 @@ def main():
parser.error("--source is required for --backup-type 'user'.") parser.error("--source is required for --backup-type 'user'.")
if args.encrypted and not (args.key_file or args.password): 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() cli_logger = CliLogger()
backup_manager = BackupManager(cli_logger) 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': if args.backup_type == 'user':
source_path = args.source source_path = args.source
if not os.path.isdir(source_path): 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) sys.exit(1)
# Determine password or key_file to pass # Determine password or key_file to pass
@@ -54,7 +68,8 @@ def main():
if args.key_file: if args.key_file:
auth_key_file = args.key_file auth_key_file = args.key_file
if not os.path.exists(auth_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) sys.exit(1)
elif args.password: elif args.password:
auth_password = args.password auth_password = args.password
@@ -78,8 +93,8 @@ def main():
dest_path=args.destination, dest_path=args.destination,
is_system=(args.backup_type == 'system'), is_system=(args.backup_type == 'system'),
is_dry_run=False, is_dry_run=False,
exclude_files=None, # Excludes are handled by AppConfig.MANUAL_EXCLUDE_LIST_PATH 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 source_size=0, # Not accurately calculable in CLI without scanning, set to 0
is_compressed=args.compressed, is_compressed=args.compressed,
is_encrypted=args.encrypted, is_encrypted=args.encrypted,
mode=args.mode, mode=args.mode,
@@ -92,5 +107,6 @@ def main():
cli_logger.log("CLI backup process finished.") cli_logger.log("CLI backup process finished.")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -157,12 +157,13 @@ class Actions:
is_encrypted = self.app.encrypted_var.get() is_encrypted = self.app.encrypted_var.get()
exclude_files = [] exclude_files = []
if AppConfig.GENERATED_EXCLUDE_LIST_PATH.exists(): if is_system:
exclude_files.append(AppConfig.GENERATED_EXCLUDE_LIST_PATH) if AppConfig.GENERATED_EXCLUDE_LIST_PATH.exists():
if AppConfig.USER_EXCLUDE_LIST_PATH.exists(): exclude_files.append(AppConfig.GENERATED_EXCLUDE_LIST_PATH)
exclude_files.append(AppConfig.USER_EXCLUDE_LIST_PATH) if AppConfig.USER_EXCLUDE_LIST_PATH.exists():
if AppConfig.MANUAL_EXCLUDE_LIST_PATH.exists(): exclude_files.append(AppConfig.USER_EXCLUDE_LIST_PATH)
exclude_files.append(AppConfig.MANUAL_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( size = self.app.backup_manager.estimate_incremental_size(
source_path=folder_path, 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.system_backup_content_frame import SystemBackupContentFrame
from pyimage_ui.user_backup_content_frame import UserBackupContentFrame from pyimage_ui.user_backup_content_frame import UserBackupContentFrame
from shared_libs.logger import app_logger from shared_libs.logger import app_logger
from shared_libs.message import MessageDialog
class BackupContentFrame(ttk.Frame): class BackupContentFrame(ttk.Frame):
@@ -149,7 +148,7 @@ class BackupContentFrame(ttk.Frame):
# Check if the destination is encrypted and trigger mount if necessary # Check if the destination is encrypted and trigger mount if necessary
is_encrypted = self.backup_manager.encryption_manager.is_encrypted( is_encrypted = self.backup_manager.encryption_manager.is_encrypted(
backup_path) 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") pybackup_dir = os.path.join(backup_path, "pybackup")
@@ -161,7 +160,8 @@ class BackupContentFrame(ttk.Frame):
self.user_backups_frame.show(backup_path, []) self.user_backups_frame.show(backup_path, [])
return 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: if all_backups:
system_backups, user_backups = all_backups system_backups, user_backups = all_backups
self.system_backups_frame.show(backup_path, system_backups) self.system_backups_frame.show(backup_path, system_backups)

View File

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

View File

@@ -1,6 +1,5 @@
import tkinter as tk import tkinter as tk
from tkinter import ttk from tkinter import ttk
from shared_libs.message import MessageDialog
import keyring import keyring
@@ -20,9 +19,10 @@ class EncryptionFrame(ttk.Frame):
self.keyring_status_label = ttk.Label(self, text="") self.keyring_status_label = ttk.Label(self, text="")
self.keyring_status_label.grid( self.keyring_status_label.grid(
row=1, column=0, sticky="ew", padx=10, pady=5) row=1, column=0, sticky="ew", padx=10, pady=5)
self.keyring_usage_label = ttk.Label(self, text="") 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() 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") 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 = 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): def set_context(self, username):
self.username = username self.username = username
@@ -75,39 +76,47 @@ class EncryptionFrame(ttk.Frame):
def set_session_password(self): def set_session_password(self):
password = self.password_entry.get() password = self.password_entry.get()
if not password: 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 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 self.save_to_keyring_var.get():
if not self.username: 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 return
if self.encryption_manager.set_password_in_keyring(self.username, password): 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() self.update_keyring_status()
else: 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: 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): def clear_session_password(self):
self.encryption_manager.clear_session_password() self.encryption_manager.clear_session_password()
self.password_entry.delete(0, tk.END) 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: if self.username:
self.encryption_manager.delete_password_from_keyring(self.username) self.encryption_manager.delete_password_from_keyring(self.username)
self.update_keyring_status() self.update_keyring_status()
def update_keyring_status(self): def update_keyring_status(self):
if not self.username: 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 return
if self.encryption_manager.get_password_from_keyring(self.username): 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: 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 import os
from core.pbp_app_config import Msg from core.pbp_app_config import Msg
from shared_libs.common_tools import IconManager
from shared_libs.logger import app_logger from shared_libs.logger import app_logger
class HeaderFrame(tk.Frame): class HeaderFrame(tk.Frame):
def __init__(self, container, image_manager, encryption_manager, app, **kwargs): def __init__(self, container, image_manager, encryption_manager, app, **kwargs):
super().__init__(container, bg="#455A64", **kwargs) super().__init__(container, bg="#455A64", **kwargs)
@@ -63,8 +63,9 @@ class HeaderFrame(tk.Frame):
font=("Helvetica", 10, "bold"), font=("Helvetica", 10, "bold"),
bg="#455A64", 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() self.refresh_status()
def refresh_status(self): def refresh_status(self):
@@ -74,10 +75,12 @@ class HeaderFrame(tk.Frame):
app_logger.log(f"HeaderFrame: Destination path is '{dest_path}'") app_logger.log(f"HeaderFrame: Destination path is '{dest_path}'")
if not dest_path or not self.encryption_manager.is_encrypted(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.") app_logger.log(
self.keyring_status_label.config(text="") # Clear status if not encrypted "HeaderFrame: No destination path or not encrypted. Clearing status.")
# Clear status if not encrypted
self.keyring_status_label.config(text="")
return return
app_logger.log("HeaderFrame: Destination is encrypted.") app_logger.log("HeaderFrame: Destination is encrypted.")
username = os.path.basename(dest_path.rstrip('/')) username = os.path.basename(dest_path.rstrip('/'))
app_logger.log(f"HeaderFrame: Username is '{username}'") app_logger.log(f"HeaderFrame: Username is '{username}'")
@@ -97,9 +100,11 @@ class HeaderFrame(tk.Frame):
fg="#2E8B57" # SeaGreen fg="#2E8B57" # SeaGreen
) )
else: 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}") 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}") app_logger.log(f"HeaderFrame: Key file exists? {key_file_exists}")
if key_in_keyring: if key_in_keyring:
@@ -117,4 +122,4 @@ class HeaderFrame(tk.Frame):
text="Key: Not Available", text="Key: Not Available",
fg="#A9A9A9" # DarkGray 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() self.app.drawing.redraw_right_canvas()
def toggle_mode(self, mode=None, active_index=None, trigger_calculation=True): 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 self.app.backup_is_running:
# If a backup is running, we only want to switch the view to the main backup screen. # If a backup is running, we only want to switch the view to the main backup screen.
# We don't reset anything. # We don't reset anything.
@@ -140,7 +142,6 @@ class Navigation:
self.app.scheduler_frame.hide() self.app.scheduler_frame.hide()
self.app.settings_frame.hide() self.app.settings_frame.hide()
self.app.backup_content_frame.grid_remove() self.app.backup_content_frame.grid_remove()
# Show the main content frames # Show the main content frames
self.app.canvas_frame.grid() self.app.canvas_frame.grid()
@@ -185,7 +186,7 @@ class Navigation:
self.app.scheduler_frame.hide() self.app.scheduler_frame.hide()
self.app.settings_frame.hide() self.app.settings_frame.hide()
self.app.backup_content_frame.grid_remove() self.app.backup_content_frame.grid_remove()
self.app.canvas_frame.grid() self.app.canvas_frame.grid()
self.app.source_size_frame.grid() self.app.source_size_frame.grid()
self.app.target_size_frame.grid() self.app.target_size_frame.grid()
@@ -232,6 +233,8 @@ class Navigation:
self._update_task_bar_visibility("log") self._update_task_bar_visibility("log")
def toggle_scheduler_frame(self, active_index=None): def toggle_scheduler_frame(self, active_index=None):
if self.app.refresh_log_var.get():
self.app.log_window.clear_log()
self._cancel_calculation() self._cancel_calculation()
if active_index is not None: if active_index is not None:
self.app.drawing.update_nav_buttons(active_index) self.app.drawing.update_nav_buttons(active_index)
@@ -240,7 +243,7 @@ class Navigation:
self.app.log_frame.grid_remove() self.app.log_frame.grid_remove()
self.app.settings_frame.hide() self.app.settings_frame.hide()
self.app.backup_content_frame.grid_remove() self.app.backup_content_frame.grid_remove()
self.app.source_size_frame.grid_remove() self.app.source_size_frame.grid_remove()
self.app.target_size_frame.grid_remove() self.app.target_size_frame.grid_remove()
self.app.restore_size_frame_before.grid_remove() self.app.restore_size_frame_before.grid_remove()
@@ -250,6 +253,8 @@ class Navigation:
self._update_task_bar_visibility("scheduler") self._update_task_bar_visibility("scheduler")
def toggle_settings_frame(self, active_index=None): def toggle_settings_frame(self, active_index=None):
if self.app.refresh_log_var.get():
self.app.log_window.clear_log()
self._cancel_calculation() self._cancel_calculation()
if active_index is not None: if active_index is not None:
self.app.drawing.update_nav_buttons(active_index) self.app.drawing.update_nav_buttons(active_index)
@@ -258,7 +263,7 @@ class Navigation:
self.app.log_frame.grid_remove() self.app.log_frame.grid_remove()
self.app.backup_content_frame.grid_remove() self.app.backup_content_frame.grid_remove()
self.app.scheduler_frame.hide() self.app.scheduler_frame.hide()
self.app.source_size_frame.grid_remove() self.app.source_size_frame.grid_remove()
self.app.target_size_frame.grid_remove() self.app.target_size_frame.grid_remove()
self.app.restore_size_frame_before.grid_remove() self.app.restore_size_frame_before.grid_remove()
@@ -267,7 +272,9 @@ class Navigation:
self.app.top_bar.grid() self.app.top_bar.grid()
self._update_task_bar_visibility("settings") 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._cancel_calculation()
self.app.drawing.update_nav_buttons(2) # Index 2 for Backup Content 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.log_frame.grid_remove()
self.app.scheduler_frame.hide() self.app.scheduler_frame.hide()
self.app.settings_frame.hide() self.app.settings_frame.hide()
self.app.source_size_frame.grid_remove() self.app.source_size_frame.grid_remove()
self.app.target_size_frame.grid_remove() self.app.target_size_frame.grid_remove()
self.app.restore_size_frame_before.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.next_backup_content_view = 'system'
self.app.top_bar.grid() 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, text=Msg.STR["scheduled_jobs"], padding=10)
self.jobs_frame.pack(fill=tk.BOTH, expand=True) 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_tree = ttk.Treeview(
self.jobs_frame, columns=columns, show="headings") self.jobs_frame, columns=columns, show="headings")
self.jobs_tree.heading("active", text=Msg.STR["active"]) 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_system_var = tk.BooleanVar(value=True)
self.backup_type_user_var = tk.BooleanVar(value=False) self.backup_type_user_var = tk.BooleanVar(value=False)
self.freq_daily_var = tk.BooleanVar(value=True) self.freq_daily_var = tk.BooleanVar(value=True)
self.freq_weekly_var = tk.BooleanVar(value=False) self.freq_weekly_var = tk.BooleanVar(value=False)
self.freq_monthly_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( self.user_sources_frame = ttk.LabelFrame(
source_options_container, text=Msg.STR["source_folders"], padding=10) 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(): for name, var in self.user_sources.items():
ttk.Checkbutton(self.user_sources_frame, text=name, ttk.Checkbutton(self.user_sources_frame, text=name,
variable=var, style="Switch.TCheckbutton").pack(anchor=tk.W) 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.compress_var = tk.BooleanVar(value=False)
self.encrypt_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.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.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.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) self.encrypt_checkbutton.pack(anchor=tk.W)
dest_frame = ttk.LabelFrame( dest_frame = ttk.LabelFrame(
@@ -147,7 +153,8 @@ class SchedulerFrame(ttk.Frame):
self._toggle_user_sources() self._toggle_user_sources()
def _handle_freq_switch(self, changed_var): 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(): if vars[changed_var].get():
self.frequency.set(changed_var) self.frequency.set(changed_var)
for var_name, var_obj in vars.items(): for var_name, var_obj in vars.items():
@@ -187,7 +194,7 @@ class SchedulerFrame(ttk.Frame):
self.destination.set("") self.destination.set("")
for var in self.user_sources.values(): for var in self.user_sources.values():
var.set(False) var.set(False)
self._on_compression_toggle_scheduler() # Update state of incremental checkbox self._on_compression_toggle_scheduler() # Update state of incremental checkbox
else: else:
self.add_job_frame.pack_forget() self.add_job_frame.pack_forget()
self.jobs_frame.pack(fill=tk.BOTH, expand=True) self.jobs_frame.pack(fill=tk.BOTH, expand=True)
@@ -240,7 +247,7 @@ class SchedulerFrame(ttk.Frame):
if job_type == "user": if job_type == "user":
command += f" --sources " command += f" --sources "
for s in job_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}" comment = f"{self.backup_manager.app_tag}; type:{job_type}; freq:{job_frequency}; dest:{dest}"
if job_type == "user": if job_type == "user":
@@ -289,4 +296,4 @@ class SchedulerFrame(ttk.Frame):
job_id = self.jobs_tree.item(selected_item)["values"][0] job_id = self.jobs_tree.item(selected_item)["values"][0]
self.backup_manager.remove_scheduled_job(job_id) 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 import tkinter as tk
from tkinter import ttk from tkinter import ttk
import os import os
import shutil
from core.pbp_app_config import Msg from core.pbp_app_config import Msg
from pyimage_ui.comment_editor_dialog import CommentEditorDialog from pyimage_ui.comment_editor_dialog import CommentEditorDialog