WIP: Refactor UI

This commit is contained in:
Orsiris de Jong 2024-01-22 09:38:52 +01:00
parent 9ddd676af3
commit 1ef8887b8b
5 changed files with 189 additions and 148 deletions

View file

@ -14,12 +14,13 @@ from typing import List
import os import os
from logging import getLogger from logging import getLogger
import PySimpleGUI as sg import PySimpleGUI as sg
import textwrap
from ruamel.yaml.comments import CommentedMap from ruamel.yaml.comments import CommentedMap
import npbackup.configuration as configuration import npbackup.configuration as configuration
from ofunctions.misc import get_key_from_value from ofunctions.misc import get_key_from_value
from npbackup.core.i18n_helper import _t from npbackup.core.i18n_helper import _t
from npbackup.path_helper import CURRENT_EXECUTABLE from npbackup.path_helper import CURRENT_EXECUTABLE
from npbackup.customization import INHERITANCE_ICON from npbackup.customization import INHERITANCE_ICON, FILE_ICON, FOLDER_ICON
if os.name == "nt": if os.name == "nt":
from npbackup.windows.task import create_scheduled_task from npbackup.windows.task import create_scheduled_task
@ -27,6 +28,29 @@ if os.name == "nt":
logger = getLogger() logger = getLogger()
# Monkeypatching PySimpleGUI
def delete(self, key):
if key == '':
return False
try:
node = self.tree_dict[key]
key_list = [key, ]
parent_node = self.tree_dict[node.parent]
parent_node.children.remove(node)
while key_list != []:
temp = []
for item in key_list:
temp += self.tree_dict[item].children
del self.tree_dict[item]
key_list = temp
return True
except KeyError:
pass
sg.TreeData.delete = delete
def ask_manager_password(manager_password: str) -> bool: def ask_manager_password(manager_password: str) -> bool:
if manager_password: if manager_password:
if sg.PopupGetText( if sg.PopupGetText(
@ -433,47 +457,14 @@ def config_gui(full_config: dict, config_file: str):
Returns the GUI layout depending on the object type Returns the GUI layout depending on the object type
""" """
backup_col = [ backup_col = [
[
sg.Text(_t("config_gui.compression"), size=(40, 1)),
sg.pin(
sg.Image(
INHERITANCE_ICON,
key="inherited.backup_opts.compression",
tooltip=_t("config_gui.group_inherited"),
)
),
sg.Combo(
list(combo_boxes["compression"].values()),
key="backup_opts.compression",
size=(48, 1),
),
],
[ [
sg.Text( sg.Text(
f"{_t('config_gui.backup_paths')}\n({_t('config_gui.one_per_line')})", textwrap.fill(f"{_t('config_gui.backup_paths')}"),
size=(40, 2), size=(None, None), expand_x=True,
), ),
sg.pin( sg.Text(
sg.Image( textwrap.fill(f"{_t('config_gui.source_type')}"),
INHERITANCE_ICON, size=(None, None), expand_x=True, justification='R'
expand_x=True,
expand_y=True,
key="inherited.backup_opts.paths",
tooltip=_t("config_gui.group_inherited"),
)
),
sg.Multiline(key="backup_opts.paths", size=(48, 4)),
],
[
sg.Text(_t("config_gui.source_type"), size=(40, 1)),
sg.pin(
sg.Image(
INHERITANCE_ICON,
expand_x=True,
expand_y=True,
key="inherited.backup_opts.source_type",
tooltip=_t("config_gui.group_inherited"),
)
), ),
sg.Combo( sg.Combo(
list(combo_boxes["source_type"].values()), list(combo_boxes["source_type"].values()),
@ -482,32 +473,67 @@ def config_gui(full_config: dict, config_file: str):
), ),
], ],
[ [
sg.Tree(sg.TreeData(), key="inherited.backup_opts.path", headings=[], expand_x=True, expand_y=True)
],
[
sg.Input(visible=False, key="--PATHS-ADD-FILE--", enable_events=True),
sg.FilesBrowse(_t("generic.add_files"), target="--PATHS-ADD-FILE--"),
sg.Input(visible=False, key="--PATHS-ADD-FOLDER--", enable_events=True),
sg.FolderBrowse(_t("generic.add_folder"), target="--PATHS-ADD-FOLDER--"),
sg.Button(_t("generic.remove_selected"), key="--REMOVE-SELECTED-BACKUP-PATHS--")
],
[
sg.Text(_t("config_gui.compression"), size=(20, None)),
sg.Combo(list(combo_boxes["compression"].values()), key="backup_opts.compression", size=(20, 1)),
sg.pin(sg.Image(INHERITANCE_ICON, key="inherited.backup_opts.compression", tooltip=_t("config_gui.group_inherited"))),
sg.Text(_t("config_gui.backup_priority"), size=(20, 1)),
sg.Combo(
list(combo_boxes["priority"].values()),
key="backup_opts.priority",
size=(20, 1),
),
sg.pin(sg.Image(INHERITANCE_ICON, key="inherited.backup_opts.backup_priority", tooltip=_t("config_gui.group_inherited")))
],
[
sg.Checkbox("", key="backup_opts.use_fs_snapshot", size=(1, 1)),
sg.Text( sg.Text(
"{}\n({})".format( textwrap.fill(f'{_t("config_gui.use_fs_snapshot")} ({_t("config_gui.windows_only")})', width=34),
_t("config_gui.use_fs_snapshot"), _t("config_gui.windows_only") size=(34, 2),
),
size=(40, 2),
), ),
sg.pin( ],
sg.Image( [
INHERITANCE_ICON, sg.Text(_t("config_gui.minimum_backup_size_error"), size=(40, 2)),
expand_x=True, sg.Input(key="backup_opts.minimum_backup_size_error", size=(50, 1)),
expand_y=True,
key="inherited.backup_opts.use_fs_snapshot",
tooltip=_t("config_gui.group_inherited"),
)
),
sg.Checkbox("", key="backup_opts.use_fs_snapshot", size=(41, 1)),
], ],
[ [
sg.Text( sg.Text(
"{}\n({})".format( f"{_t('config_gui.tags')}\n({_t('config_gui.one_per_line')})",
_t("config_gui.ignore_cloud_files"),
_t("config_gui.windows_only"),
),
size=(40, 2), size=(40, 2),
), ),
sg.Checkbox("", key="backup_opts.ignore_cloud_files", size=(41, 1)), sg.Multiline(key="backup_opts.tags", size=(48, 4)),
],
[
sg.Text(_t("config_gui.additional_parameters"), size=(40, 1)),
sg.Input(key="backup_opts.additional_parameters", size=(50, 1)),
],
[
sg.Text(
_t("config_gui.additional_backup_only_parameters"), size=(40, 1)
),
sg.Input(
key="backup_opts.additional_backup_only_parameters", size=(50, 1)
),
],
]
exclusions_col = [
[
sg.Checkbox("", key="backup_opts.ignore_cloud_files", size=(1, 1)),
sg.Text(
textwrap.fill(f'{_t("config_gui.ignore_cloud_files")} ({_t("config_gui.windows_only")})', width=34),
size=(34, 2),
),
], ],
[ [
sg.Text( sg.Text(
@ -548,11 +574,10 @@ def config_gui(full_config: dict, config_file: str):
sg.Text(_t("config_gui.one_file_system"), size=(40, 1)), sg.Text(_t("config_gui.one_file_system"), size=(40, 1)),
sg.Checkbox("", key="backup_opts.one_file_system", size=(41, 1)), sg.Checkbox("", key="backup_opts.one_file_system", size=(41, 1)),
], ],
[ ]
sg.Text(_t("config_gui.minimum_backup_size_error"), size=(40, 2)),
sg.Input(key="backup_opts.minimum_backup_size_error", size=(50, 1)), pre_post_col = [
], [
[
sg.Text( sg.Text(
f"{_t('config_gui.pre_exec_commands')}\n({_t('config_gui.one_per_line')})", f"{_t('config_gui.pre_exec_commands')}\n({_t('config_gui.one_per_line')})",
size=(40, 2), size=(40, 2),
@ -594,33 +619,6 @@ def config_gui(full_config: dict, config_file: str):
size=(41, 1), size=(41, 1),
), ),
], ],
[
sg.Text(
f"{_t('config_gui.tags')}\n({_t('config_gui.one_per_line')})",
size=(40, 2),
),
sg.Multiline(key="backup_opts.tags", size=(48, 4)),
],
[
sg.Text(_t("config_gui.backup_priority"), size=(40, 1)),
sg.Combo(
list(combo_boxes["priority"].values()),
key="backup_opts.priority",
size=(48, 1),
),
],
[
sg.Text(_t("config_gui.additional_parameters"), size=(40, 1)),
sg.Input(key="backup_opts.additional_parameters", size=(50, 1)),
],
[
sg.Text(
_t("config_gui.additional_backup_only_parameters"), size=(40, 1)
),
sg.Input(
key="backup_opts.additional_backup_only_parameters", size=(50, 1)
),
],
] ]
repo_col = [ repo_col = [
@ -628,6 +626,14 @@ def config_gui(full_config: dict, config_file: str):
sg.Text(_t("config_gui.backup_repo_uri"), size=(40, 1)), sg.Text(_t("config_gui.backup_repo_uri"), size=(40, 1)),
sg.Input(key="repo_uri", size=(50, 1)), sg.Input(key="repo_uri", size=(50, 1)),
], ],
[
sg.Text(_t("config_gui.backup_repo_password"), size=(40, 1)),
sg.Input(key="repo_opts.repo_password", size=(50, 1)),
],
[
sg.Text(_t("config_gui.backup_repo_password_command"), size=(40, 1)),
sg.Input(key="repo_opts.repo_password_command", size=(50, 1)),
],
[sg.Button(_t("config_gui.set_permissions"), key="--SET-PERMISSIONS--")], [sg.Button(_t("config_gui.set_permissions"), key="--SET-PERMISSIONS--")],
[ [
sg.Text(_t("config_gui.repo_group"), size=(40, 1)), sg.Text(_t("config_gui.repo_group"), size=(40, 1)),
@ -642,14 +648,6 @@ def config_gui(full_config: dict, config_file: str):
), ),
sg.Input(key="repo_opts.minimum_backup_age", size=(50, 1)), sg.Input(key="repo_opts.minimum_backup_age", size=(50, 1)),
], ],
[
sg.Text(_t("config_gui.backup_repo_password"), size=(40, 1)),
sg.Input(key="repo_opts.repo_password", size=(50, 1)),
],
[
sg.Text(_t("config_gui.backup_repo_password_command"), size=(40, 1)),
sg.Input(key="repo_opts.repo_password_command", size=(50, 1)),
],
[ [
sg.Text(_t("config_gui.upload_speed"), size=(40, 1)), sg.Text(_t("config_gui.upload_speed"), size=(40, 1)),
sg.Input(key="repo_opts.upload_speed", size=(50, 1)), sg.Input(key="repo_opts.upload_speed", size=(50, 1)),
@ -774,37 +772,41 @@ def config_gui(full_config: dict, config_file: str):
[ [
sg.Tab( sg.Tab(
_t("config_gui.backup"), _t("config_gui.backup"),
[ backup_col,
[
sg.Column(
backup_col,
scrollable=True,
vertical_scroll_only=True,
size=(700, 450),
)
]
],
font="helvetica 16", font="helvetica 16",
key="--tab-backup--", key="--tab-backup--",
element_justification="L", #element_justification="L",
expand_x=True,
expand_y=True
) )
], ],
[ [
sg.Tab( sg.Tab(
_t("config_gui.backup_destination"), _t("config_gui.backup_destination"),
[ repo_col,
[
sg.Column(
repo_col,
scrollable=True,
vertical_scroll_only=True,
size=(700, 450),
)
]
],
font="helvetica 16", font="helvetica 16",
key="--tab-repo--", key="--tab-repo--",
element_justification="L", expand_x=True, expand_y=True,
)
],
[
sg.Tab(
_t("config_gui.exclusions"),
exclusions_col,
font="helvetica 16",
key="--tab-exclusions--",
expand_x=True,
expand_y=True
)
],
[
sg.Tab(
_t("config_gui.pre_post"),
pre_post_col,
font="helvetica 16",
key="--tab-hooks--",
expand_x=True,
expand_y=True
) )
], ],
[ [
@ -813,7 +815,7 @@ def config_gui(full_config: dict, config_file: str):
prometheus_col, prometheus_col,
font="helvetica 16", font="helvetica 16",
key="--tab-prometheus--", key="--tab-prometheus--",
element_justification="L", expand_x=True, expand_y=True,
) )
], ],
[ [
@ -822,13 +824,15 @@ def config_gui(full_config: dict, config_file: str):
env_col, env_col,
font="helvetica 16", font="helvetica 16",
key="--tab-env--", key="--tab-env--",
element_justification="L", expand_x=True, expand_y=True,
) )
], ],
] ]
_layout = [ _layout = [
[sg.Column(object_selector, element_justification="L")], [sg.Column(object_selector,
#element_justification="L"
)],
[ [
sg.TabGroup( sg.TabGroup(
tab_group_layout, enable_events=True, key="--object-tabgroup--" tab_group_layout, enable_events=True, key="--object-tabgroup--"
@ -906,7 +910,7 @@ def config_gui(full_config: dict, config_file: str):
identity_col, identity_col,
font="helvetica 16", font="helvetica 16",
key="--tab-global-identification--", key="--tab-global-identification--",
element_justification="L", #element_justification="L",
) )
], ],
[ [
@ -915,7 +919,7 @@ def config_gui(full_config: dict, config_file: str):
global_options_col, global_options_col,
font="helvetica 16", font="helvetica 16",
key="--tab-global-options--", key="--tab-global-options--",
element_justification="L", #element_justification="L",
) )
], ],
[ [
@ -924,7 +928,7 @@ def config_gui(full_config: dict, config_file: str):
scheduled_task_col, scheduled_task_col,
font="helvetica 16", font="helvetica 16",
key="--tab-global-scheduled_task--", key="--tab-global-scheduled_task--",
element_justification="L", #element_justification="L",
) )
], ],
] ]
@ -943,13 +947,13 @@ def config_gui(full_config: dict, config_file: str):
[ [
sg.Push(), sg.Push(),
sg.Button( sg.Button(
_t("config_gui.create_object"), key="-OBJECT-CREATE-", size=(30, 1) _t("config_gui.create_object"), key="-OBJECT-CREATE-", size=(28, 1)
), ),
sg.Button( sg.Button(
_t("config_gui.delete_object"), key="-OBJECT-DELETE-", size=(30, 1) _t("config_gui.delete_object"), key="-OBJECT-DELETE-", size=(28, 1)
), ),
sg.Button(_t("generic.cancel"), key="--CANCEL--", size=(15, 1)), sg.Button(_t("generic.cancel"), key="--CANCEL--", size=(13, 1)),
sg.Button(_t("generic.accept"), key="--ACCEPT--", size=(15, 1)), sg.Button(_t("generic.accept"), key="--ACCEPT--", size=(13, 1)),
] ]
] ]
@ -959,6 +963,8 @@ def config_gui(full_config: dict, config_file: str):
_t("config_gui.repo_group_config"), _t("config_gui.repo_group_config"),
object_layout(), object_layout(),
key="--repo-group-config--", key="--repo-group-config--",
expand_x=True,
expand_y=True
) )
], ],
[ [
@ -966,6 +972,8 @@ def config_gui(full_config: dict, config_file: str):
_t("config_gui.global_config"), _t("config_gui.global_config"),
global_options_layout(), global_options_layout(),
key="--global-config--", key="--global-config--",
expand_x=True,
expand_y=True
) )
], ],
] ]
@ -973,10 +981,11 @@ def config_gui(full_config: dict, config_file: str):
_global_layout = [ _global_layout = [
[ [
sg.TabGroup( sg.TabGroup(
tab_group_layout, enable_events=True, key="--configtabgroup--" tab_group_layout, enable_events=True, key="--configtabgroup--", expand_x=True, expand_y=True,
) )
], ],
[sg.Push(), sg.Column(buttons, element_justification="L")], [sg.Push(), sg.Column(buttons,
)],
] ]
return _global_layout return _global_layout
@ -984,8 +993,7 @@ def config_gui(full_config: dict, config_file: str):
window = sg.Window( window = sg.Window(
"Configuration", "Configuration",
config_layout(), config_layout(),
size=(800, 600), #size=(800, 650),
text_justification="C",
auto_size_text=True, auto_size_text=True,
auto_size_buttons=False, auto_size_buttons=False,
no_titlebar=False, no_titlebar=False,
@ -997,6 +1005,8 @@ def config_gui(full_config: dict, config_file: str):
finalize=True, finalize=True,
) )
backup_paths_tree = sg.TreeData()
# Update gui with first default object (repo or group) # Update gui with first default object (repo or group)
update_object_gui(get_objects()[0], unencrypted=False) update_object_gui(get_objects()[0], unencrypted=False)
update_global_gui(full_config, unencrypted=False) update_global_gui(full_config, unencrypted=False)
@ -1028,6 +1038,20 @@ def config_gui(full_config: dict, config_file: str):
if ask_manager_password(manager_password): if ask_manager_password(manager_password):
full_config = set_permissions(full_config, values["-OBJECT-SELECT-"]) full_config = set_permissions(full_config, values["-OBJECT-SELECT-"])
continue continue
if event in ("--PATHS-ADD-FILE--", '--PATHS-ADD-FOLDER--'):
if event == "--PATHS-ADD-FILE--":
node = values["--PATHS-ADD-FILE--"]
icon = FILE_ICON
elif event == '--PATHS-ADD-FOLDER--':
node = values['--PATHS-ADD-FOLDER--']
icon = FOLDER_ICON
backup_paths_tree.insert('', node, node, node, icon=icon)
window['inherited.backup_opts.path'].update(values=backup_paths_tree)
if event == "--REMOVE-SELECTED-BACKUP-PATHS--":
# TODO: prevent removing inherited values
for key in values['inherited.backup_opts.path']:
backup_paths_tree.delete(key)
window['inherited.backup_opts.path'].update(values=backup_paths_tree)
if event == "--ACCEPT--": if event == "--ACCEPT--":
if ( if (
not values["repo_opts.repo_password"] not values["repo_opts.repo_password"]

View file

@ -1,6 +1,11 @@
en: en:
encrypted_data: Encrypted Data # tabs
backup: Backup backup: Backup
backup_destination: Destination
exclusions: Exclusions
pre_post: Pre/Post exec
encrypted_data: Encrypted Data
compression: Compression compression: Compression
backup_paths: Backup paths backup_paths: Backup paths
use_fs_snapshot: Use VSS snapshots use_fs_snapshot: Use VSS snapshots
@ -24,7 +29,6 @@ en:
additional_parameters: Additional parameters additional_parameters: Additional parameters
additional_backup_only_parameters: Additional backup only parmas additional_backup_only_parameters: Additional backup only parmas
backup_destination: Backup destination
minimum_backup_age: Minimum delay between two backups minimum_backup_age: Minimum delay between two backups
backup_repo_uri: backup repo URI / path backup_repo_uri: backup repo URI / path
backup_repo_password: Backup repo encryption password backup_repo_password: Backup repo encryption password
@ -98,10 +102,10 @@ en:
# source types # source types
source_type: Sources type source_type: Sources type
folder_list: Folder list folder_list: Folder / file list
files_from: Files from list files_from: From file
files_from_verbatim: Files from verbatim list files_from_verbatim: From verbatim
files_from_raw: Files from raw list files_from_raw: From raw
# retention policiy # retention policiy
keep: Keep keep: Keep

View file

@ -1,6 +1,12 @@
fr: fr:
encrypted_data: Donnée Chiffrée # tabs
backup: Sauvegarde backup: Sauvegarde
backup_destination: Destination
exclusions: Exclusions
pre_post: Pré/Post exec
encrypted_data: Donnée Chiffrée
compression: Compression compression: Compression
backup_paths: Chemins à sauvegarder backup_paths: Chemins à sauvegarder
use_fs_snapshot: Utiliser les instantanés VSS use_fs_snapshot: Utiliser les instantanés VSS
@ -24,7 +30,6 @@ fr:
additional_parameters: Paramètres supplémentaires additional_parameters: Paramètres supplémentaires
additional_backup_only_parameters: Paramètres supp. sauvegarde additional_backup_only_parameters: Paramètres supp. sauvegarde
backup_destination: Destination de sauvegarde
minimum_backup_age: Délai minimal entre deux sauvegardes minimum_backup_age: Délai minimal entre deux sauvegardes
backup_repo_uri: URL / chemin local dépot de sauvegarde backup_repo_uri: URL / chemin local dépot de sauvegarde
backup_repo_password: Mot de passe (chiffrement) dépot de sauvegarde backup_repo_password: Mot de passe (chiffrement) dépot de sauvegarde
@ -98,10 +103,10 @@ fr:
# source types # source types
source_type: Type de sources source_type: Type de sources
folder_list: Liste de dossiers folder_list: Liste de dossiers / fichiers
files_from: Liste fichiers depuis un fichier files_from: Liste depuis un fichier
files_from_verbatim: Liste fichiers depuis un fichier "exact" files_from_verbatim: Liste depuis un fichier "exact"
files_from_raw: Liste fichiers depuis un fichier "raw" files_from_raw: Liste depuis un fichier "raw"
# retention policies # retention policies
retention_policy: Politique de conservation retention_policy: Politique de conservation

View file

@ -62,4 +62,8 @@ en:
please_wait: Please wait please_wait: Please wait
bad_file: Bad file bad_file: Bad file
file_does_not_exist: File does not exist file_does_not_exist: File does not exist
add_files: Add files
add_folder: Add folder
remove_selected: Remove selected

View file

@ -62,4 +62,8 @@ fr:
please_wait: Merci de patienter please_wait: Merci de patienter
bad_file: Fichier erroné bad_file: Fichier erroné
file_does_not_exist: Fichier inexistant file_does_not_exist: Fichier inexistant
add_files: Ajouter fichiers
add_folder: Ajouter dossier
remove_selected: Enlever la sélection