add new button for refresh log disable
This commit is contained in:
@@ -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))
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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()
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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")
|
||||
|
@@ -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.")
|
||||
|
@@ -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")
|
||||
|
@@ -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()
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user