mirror of
https://github.com/netinvent/npbackup.git
synced 2026-01-02 13:46:08 +08:00
GUI: WIP wizard theming
This commit is contained in:
parent
6c80be6d74
commit
70021916bc
12 changed files with 183 additions and 68 deletions
|
|
@ -9,8 +9,8 @@ __site__ = "https://www.netperfect.fr/npbackup"
|
|||
__description__ = "NetPerfect Backup Client"
|
||||
__copyright__ = "Copyright (C) 2022-2025 NetInvent"
|
||||
__license__ = "GPL-3.0-only"
|
||||
__build__ = "2025092901"
|
||||
__version__ = "3.0.4"
|
||||
__build__ = "2025102201"
|
||||
__version__ = "3.1.0-dev"
|
||||
|
||||
|
||||
import sys
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ __intname__ = "npbackup.configuration"
|
|||
__author__ = "Orsiris de Jong"
|
||||
__copyright__ = "Copyright (C) 2022-2025 NetInvent"
|
||||
__license__ = "GPL-3.0-only"
|
||||
__build__ = "2025100401"
|
||||
__version__ = "npbackup 3.0.4+"
|
||||
__build__ = "2025102201"
|
||||
__version__ = "npbackup 3.1.0+"
|
||||
|
||||
|
||||
from typing import Tuple, Optional, List, Any, Union
|
||||
|
|
@ -32,7 +32,7 @@ from npbackup.key_management import AES_KEY, EARLIER_AES_KEY, IS_PRIV_BUILD, get
|
|||
from npbackup.__version__ import __version__ as MAX_CONF_VERSION
|
||||
|
||||
MIN_MIGRATABLE_CONF_VERSION = "3.0.0"
|
||||
MIN_CONF_VERSION = "3.0.4"
|
||||
MIN_CONF_VERSION = "3.1.0"
|
||||
|
||||
|
||||
sys.path.insert(0, os.path.normpath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
|
@ -249,6 +249,31 @@ empty_config_dict = {
|
|||
"full_concurrency": False, # Allow multiple npbackup instances to run at the same time
|
||||
"repo_aware_concurrency": False, # Allow multiple npbackup instances to run at the same time, but only for different repos
|
||||
},
|
||||
"presets": {
|
||||
"adds_to_existing": True,
|
||||
"replaces_existing": False,
|
||||
"retention_policies": {
|
||||
"gfs": {
|
||||
"keep_daily": 30,
|
||||
"keep_weekly": 4,
|
||||
"keep_monthly": 12,
|
||||
"keep_yearly": 3,
|
||||
"keep_within": True,
|
||||
"group_by_host": True,
|
||||
"group_by_tags": True,
|
||||
"group_by_paths": False,
|
||||
"ntp_server": None,
|
||||
"keep_tags": [],
|
||||
"apply_on_tags": [],
|
||||
}
|
||||
},
|
||||
},
|
||||
"destinations": {
|
||||
"default_destination": {
|
||||
"repo_uri": None,
|
||||
"repo_password": None,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -946,6 +971,8 @@ def _load_config_file(config_file: Path) -> Union[bool, dict]:
|
|||
|
||||
|
||||
def load_config(config_file: Path) -> Optional[dict]:
|
||||
if not isinstance(config_file, Path):
|
||||
config_file = Path(config_file)
|
||||
full_config = _load_config_file(config_file)
|
||||
if not full_config:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ from npbackup.__version__ import version_dict, version_string
|
|||
from npbackup.__debug__ import _DEBUG, _NPBACKUP_ALLOW_AUTOUPGRADE_DEBUG
|
||||
from npbackup.restic_wrapper import ResticRunner
|
||||
from npbackup.restic_wrapper import schema
|
||||
import sv_ttk
|
||||
|
||||
|
||||
logger = getLogger()
|
||||
|
|
@ -1128,10 +1129,11 @@ def _main_gui(viewer_mode: bool):
|
|||
alpha_channel=1.0,
|
||||
default_button_element_size=(16, 1),
|
||||
right_click_menu=right_click_menu,
|
||||
finalize=True,
|
||||
use_ttk_buttons=True,
|
||||
)
|
||||
|
||||
# Auto reisze table to window size
|
||||
window.finalize()
|
||||
sv_ttk.set_theme("light")
|
||||
# Auto resize table to window size
|
||||
window["snapshot-list"].expand(True, True)
|
||||
|
||||
window.read(timeout=0.01)
|
||||
|
|
@ -1140,7 +1142,8 @@ def _main_gui(viewer_mode: bool):
|
|||
|
||||
if repo_config:
|
||||
try:
|
||||
current_state, backup_tz, snapshot_list = get_gui_data(repo_config)
|
||||
# current_state, backup_tz, snapshot_list = get_gui_data(repo_config)
|
||||
raise TypeError # WIP
|
||||
except (TypeError, ValueError):
|
||||
current_state = None
|
||||
backup_tz = None
|
||||
|
|
|
|||
|
|
@ -111,7 +111,8 @@ def RoundedButton(
|
|||
metadata=None,
|
||||
btn_size=(None, None),
|
||||
):
|
||||
|
||||
if button_color is None:
|
||||
button_color = sg.theme_button_color()
|
||||
if btn_size != (None, None):
|
||||
btn_width: int = btn_size[0]
|
||||
btn_height: int = btn_size[1]
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ from resources.customization import (
|
|||
from npbackup.task import create_scheduled_task
|
||||
from npbackup.gui.helpers import quick_close_simplegui_window
|
||||
from npbackup.gui.constants import combo_boxes, byte_units
|
||||
import sv_ttk
|
||||
|
||||
logger = getLogger()
|
||||
|
||||
|
|
@ -2628,6 +2629,7 @@ Google Cloud storage: GOOGLE_PROJECT_ID GOOGLE_APPLICATION_CREDENTIALS\n\
|
|||
if config_file:
|
||||
window.set_title(f"Configuration - {config_file}")
|
||||
|
||||
# sv_ttk.set_theme("light")
|
||||
while True:
|
||||
event, values = window.read()
|
||||
# Get object type for various delete operations
|
||||
|
|
@ -2822,6 +2824,7 @@ Google Cloud storage: GOOGLE_PROJECT_ID GOOGLE_APPLICATION_CREDENTIALS\n\
|
|||
tree = env_variables_tree
|
||||
option_key = "env.env_variables"
|
||||
|
||||
# It's --ADD- and not --ADD-- since we include --ADD-*-- STYLE events
|
||||
if event.startswith("--ADD-"):
|
||||
icon = TREE_ICON
|
||||
if "ENV-VARIABLE" in event or "ENCRYPTED-ENV-VARIABLE" in event:
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ combo_boxes = {
|
|||
},
|
||||
"retention_options": {
|
||||
"GFS": _t("wizard_gui.retention_gfs"),
|
||||
"30days": _t("wizard_gui.retention_30days"),
|
||||
"14days": _t("wizard_gui.retention_14_days"),
|
||||
"30days": _t("wizard_gui.retention_30_days"),
|
||||
"keep_all": _t("wizard_gui.retention_keep_all"),
|
||||
},
|
||||
"backends": {
|
||||
|
|
|
|||
|
|
@ -34,77 +34,85 @@ from resources.customization import (
|
|||
TXT_COLOR_LDR,
|
||||
SIMPLEGUI_THEME,
|
||||
THEME_CHOOSER_ICON,
|
||||
TREE_ICON,
|
||||
)
|
||||
|
||||
from npbackup.gui.constants import combo_boxes, byte_units
|
||||
from npbackup.core.i18n_helper import _t
|
||||
from npbackup.core.i18n_helper import _t, _locale
|
||||
from npbackup.gui.buttons import RoundedButton
|
||||
import npbackup.configuration
|
||||
import sv_ttk
|
||||
|
||||
|
||||
CONFIG_FILE = "npbackup.conf" # WIP override via --config-file
|
||||
sg.LOOK_AND_FEEL_TABLE["CLEAR"] = LOOK_AND_FEEL_TABLE["CLEAR"]
|
||||
sg.LOOK_AND_FEEL_TABLE["DARK"] = LOOK_AND_FEEL_TABLE["DARK"]
|
||||
sg.theme(SIMPLEGUI_THEME)
|
||||
logger = getLogger()
|
||||
|
||||
add_source_menu = [
|
||||
"-ADD-SOURCE-",
|
||||
[
|
||||
_t("generic.add_files"),
|
||||
_t("generic.add_folder"),
|
||||
_t("wizard_gui.add_system"),
|
||||
_t("wizard_gui.add_hyper_v"),
|
||||
_t("wizard_gui.add_kvm"),
|
||||
],
|
||||
]
|
||||
|
||||
date_options = {
|
||||
"format": "%Y-%m-%d",
|
||||
"default_date_m_d_y": (
|
||||
datetime.now().month,
|
||||
datetime.now().day,
|
||||
datetime.now().year,
|
||||
),
|
||||
"close_when_date_chosen": True,
|
||||
}
|
||||
|
||||
conf = npbackup.configuration.load_config(CONFIG_FILE)
|
||||
if not conf:
|
||||
conf = npbackup.configuration.get_default_config()
|
||||
try:
|
||||
retention_policies = list(conf.g("presets.retention_policies").keys())
|
||||
except Exception:
|
||||
retention_policies = {}
|
||||
|
||||
backup_paths_tree = sg.TreeData()
|
||||
# retention_policies = list(combo_boxes["retention_options"].values())
|
||||
|
||||
wizard_layouts = {
|
||||
"wizard_layout_1": [
|
||||
[
|
||||
sg.Text(
|
||||
textwrap.fill(f"{_t('wizard_gui.select_backup_sources')}", 70),
|
||||
size=(None, None),
|
||||
expand_x=True,
|
||||
justification="c",
|
||||
textwrap.fill(f"{_t('wizard_gui.select_backup_sources')}"),
|
||||
size=(40, 1),
|
||||
expand_x=False,
|
||||
font=("Helvetica", 16),
|
||||
),
|
||||
sg.Push(),
|
||||
sg.ButtonMenu(
|
||||
_t("generic.add"),
|
||||
menu_def=add_source_menu,
|
||||
key="-ADD-SOURCE-MENU-",
|
||||
button_color=(TXT_COLOR_LDR, BG_COLOR_LDR),
|
||||
),
|
||||
],
|
||||
[
|
||||
sg.Input(visible=False, key="--ADD-PATHS-FILE--", enable_events=True),
|
||||
sg.FilesBrowse(
|
||||
"", # _t("generic.add_files"
|
||||
target="--ADD-PATHS-FILE--",
|
||||
key="--ADD-PATHS-FILE-BUTTON--",
|
||||
# button_color=(None, sg.LOOK_AND_FEEL_TABLE[SIMPLEGUI_THEME]["BACKGROUND"])
|
||||
),
|
||||
sg.Input(visible=False, key="--ADD-PATHS-FOLDER--", enable_events=True),
|
||||
sg.FolderBrowse(
|
||||
"", # _t("generic.add_folder"),
|
||||
target="--ADD-PATHS-FOLDER--",
|
||||
key="--ADD-PATHS-FOLDER-BUTTON--",
|
||||
# button_color=(None, sg.LOOK_AND_FEEL_TABLE[SIMPLEGUI_THEME]["BACKGROUND"])
|
||||
),
|
||||
sg.Button(
|
||||
"", # _t("generic.add_manually"),
|
||||
key="--ADD-PATHS-MANUALLY--",
|
||||
border_width=0,
|
||||
# button_color=(None, sg.LOOK_AND_FEEL_TABLE[SIMPLEGUI_THEME]["BACKGROUND"])
|
||||
),
|
||||
sg.Button(
|
||||
"", # _t("generic.remove_selected"),
|
||||
key="--REMOVE-PATHS--",
|
||||
border_width=0,
|
||||
# button_color=(None, sg.LOOK_AND_FEEL_TABLE[SIMPLEGUI_THEME]["BACKGROUND"])
|
||||
),
|
||||
sg.Button(
|
||||
"",
|
||||
key="-ADD-WINDOWS-SYSTEM-",
|
||||
border_width=0,
|
||||
),
|
||||
sg.Button(
|
||||
"",
|
||||
key="-ADD-HYPERV-",
|
||||
border_width=0,
|
||||
),
|
||||
sg.Button(
|
||||
"",
|
||||
key="-ADD-KVM-",
|
||||
border_width=0,
|
||||
sg.Text(
|
||||
textwrap.fill(f"{_t('wizard_gui.select_backup_sources_description')}"),
|
||||
size=(80, 2),
|
||||
expand_x=False,
|
||||
justification="L",
|
||||
),
|
||||
],
|
||||
[
|
||||
sg.Tree(
|
||||
sg.TreeData(),
|
||||
key="backup_opts.paths",
|
||||
headings=[],
|
||||
col0_heading=_t("config_gui.backup_sources"),
|
||||
headings=["Type", "Details"],
|
||||
# col0_heading=_t("config_gui.backup_sources"),
|
||||
expand_x=True,
|
||||
expand_y=True,
|
||||
header_text_color=TXT_COLOR_LDR,
|
||||
|
|
@ -126,6 +134,30 @@ wizard_layouts = {
|
|||
],
|
||||
],
|
||||
"wizard_layout_3": [
|
||||
[sg.Text(_t("wizard_gui.step_3"), font=("Helvetica", 16))],
|
||||
[
|
||||
sg.Input("YYYY/MM/DD", key="-FIRST-BACKUP-DATE-", size=(12, 1)),
|
||||
sg.Combo(
|
||||
values=[h for h in range(0, 24)],
|
||||
default_value=0,
|
||||
key="-FIRST-BACKUP-HOUR-",
|
||||
size=(3, 1),
|
||||
),
|
||||
sg.Text(" : "),
|
||||
sg.Combo(
|
||||
values=[m for m in range(0, 60)],
|
||||
default_value=0,
|
||||
key="-FIRST-BACKUP-MINUTE-",
|
||||
size=(3, 1),
|
||||
),
|
||||
],
|
||||
[
|
||||
sg.CalendarButton(
|
||||
"Calendar", target="-FIRST-BACKUP-DATE-", key="CALENDAR", **date_options
|
||||
),
|
||||
],
|
||||
],
|
||||
"wizard_layout_4": [
|
||||
[
|
||||
sg.Column(
|
||||
[
|
||||
|
|
@ -168,18 +200,18 @@ wizard_layouts = {
|
|||
),
|
||||
],
|
||||
],
|
||||
"wizard_layout_4": [
|
||||
"wizard_layout_5": [
|
||||
[sg.T(_t("wizard_gui.retention_settings"), font=("Helvetica", 16))],
|
||||
[
|
||||
sg.Combo(
|
||||
values=list(combo_boxes["retention_options"].values()),
|
||||
default_value=next(iter(combo_boxes["retention_options"])),
|
||||
values=retention_policies,
|
||||
default_value=retention_policies[0],
|
||||
key="-RETENTION-TYPE-",
|
||||
enable_events=True,
|
||||
)
|
||||
],
|
||||
],
|
||||
"wizard_layout_5": [
|
||||
"wizard_layout_6": [
|
||||
[sg.Text(_t("wizard_gui.end_user_experience"), font=("Helvetica", 16))],
|
||||
[
|
||||
sg.Checkbox(
|
||||
|
|
@ -189,7 +221,7 @@ wizard_layouts = {
|
|||
)
|
||||
],
|
||||
],
|
||||
"wizard_layout_6": [
|
||||
"wizard_layout_7": [
|
||||
[sg.Text(_t("wizard_gui.end_user_experience"), font=("Helvetica", 16))],
|
||||
],
|
||||
}
|
||||
|
|
@ -208,7 +240,7 @@ for i in range(1, len(wizard_layouts)):
|
|||
[
|
||||
RoundedButton(
|
||||
str(i),
|
||||
button_color=("#FAFAFA", "#ADADAD"),
|
||||
button_color=(TXT_COLOR_LDR, BG_COLOR_LDR),
|
||||
border_width=0,
|
||||
key=f"-BREADCRUMB-{i}-",
|
||||
btn_size=(30, 30),
|
||||
|
|
@ -249,7 +281,7 @@ wizard_layout = [
|
|||
border_width=0,
|
||||
),
|
||||
RoundedButton(
|
||||
_t("generic.start"),
|
||||
_t("generic.next"),
|
||||
key="-NEXT-",
|
||||
button_color=(TXT_COLOR_LDR, BG_COLOR_LDR),
|
||||
border_width=0,
|
||||
|
|
@ -266,8 +298,8 @@ wizard_layout = [
|
|||
|
||||
def start_wizard():
|
||||
CURRENT_THEME = SIMPLEGUI_THEME
|
||||
NUMBER_OF_TABS = len(wizard_tabs) + 1
|
||||
current_tab = 0
|
||||
NUMBER_OF_TABS = len(wizard_tabs)
|
||||
current_tab = 1
|
||||
wizard = sg.Window(
|
||||
"NPBackup Wizard",
|
||||
layout=wizard_layout,
|
||||
|
|
@ -286,7 +318,7 @@ def start_wizard():
|
|||
wizard.TKroot.after(60000, _reskin_job)
|
||||
|
||||
def set_active_tab(active_number):
|
||||
for tab_index in range(1, NUMBER_OF_TABS):
|
||||
for tab_index in range(1, NUMBER_OF_TABS + 1):
|
||||
if tab_index != active_number:
|
||||
wizard[f"-TAB{tab_index}-"].Update(visible=False)
|
||||
wizard[f"-BREADCRUMB-{tab_index}-"].Update(
|
||||
|
|
@ -296,9 +328,13 @@ def start_wizard():
|
|||
wizard[f"-BREADCRUMB-{active_number}-"].Update(button_color=("#3F2DCB", None))
|
||||
|
||||
wizard.finalize()
|
||||
# Widget theming from https://github.com/rdbende/Sun-Valley-ttk-theme?tab=readme-ov-file
|
||||
sv_ttk.set_theme("light")
|
||||
set_active_tab(1)
|
||||
|
||||
while True:
|
||||
event, values = wizard.read()
|
||||
print(event, values)
|
||||
if event == sg.WIN_CLOSED or event == _t("generic.cancel"):
|
||||
break
|
||||
if event == "-THEME-":
|
||||
|
|
@ -337,6 +373,33 @@ def start_wizard():
|
|||
set_active_tab(current_tab)
|
||||
elif current_tab == 1:
|
||||
break
|
||||
if event == "-ADD-SOURCE-MENU-":
|
||||
node = None
|
||||
if values["-ADD-SOURCE-MENU-"] == _t("generic.add_files"):
|
||||
sg.FileBrowse(_t("generic.add_files"), target="backup_opts.paths")
|
||||
node = sg.popup_get_file("Add files clicked", no_window=True)
|
||||
elif values["-ADD-SOURCE-MENU-"] == _t("generic.add_folder"):
|
||||
node = sg.popup_get_folder("Add folder clicked", no_window=True)
|
||||
elif values["-ADD-SOURCE-MENU-"] == _t("wizard_gui.add_system"):
|
||||
sg.popup("Add Windows system clicked", keep_on_top=True)
|
||||
elif values["-ADD-SOURCE-MENU-"] == _t("wizard_gui.add_hyper_v"):
|
||||
sg.popup("Add Hyper-V virtual machines clicked", keep_on_top=True)
|
||||
elif values["-ADD-SOURCE-MENU-"] == _t("wizard_gui.add_kvm"):
|
||||
sg.popup("Add KVM virtual machines clicked", keep_on_top=True)
|
||||
if node:
|
||||
icon = TREE_ICON
|
||||
tree = backup_paths_tree
|
||||
# Check if node is ADD-PATH-FILES which can contain multiple elements separated by semicolon
|
||||
if ";" in node:
|
||||
for path in node.split(";"):
|
||||
if tree.tree_dict.get(path):
|
||||
tree.delete(path)
|
||||
tree.insert("", path, path, path, icon=icon)
|
||||
else:
|
||||
if tree.tree_dict.get(node):
|
||||
tree.delete(node)
|
||||
tree.insert("", node, node, node, icon=icon)
|
||||
wizard["backup_opts.paths"].update(values=tree)
|
||||
wizard.close()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ ofunctions.mailer>=1.3.0
|
|||
# keep in mind that freesimplegui might higher required python version in the future
|
||||
freesimplegui==5.2.0.post1
|
||||
reskinner==4.0.0
|
||||
sv_ttk
|
||||
pillow
|
||||
requests
|
||||
ruamel.yaml
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ en:
|
|||
bad_file: Bad file
|
||||
file_does_not_exist: File does not exist
|
||||
|
||||
add: Add
|
||||
add_files: Add files
|
||||
add_folder: Add folder
|
||||
add_manually: Add manually
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ fr:
|
|||
bad_file: Fichier erroné
|
||||
file_does_not_exist: Fichier inexistant
|
||||
|
||||
add: Ajouter
|
||||
add_files: Ajouter fichiers
|
||||
add_folder: Ajouter dossier
|
||||
add_manually: Ajouter manuellement
|
||||
|
|
|
|||
|
|
@ -17,5 +17,12 @@ en:
|
|||
step_4: Review summary
|
||||
step_5: Finalize setup
|
||||
|
||||
add_system: Add Windows system
|
||||
add_hyper_v: Add Hyper-V virtual machines
|
||||
add_kvm: Add KVM virtual machines
|
||||
|
||||
retention_gfs: GFS Retention
|
||||
retention_30_days: 30 Days Retention
|
||||
retention_14_days: 14 Days Retention
|
||||
retention_keep_all: Keep All Backups
|
||||
|
||||
|
|
|
|||
|
|
@ -17,4 +17,11 @@ fr:
|
|||
step_4: Revoir le résumé
|
||||
step_5: Finaliser la configuration
|
||||
|
||||
add_system: Ajouter un système Windows
|
||||
add_hyper_v: Ajouter des machines virtuelles Hyper-V
|
||||
add_kvm: Ajouter des machines virtuelles KVM
|
||||
|
||||
retention_gfs: Rétention GFS
|
||||
retention_30_days: Rétention de 30 jours
|
||||
retention_14_days: Rétention de 14 jours
|
||||
retention_keep_all: Conserver toutes les sauvegardes
|
||||
Loading…
Add table
Reference in a new issue