From ff254db92597e719db3dedacf15dd8638f0f0dd1 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Mon, 11 Apr 2022 15:09:18 -0300 Subject: [PATCH] [api] improvement: allowing several settings views to be returned (SoftwareManager.get_settings) --- bauh/api/abstract/controller.py | 33 +++++-- bauh/api/abstract/view.py | 16 +++- bauh/gems/appimage/controller.py | 14 +-- bauh/gems/arch/controller.py | 41 +++++--- bauh/gems/debian/controller.py | 33 ++++--- bauh/gems/flatpak/controller.py | 16 ++-- bauh/gems/snap/controller.py | 17 ++-- bauh/gems/web/controller.py | 19 ++-- bauh/view/core/controller.py | 12 +-- bauh/view/core/settings.py | 159 ++++++++++++++++--------------- bauh/view/qt/settings.py | 4 +- 11 files changed, 206 insertions(+), 158 deletions(-) diff --git a/bauh/api/abstract/controller.py b/bauh/api/abstract/controller.py index ee810179..dde6334e 100644 --- a/bauh/api/abstract/controller.py +++ b/bauh/api/abstract/controller.py @@ -119,6 +119,29 @@ class SoftwareAction(Enum): DOWNGRADE = 5 +class SettingsController(ABC): + + def save_settings(self, component: ViewComponent) -> Tuple[bool, Optional[List[str]]]: + """ + :return: a tuple with a bool informing if the settings were saved and a list of error messages + """ + pass + + +class SettingsView: + + def __init__(self, controller: SettingsController, component: ViewComponent, label: Optional[str] = None, + icon_path: Optional[str] = None): + + self.controller = controller + self.component = component + self.label = label + self.icon_path = icon_path + + def save(self) -> Tuple[bool, Optional[List[str]]]: + return self.controller.save_settings(self.component) + + class SoftwareManager(ABC): """ @@ -379,15 +402,9 @@ class SoftwareManager(ABC): """ pass - def get_settings(self) -> Optional[ViewComponent]: + def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: """ - :return: a form abstraction with all available settings - """ - pass - - def save_settings(self, component: ViewComponent) -> Tuple[bool, Optional[List[str]]]: - """ - :return: a tuple with a bool informing if the settings were saved and a list of error messages + :return: panel abstractions with optional icon paths associated with """ pass diff --git a/bauh/api/abstract/view.py b/bauh/api/abstract/view.py index f476606e..50781c82 100644 --- a/bauh/api/abstract/view.py +++ b/bauh/api/abstract/view.py @@ -1,6 +1,6 @@ from abc import ABC from enum import Enum -from typing import List, Set, Optional, Dict +from typing import List, Set, Optional, Dict, TypeVar, Type class MessageType(Enum): @@ -27,6 +27,9 @@ class ViewComponent(ABC): self.observers.append(obs) +V = TypeVar('V', bound=ViewComponent) + + class SpacerComponent(ViewComponent): def __init__(self): @@ -35,7 +38,7 @@ class SpacerComponent(ViewComponent): class InputViewComponent(ViewComponent): """ - Represents an component which needs a user interaction to provide its value + Represents a component which needs a user interaction to provide its value """ @@ -317,6 +320,15 @@ class PanelComponent(ViewComponent): if self.component_map: return self.component_map.get(id_) + def get_component_by_idx(self, idx: int, type_: Type[V]) -> Optional[V]: + if self.components and idx < len(self.components): + c = self.components[idx] + if isinstance(c, type_): + return c + + raise Exception(f"The {ViewComponent.__class__.__name__} at index {idx} is not an " + f"instance of '{type_.__name__}'") + def get_form_component(self, id_: str) -> Optional[FormComponent]: comp = self.get_component(id_) diff --git a/bauh/gems/appimage/controller.py b/bauh/gems/appimage/controller.py index ed0b140d..c84e85a6 100644 --- a/bauh/gems/appimage/controller.py +++ b/bauh/gems/appimage/controller.py @@ -12,17 +12,17 @@ from pathlib import Path from typing import Set, Type, List, Tuple, Optional, Iterable, Generator from colorama import Fore -from packaging.version import parse as parse_version from packaging.version import LegacyVersion +from packaging.version import parse as parse_version from bauh.api.abstract.context import ApplicationContext from bauh.api.abstract.controller import SoftwareManager, SearchResult, UpgradeRequirements, UpgradeRequirement, \ - TransactionResult, SoftwareAction + TransactionResult, SoftwareAction, SettingsView, SettingsController from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion, \ SuggestionPriority, CustomSoftwareAction -from bauh.api.abstract.view import MessageType, ViewComponent, FormComponent, InputOption, SingleSelectComponent, \ +from bauh.api.abstract.view import MessageType, FormComponent, InputOption, SingleSelectComponent, \ SelectViewType, TextInputComponent, PanelComponent, FileChooserComponent, ViewObserver from bauh.api.paths import DESKTOP_ENTRIES_DIR from bauh.commons import resource @@ -67,7 +67,7 @@ class ManualInstallationFileObserver(ViewObserver): self.version.set_value(None) -class AppImageManager(SoftwareManager): +class AppImageManager(SoftwareManager, SettingsController): def __init__(self, context: ApplicationContext): super(AppImageManager, self).__init__(context=context) @@ -850,7 +850,7 @@ class AppImageManager(SoftwareManager): print(f'{Fore.RED}[bauh][appimage] An exception has happened when deleting {f}{Fore.RESET}') traceback.print_exc() - def get_settings(self) -> Optional[ViewComponent]: + def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: appimage_config = self.configman.get_config() max_width = floor(self.context.screen_width * 0.15) @@ -871,12 +871,12 @@ class AppImageManager(SoftwareManager): id_='appim_sugs_exp') ] - return PanelComponent([FormComponent(components=comps, id_='form')]) + yield SettingsView(self, PanelComponent([FormComponent(components=comps)])) def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: appimage_config = self.configman.get_config() - form = component.get_form_component('form') + form = component.get_component_by_idx(0, FormComponent) appimage_config['database']['expiration'] = form.get_text_input('appim_db_exp').get_int_value() appimage_config['suggestions']['expiration'] = form.get_text_input('appim_sugs_exp').get_int_value() diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index 748d7e5d..b14fb547 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -18,7 +18,7 @@ from dateutil.parser import parse as parse_date from bauh import __app_name__ from bauh.api.abstract.controller import SearchResult, SoftwareManager, ApplicationContext, UpgradeRequirements, \ - TransactionResult, SoftwareAction + TransactionResult, SoftwareAction, SettingsView, SettingsController from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import PackageUpdate, PackageHistory, SoftwarePackage, PackageSuggestion, PackageStatus, \ @@ -194,7 +194,7 @@ class TransactionContext: self.previous_change_progress = self.change_progress -class ArchManager(SoftwareManager): +class ArchManager(SoftwareManager, SettingsController): def __init__(self, context: ApplicationContext, disk_cache_updater: Optional[ArchDiskCacheUpdater] = None): super(ArchManager, self).__init__(context=context) @@ -2860,7 +2860,7 @@ class ArchManager(SoftwareManager): id_=id_, capitalize_label=capitalize_label) - def get_settings(self) -> Optional[ViewComponent]: + def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: arch_config = self.configman.get_config() max_width = floor(self.context.screen_width * 0.25) @@ -3013,33 +3013,46 @@ class ArchManager(SoftwareManager): value=arch_config['categories_exp'] if isinstance(arch_config['categories_exp'], int) else ''), ] - return PanelComponent([FormComponent(fields, spaces=False, id_='root')]) + yield SettingsView(self, PanelComponent([FormComponent(fields, spaces=False)])) def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: arch_config = self.configman.get_config() - form = component.get_form_component('root') + form = component.get_component_by_idx(0, FormComponent) arch_config['repositories'] = form.get_single_select_component('repos').get_selected() arch_config['optimize'] = form.get_single_select_component('opts').get_selected() arch_config['aur_rebuild_detector'] = form.get_single_select_component('rebuild_detector').get_selected() - arch_config['aur_rebuild_detector_no_bin'] = form.get_single_select_component('rebuild_detector_no_bin').get_selected() + + rebuild_no_bin = form.get_single_select_component('rebuild_detector_no_bin').get_selected() + arch_config['aur_rebuild_detector_no_bin'] = rebuild_no_bin + arch_config['sync_databases'] = form.get_single_select_component('sync_dbs').get_selected() arch_config['sync_databases_startup'] = form.get_single_select_component('sync_dbs_start').get_selected() arch_config['clean_cached'] = form.get_single_select_component('clean_cached').get_selected() arch_config['refresh_mirrors_startup'] = form.get_single_select_component('ref_mirs').get_selected() - arch_config['mirrors_sort_limit'] = form.get_component('mirrors_sort_limit').get_int_value() - arch_config['repositories_mthread_download'] = form.get_component('mthread_download').get_selected() + arch_config['mirrors_sort_limit'] = form.get_text_input('mirrors_sort_limit').get_int_value() + arch_config['repositories_mthread_download'] = form.get_single_select_component('mthread_download').get_selected() arch_config['automatch_providers'] = form.get_single_select_component('autoprovs').get_selected() - arch_config['prefer_repository_provider'] = form.get_single_select_component('prefer_repo_provider').get_selected() + + prefer_repo_provider = form.get_single_select_component('prefer_repo_provider').get_selected() + arch_config['prefer_repository_provider'] = prefer_repo_provider + arch_config['edit_aur_pkgbuild'] = form.get_single_select_component('edit_aur_pkgbuild').get_selected() arch_config['aur_remove_build_dir'] = form.get_single_select_component('aur_remove_build_dir').get_selected() arch_config['aur_build_dir'] = form.get_component('aur_build_dir').file_path arch_config['aur_build_only_chosen'] = form.get_single_select_component('aur_build_only_chosen').get_selected() - arch_config['aur_idx_exp'] = form.get_component('aur_idx_exp').get_int_value() - arch_config['check_dependency_breakage'] = form.get_single_select_component('check_dependency_breakage').get_selected() - arch_config['suggest_optdep_uninstall'] = form.get_single_select_component('suggest_optdep_uninstall').get_selected() - arch_config['suggest_unneeded_uninstall'] = form.get_single_select_component('suggest_unneeded_uninstall').get_selected() - arch_config['categories_exp'] = form.get_component('arch_cats_exp').get_int_value() + arch_config['aur_idx_exp'] = form.get_text_input('aur_idx_exp').get_int_value() + + check_dep_break = form.get_single_select_component('check_dependency_breakage').get_selected() + arch_config['check_dependency_breakage'] = check_dep_break + + sug_opt_dep_uni = form.get_single_select_component('suggest_optdep_uninstall').get_selected() + arch_config['suggest_optdep_uninstall'] = sug_opt_dep_uni + + sug_unneeded_uni = form.get_single_select_component('suggest_unneeded_uninstall').get_selected() + arch_config['suggest_unneeded_uninstall'] = sug_unneeded_uni + + arch_config['categories_exp'] = form.get_text_input('arch_cats_exp').get_int_value() if not arch_config['aur_build_dir']: arch_config['aur_build_dir'] = None diff --git a/bauh/gems/debian/controller.py b/bauh/gems/debian/controller.py index c5e8d213..4df4ea0e 100644 --- a/bauh/gems/debian/controller.py +++ b/bauh/gems/debian/controller.py @@ -10,12 +10,12 @@ from typing import List, Optional, Tuple, Set, Type, Dict, Iterable, Generator from bauh.api.abstract.context import ApplicationContext from bauh.api.abstract.controller import SoftwareManager, SoftwareAction, TransactionResult, UpgradeRequirements, \ - SearchResult, UpgradeRequirement + SearchResult, UpgradeRequirement, SettingsView, SettingsController from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import TaskManager, ProcessWatcher from bauh.api.abstract.model import SoftwarePackage, PackageSuggestion, PackageUpdate, PackageHistory, \ CustomSoftwareAction -from bauh.api.abstract.view import ViewComponent, TextInputComponent, PanelComponent, FormComponent, MessageType, \ +from bauh.api.abstract.view import TextInputComponent, PanelComponent, FormComponent, MessageType, \ SingleSelectComponent, InputOption, SelectViewType from bauh.api.paths import CONFIG_DIR from bauh.commons.html import bold @@ -33,7 +33,7 @@ from bauh.gems.debian.suggestions import DebianSuggestionsDownloader from bauh.gems.debian.tasks import UpdateApplicationIndex, MapApplications, SynchronizePackages -class DebianPackageManager(SoftwareManager): +class DebianPackageManager(SoftwareManager, SettingsController): def __init__(self, context: ApplicationContext): super(DebianPackageManager, self).__init__(context) @@ -568,7 +568,7 @@ class DebianPackageManager(SoftwareManager): final_cmd = pkg.app.exe_path.replace('%U', '') Popen(final_cmd, shell=True) - def get_settings(self) -> Optional[ViewComponent]: + def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: deb_config = self.configman.get_config() comps_width = int(self.context.screen_width * 0.105) @@ -632,24 +632,23 @@ class DebianPackageManager(SoftwareManager): value=str(suggestions_exp), only_int=True, max_width=comps_width) - return PanelComponent([FormComponent([input_sources, ti_sync_pkgs, ti_index_apps_exp, ti_suggestions_exp])]) + panel = PanelComponent([FormComponent([input_sources, ti_sync_pkgs, ti_index_apps_exp, ti_suggestions_exp])]) + yield SettingsView(self, panel) def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: deb_config = self.configman.get_config() - container = component.components[0] + container = component.get_component_by_idx(0, FormComponent) + deb_config['pkg_sources.app'] = container.get_single_select_component('pkg_sources.app').get_selected() + deb_config['index_apps.exp'] = container.get_text_input('index_apps.exp').get_int_value() + deb_config['sync_pkgs.time'] = container.get_text_input('sync_pkgs.time').get_int_value() + deb_config['suggestions.exp'] = container.get_text_input('suggestions.exp').get_int_value() - if isinstance(container, FormComponent): - deb_config['pkg_sources.app'] = container.get_single_select_component('pkg_sources.app').get_selected() - deb_config['index_apps.exp'] = container.get_text_input('index_apps.exp').get_int_value() - deb_config['sync_pkgs.time'] = container.get_text_input('sync_pkgs.time').get_int_value() - deb_config['suggestions.exp'] = container.get_text_input('suggestions.exp').get_int_value() - - try: - self.configman.save_config(deb_config) - return True, None - except: - return False, [traceback.format_exc()] + try: + self.configman.save_config(deb_config) + return True, None + except: + return False, [traceback.format_exc()] def gen_custom_actions(self) -> Generator[CustomSoftwareAction, None, None]: if self._default_actions is None: diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py index 9bdd557d..09351329 100644 --- a/bauh/gems/flatpak/controller.py +++ b/bauh/gems/flatpak/controller.py @@ -11,13 +11,13 @@ from packaging.version import Version from bauh.api import user from bauh.api.abstract.controller import SearchResult, SoftwareManager, ApplicationContext, UpgradeRequirements, \ - UpgradeRequirement, TransactionResult, SoftwareAction + UpgradeRequirement, TransactionResult, SoftwareAction, SettingsView, SettingsController from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePackage, PackageSuggestion, \ SuggestionPriority, PackageStatus, CustomSoftwareAction from bauh.api.abstract.view import MessageType, FormComponent, SingleSelectComponent, InputOption, SelectViewType, \ - ViewComponent, PanelComponent + PanelComponent from bauh.commons.boot import CreateConfigFile from bauh.commons.html import strip_html, bold from bauh.commons.system import ProcessHandler @@ -33,7 +33,7 @@ DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.000Z' RE_INSTALL_REFS = re.compile(r'\d+\)\s+(.+)') -class FlatpakManager(SoftwareManager): +class FlatpakManager(SoftwareManager, SettingsController): def __init__(self, context: ApplicationContext): super(FlatpakManager, self).__init__(context=context) @@ -603,7 +603,7 @@ class FlatpakManager(SoftwareManager): else: traceback.print_exc() - def get_settings(self) -> Optional[ViewComponent]: + def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: if not self.context.root_user: fields = [] @@ -623,13 +623,15 @@ class FlatpakManager(SoftwareManager): default_option=[o for o in install_opts if o.value == flatpak_config['installation_level']][0], max_per_line=len(install_opts), max_width=floor(self.context.screen_width * 0.22), - type_=SelectViewType.RADIO)) + type_=SelectViewType.RADIO, + id_='install')) - return PanelComponent([FormComponent(fields, self.i18n['installation'].capitalize())]) + yield SettingsView(self, PanelComponent([FormComponent(fields, self.i18n['installation'].capitalize())])) def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: flatpak_config = self.configman.get_config() - flatpak_config['installation_level'] = component.components[0].components[0].get_selected() + form = component.get_component_by_idx(0, FormComponent) + flatpak_config['installation_level'] = form.get_single_select_component('install').get_selected() try: self.configman.save_config(flatpak_config) diff --git a/bauh/gems/snap/controller.py b/bauh/gems/snap/controller.py index 43b9cbe8..232b6221 100644 --- a/bauh/gems/snap/controller.py +++ b/bauh/gems/snap/controller.py @@ -5,7 +5,7 @@ from threading import Thread from typing import List, Set, Type, Optional, Tuple, Generator from bauh.api.abstract.controller import SoftwareManager, SearchResult, ApplicationContext, UpgradeRequirements, \ - TransactionResult, SoftwareAction + TransactionResult, SoftwareAction, SettingsView, SettingsController from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion, \ @@ -27,7 +27,7 @@ from bauh.gems.snap.snapd import SnapdClient RE_AVAILABLE_CHANNELS = re.compile(re.compile(r'(\w+)\s+(snap install.+)')) -class SnapManager(SoftwareManager): +class SnapManager(SoftwareManager, SettingsController): def __init__(self, context: ApplicationContext): super(SnapManager, self).__init__(context=context) @@ -427,7 +427,7 @@ class SnapManager(SoftwareManager): if pkg.screenshots: yield from pkg.screenshots - def get_settings(self) -> Optional[ViewComponent]: + def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: snap_config = self.configman.get_config() max_width = 200 @@ -446,14 +446,15 @@ class SnapManager(SoftwareManager): label=self.i18n['snap.config.categories_exp'], tooltip=self.i18n['snap.config.categories_exp.tip']) - return PanelComponent([FormComponent([install_channel, categories_exp], self.i18n['installation'].capitalize())]) + panel = PanelComponent([FormComponent([install_channel, categories_exp], self.i18n['installation'].capitalize())]) + yield SettingsView(self, panel) - def save_settings(self, component: ViewComponent) -> Tuple[bool, Optional[List[str]]]: + def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: snap_config = self.configman.get_config() - panel = component.components[0] - snap_config['install_channel'] = panel.get_component('snap_install_channel').get_selected() - snap_config['categories_exp'] = panel.get_component('snap_cat_exp').get_int_value() + form = component.get_component_by_idx(0, FormComponent) + snap_config['install_channel'] = form.get_single_select_component('snap_install_channel').get_selected() + snap_config['categories_exp'] = form.get_text_input('snap_cat_exp').get_int_value() try: self.configman.save_config(snap_config) diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py index 6f041314..5113abad 100644 --- a/bauh/gems/web/controller.py +++ b/bauh/gems/web/controller.py @@ -18,7 +18,7 @@ from requests import Response from bauh.api.abstract.context import ApplicationContext from bauh.api.abstract.controller import SoftwareManager, SearchResult, UpgradeRequirements, TransactionResult, \ - SoftwareAction + SoftwareAction, SettingsView, SettingsController from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import SoftwarePackage, CustomSoftwareAction, PackageSuggestion, PackageUpdate, \ @@ -62,7 +62,7 @@ RE_SYMBOLS_SPLIT = re.compile(r'[\-|_\s:.]') DEFAULT_LANGUAGE_HEADER = 'en-US, en' -class WebApplicationManager(SoftwareManager): +class WebApplicationManager(SoftwareManager, SettingsController): def __init__(self, context: ApplicationContext, suggestions_loader: Optional[SuggestionsLoader] = None): super(WebApplicationManager, self).__init__(context=context) @@ -1105,7 +1105,7 @@ class WebApplicationManager(SoftwareManager): print('{}[bauh][web] An exception has happened when deleting {}{}'.format(Fore.RED, ENV_PATH, Fore.RESET)) traceback.print_exc() - def get_settings(self) -> Optional[ViewComponent]: + def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: web_config = self.configman.get_config() max_width = floor(self.context.screen_width * 0.15) @@ -1149,26 +1149,27 @@ class WebApplicationManager(SoftwareManager): form_env = FormComponent(label=self.i18n['web.settings.nativefier.env'].capitalize(), components=[input_electron, select_nativefier, env_settings_exp, sugs_exp]) - return PanelComponent([form_env]) + yield SettingsView(self, PanelComponent([form_env])) def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: web_config = self.configman.get_config() - form_env = component.components[0] + form_env = component.get_component_by_idx(0, FormComponent) - web_config['environment']['electron']['version'] = str(form_env.get_component('electron_branch').get_value()).strip() + electron_version = str(form_env.get_text_input('electron_branch').get_value()).strip() + web_config['environment']['electron']['version'] = electron_version if len(web_config['environment']['electron']['version']) == 0: web_config['environment']['electron']['version'] = None - system_nativefier = form_env.get_component('nativefier').get_selected() + system_nativefier = form_env.get_single_select_component('nativefier').get_selected() if system_nativefier and not nativefier.is_available(): return False, [self.i18n['web.settings.env.nativefier.system.not_installed'].format('Nativefier')] web_config['environment']['system'] = system_nativefier - web_config['environment']['cache_exp'] = form_env.get_component('web_cache_exp').get_int_value() - web_config['suggestions']['cache_exp'] = form_env.get_component('web_sugs_exp').get_int_value() + web_config['environment']['cache_exp'] = form_env.get_text_input('web_cache_exp').get_int_value() + web_config['suggestions']['cache_exp'] = form_env.get_text_input('web_sugs_exp').get_int_value() try: self.configman.save_config(web_config) diff --git a/bauh/view/core/controller.py b/bauh/view/core/controller.py index d78e2ade..44a93cc2 100755 --- a/bauh/view/core/controller.py +++ b/bauh/view/core/controller.py @@ -7,12 +7,12 @@ from threading import Thread from typing import List, Set, Type, Tuple, Dict, Optional, Generator, Callable from bauh.api.abstract.controller import SoftwareManager, SearchResult, ApplicationContext, UpgradeRequirements, \ - UpgradeRequirement, TransactionResult, SoftwareAction + UpgradeRequirement, TransactionResult, SoftwareAction, SettingsView, SettingsController from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import SoftwarePackage, PackageUpdate, PackageHistory, PackageSuggestion, \ CustomSoftwareAction -from bauh.api.abstract.view import ViewComponent, TabGroupComponent, MessageType +from bauh.api.abstract.view import ViewComponent, TabGroupComponent, MessageType, PanelComponent from bauh.api.exception import NoInternetException from bauh.commons.boot import CreateConfigFile from bauh.commons.html import bold @@ -36,7 +36,7 @@ class GenericUpgradeRequirements(UpgradeRequirements): self.sub_requirements = sub_requirements -class GenericSoftwareManager(SoftwareManager): +class GenericSoftwareManager(SoftwareManager, SettingsController): def __init__(self, managers: List[SoftwareManager], context: ApplicationContext, config: dict): super(GenericSoftwareManager, self).__init__(context=context) @@ -532,7 +532,7 @@ class GenericSoftwareManager(SoftwareManager): def get_working_managers(self): return [m for m in self.managers if self._can_work(m)] - def get_settings(self) -> ViewComponent: + def get_settings(self) -> Optional[Generator[SettingsView, None, None]]: if self.settings_manager is None: self.settings_manager = GenericSettingsManager(managers=self.managers, working_managers=self.working_managers, @@ -542,9 +542,9 @@ class GenericSoftwareManager(SoftwareManager): self.settings_manager.managers = self.managers self.settings_manager.working_managers = self.working_managers - return self.settings_manager.get_settings() + yield SettingsView(self, self.settings_manager.get_settings()) - def save_settings(self, component: TabGroupComponent) -> Tuple[bool, List[str]]: + def save_settings(self, component: TabGroupComponent) -> Tuple[bool, Optional[List[str]]]: return self.settings_manager.save_settings(component) def _map_pkgs_by_manager(self, pkgs: List[SoftwarePackage], pkg_filters: list = None) -> Dict[SoftwareManager, List[SoftwarePackage]]: diff --git a/bauh/view/core/settings.py b/bauh/view/core/settings.py index b324860d..193a694e 100644 --- a/bauh/view/core/settings.py +++ b/bauh/view/core/settings.py @@ -1,18 +1,17 @@ -import logging +import os import os import time import traceback from math import floor from threading import Thread -from typing import List, Tuple, Optional, Dict +from typing import List, Tuple, Optional, Dict, Type, Iterable from PyQt5.QtWidgets import QApplication, QStyleFactory from bauh import ROOT_DIR, __app_name__ from bauh.api.abstract.context import ApplicationContext -from bauh.api.abstract.controller import SoftwareManager -from bauh.api.abstract.download import FileDownloader -from bauh.api.abstract.view import ViewComponent, TabComponent, InputOption, TextComponent, MultipleSelectComponent, \ +from bauh.api.abstract.controller import SoftwareManager, SettingsController, SettingsView +from bauh.api.abstract.view import TabComponent, InputOption, TextComponent, MultipleSelectComponent, \ PanelComponent, FormComponent, TabGroupComponent, SingleSelectComponent, SelectViewType, TextInputComponent, \ FileChooserComponent, RangeInputComponent from bauh.commons.view_utils import new_select @@ -20,10 +19,9 @@ from bauh.view.core import timeshift from bauh.view.core.config import CoreConfigManager, BACKUP_REMOVE_METHODS, BACKUP_DEFAULT_REMOVE_METHOD from bauh.view.core.downloader import AdaptableFileDownloader from bauh.view.util import translation -from bauh.view.util.translation import I18n -class GenericSettingsManager: +class GenericSettingsManager(SettingsController): def __init__(self, context: ApplicationContext, managers: List[SoftwareManager], working_managers: List[SoftwareManager], configman: CoreConfigManager): @@ -34,27 +32,35 @@ class GenericSettingsManager: self.logger = context.logger self.file_downloader = self.context.file_downloader self.configman = configman + self._settings_views: Optional[Dict[Type, List[SettingsView]]] = None - def get_settings(self) -> ViewComponent: + def get_settings(self) -> TabGroupComponent: tabs = list() gem_opts, def_gem_opts, gem_tabs = [], set(), [] + self._settings_views = dict() + for man in self.managers: can_work, reason_not_work = man.can_work() modname = man.__module__.split('.')[-2] - icon_path = "{r}/gems/{n}/resources/img/{n}.svg".format(r=ROOT_DIR, n=modname) - man_comp = man.get_settings() if can_work else None - if man_comp: - tab_name = self.i18n.get('gem.{}.label'.format(modname), modname.capitalize()) - gem_tabs.append(TabComponent(label=tab_name, content=man_comp, icon_path=icon_path, id_=modname)) + man_settings = man.get_settings() if can_work else None + if man_settings: + for view in man_settings: + icon_path = view.icon_path if view.icon_path else f"{ROOT_DIR}/gems/{modname}/resources/img/{modname}.svg" + tab_name = view.label if view.label else self.i18n.get(f'gem.{modname}.label', modname.capitalize()) + gem_tabs.append(TabComponent(label=tab_name, content=view.component, icon_path=icon_path)) + + views = self._settings_views.get(man.__class__, list()) + self._settings_views[man.__class__] = views + views.append(view) help_tip = reason_not_work if not can_work and reason_not_work else self.i18n.get(f'gem.{modname}.info') - opt = InputOption(label=self.i18n.get('gem.{}.label'.format(modname), modname.capitalize()), + opt = InputOption(label=self.i18n.get(f'gem.{modname}.label', modname.capitalize()), tooltip=help_tip, value=modname, - icon_path='{r}/gems/{n}/resources/img/{n}.svg'.format(r=ROOT_DIR, n=modname), + icon_path=f'{ROOT_DIR}/gems/{modname}/resources/img/{modname}.svg', read_only=not can_work, extra_properties={'warning': 'true'} if not can_work else None) gem_opts.append(opt) @@ -351,63 +357,64 @@ class GenericSettingsManager: core_config = self.configman.get_config() # general - general_form = general.components[0] + gen_form = general.get_component_by_idx(0, FormComponent) - locale = general_form.get_component('locale').get_selected() + locale = gen_form.get_single_select_component('locale').get_selected() if locale != self.i18n.current_key: core_config['locale'] = locale - core_config['system']['notifications'] = general_form.get_component('sys_notify').get_selected() - core_config['suggestions']['enabled'] = general_form.get_component('sugs_enabled').get_selected() - core_config['store_root_password'] = general_form.get_component('store_pwd').get_selected() + core_config['system']['notifications'] = gen_form.get_single_select_component('sys_notify').get_selected() + core_config['suggestions']['enabled'] = gen_form.get_single_select_component('sugs_enabled').get_selected() + core_config['store_root_password'] = gen_form.get_single_select_component('store_pwd').get_selected() - sugs_by_type = general_form.get_component('sugs_by_type').get_int_value() + sugs_by_type = gen_form.get_text_input('sugs_by_type').get_int_value() core_config['suggestions']['by_type'] = sugs_by_type - core_config['updates']['ask_for_reboot'] = general_form.get_component('ask_for_reboot').get_selected() - core_config['boot']['load_apps'] = general_form.get_component('boot.load_apps').get_selected() + core_config['updates']['ask_for_reboot'] = gen_form.get_single_select_component('ask_for_reboot').get_selected() + core_config['boot']['load_apps'] = gen_form.get_single_select_component('boot.load_apps').get_selected() # advanced - adv_form = advanced.components[0] + adv_form = advanced.get_component_by_idx(0, FormComponent) - download_mthreaded = adv_form.get_component('down_mthread').get_selected() + download_mthreaded = adv_form.get_single_select_component('down_mthread').get_selected() core_config['download']['multithreaded'] = download_mthreaded - mthread_client = adv_form.get_component('mthread_client').get_selected() + mthread_client = adv_form.get_single_select_component('mthread_client').get_selected() core_config['download']['multithreaded_client'] = mthread_client if isinstance(self.file_downloader, AdaptableFileDownloader): self.file_downloader.multithread_client = mthread_client self.file_downloader.multithread_enabled = download_mthreaded - single_dep_check = adv_form.get_component('dep_check').get_selected() + single_dep_check = adv_form.get_single_select_component('dep_check').get_selected() core_config['system']['single_dependency_checking'] = single_dep_check - data_exp = adv_form.get_component('data_exp').get_int_value() + data_exp = adv_form.get_text_input('data_exp').get_int_value() core_config['memory_cache']['data_expiration'] = data_exp - icon_exp = adv_form.get_component('icon_exp').get_int_value() + icon_exp = adv_form.get_text_input('icon_exp').get_int_value() core_config['memory_cache']['icon_expiration'] = icon_exp - core_config['disk']['trim']['after_upgrade'] = adv_form.get_component('trim_after_upgrade').get_selected() + trim_after_upgrade = adv_form.get_single_select_component('trim_after_upgrade').get_selected() + core_config['disk']['trim']['after_upgrade'] = trim_after_upgrade # backup if backup: - bkp_form = backup.components[0] + bkp_form = backup.get_component_by_idx(0, FormComponent) - core_config['backup']['enabled'] = bkp_form.get_component('enabled').get_selected() - core_config['backup']['mode'] = bkp_form.get_component('mode').get_selected() - core_config['backup']['type'] = bkp_form.get_component('type').get_selected() - core_config['backup']['remove_method'] = bkp_form.get_component('remove_method').get_selected() - core_config['backup']['install'] = bkp_form.get_component('install').get_selected() - core_config['backup']['uninstall'] = bkp_form.get_component('uninstall').get_selected() - core_config['backup']['upgrade'] = bkp_form.get_component('upgrade').get_selected() - core_config['backup']['downgrade'] = bkp_form.get_component('downgrade').get_selected() + core_config['backup']['enabled'] = bkp_form.get_single_select_component('enabled').get_selected() + core_config['backup']['mode'] = bkp_form.get_single_select_component('mode').get_selected() + core_config['backup']['type'] = bkp_form.get_single_select_component('type').get_selected() + core_config['backup']['remove_method'] = bkp_form.get_single_select_component('remove_method').get_selected() + core_config['backup']['install'] = bkp_form.get_single_select_component('install').get_selected() + core_config['backup']['uninstall'] = bkp_form.get_single_select_component('uninstall').get_selected() + core_config['backup']['upgrade'] = bkp_form.get_single_select_component('upgrade').get_selected() + core_config['backup']['downgrade'] = bkp_form.get_single_select_component('downgrade').get_selected() # tray - tray_form = tray.components[0] - core_config['updates']['check_interval'] = tray_form.get_component('updates_interval').get_int_value() + tray_form = tray.get_component_by_idx(0, FormComponent) + core_config['updates']['check_interval'] = tray_form.get_text_input('updates_interval').get_int_value() def_icon_path = tray_form.get_component('def_icon').file_path core_config['ui']['tray']['default_icon'] = def_icon_path if def_icon_path else None @@ -416,30 +423,30 @@ class GenericSettingsManager: core_config['ui']['tray']['updates_icon'] = up_icon_path if up_icon_path else None # ui - ui_form = ui.components[0] + ui_form = ui.get_component_by_idx(0, FormComponent) - core_config['download']['icons'] = ui_form.get_component('down_icons').get_selected() - core_config['ui']['hdpi'] = ui_form.get_component('hdpi').get_selected() + core_config['download']['icons'] = ui_form.get_single_select_component('down_icons').get_selected() + core_config['ui']['hdpi'] = ui_form.get_single_select_component('hdpi').get_selected() previous_autoscale = core_config['ui']['auto_scale'] - core_config['ui']['auto_scale'] = ui_form.get_component('auto_scale').get_selected() + core_config['ui']['auto_scale'] = ui_form.get_single_select_component('auto_scale').get_selected() if previous_autoscale and not core_config['ui']['auto_scale']: self.logger.info("Deleting environment variable QT_AUTO_SCREEN_SCALE_FACTOR") del os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] core_config['ui']['scale_factor'] = ui_form.get_component('scalef').value / 100 - core_config['ui']['table']['max_displayed'] = ui_form.get_component('table_max').get_int_value() + core_config['ui']['table']['max_displayed'] = ui_form.get_text_input('table_max').get_int_value() - style = ui_form.get_component('style').get_selected() + style = ui_form.get_single_select_component('style').get_selected() cur_style = core_config['ui']['qt_style'] if core_config['ui']['qt_style'] else QApplication.instance().property('qt_style') if style != cur_style: core_config['ui']['qt_style'] = style QApplication.instance().setProperty('qt_style', style) - core_config['ui']['system_theme'] = ui_form.get_component('system_theme').get_selected() + core_config['ui']['system_theme'] = ui_form.get_single_select_component('system_theme').get_selected() # gems checked_gems = gems_panel.components[1].get_component('gems').get_selected_values() @@ -457,24 +464,26 @@ class GenericSettingsManager: except: return False, [traceback.format_exc()] - def _save_manager_settings(self, man: SoftwareManager, panel: ViewComponent, success_map: Dict[str, bool], warnings: List[str]): + def _save_views(self, views: Iterable[SettingsView], success_list: List[bool], warnings: List[str]): success = False - try: - res = man.save_settings(panel) + for view in views: + try: + res = view.save() - if res: - success, errors = res[0], res[1] + if res: + success, errors = res[0], res[1] - if errors: - warnings.extend(errors) - except: - self.logger.error("An exception happened while {} was trying to save its settings".format(man.__class__.__name__)) - traceback.print_exc() - finally: - success_map[man.__class__.__name__] = success + if errors: + warnings.extend(errors) + except: + self.logger.error(f"An exception happened while {view.controller.__class__.__name__}" + f" was trying to save settings") + traceback.print_exc() + finally: + success_list.append(success) - def _save_core_settings(self, root_component: TabGroupComponent, success_map: Dict[str, bool], warnings: List[str]): + def _save_core_settings(self, root_component: TabGroupComponent, success_list: List[bool], warnings: List[str]): success = False try: @@ -492,35 +501,29 @@ class GenericSettingsManager: self.logger.error("An exception happened while saving the core settings") traceback.print_exc() finally: - success_map[self.__class__.__name__] = success + success_list.append(success) - def save_settings(self, component: TabGroupComponent) -> Tuple[bool, List[str]]: + def save_settings(self, component: TabGroupComponent) -> Tuple[bool, Optional[List[str]]]: ti = time.time() - save_threads, warnings, success_map = [], [], {} + save_threads, warnings, success_list = [], [], [] - save_core = Thread(target=self._save_core_settings, args=(component, success_map, warnings), daemon=True) + save_core = Thread(target=self._save_core_settings, args=(component, success_list, warnings)) save_core.start() save_threads.append(save_core) - for man in self.managers: - if man: - modname = man.__module__.split('.')[-2] - tab = component.get_tab(modname) + if self._settings_views: - if not tab: - self.logger.warning("Tab for {} was not found".format(man.__class__.__name__)) - else: - save_man = Thread(target=self._save_manager_settings(man, tab.content, success_map, warnings), daemon=True) - save_man.start() - save_threads.append(save_man) + for views in self._settings_views.values(): + save_view = Thread(target=self._save_views, args=(views, success_list, warnings)) + save_view.start() + save_threads.append(save_view) for t in save_threads: t.join() - success = all(success_map.values()) - + success = all(success_list) tf = time.time() - self.logger.info("Saving all settings took {0:.8f} seconds".format(tf - ti)) + self.logger.info(f"Saving all settings took {tf - ti:.8f} seconds") return success, warnings def _gen_backup_settings(self, core_config: dict) -> TabComponent: diff --git a/bauh/view/qt/settings.py b/bauh/view/qt/settings.py index 7d82bfae..bb7bf0df 100644 --- a/bauh/view/qt/settings.py +++ b/bauh/view/qt/settings.py @@ -2,7 +2,7 @@ import gc from io import StringIO from typing import Optional -from PyQt5.QtCore import QSize, Qt, QCoreApplication, QThread, pyqtSignal +from PyQt5.QtCore import Qt, QCoreApplication, QThread, pyqtSignal from PyQt5.QtGui import QCursor from PyQt5.QtWidgets import QWidget, QVBoxLayout, QSizePolicy, QPushButton, QHBoxLayout, QApplication @@ -45,7 +45,7 @@ class SettingsWindow(QWidget): self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) self.window = window - self.settings_model = self.manager.get_settings() + self.settings_model = tuple(v for v in self.manager.get_settings())[0].component self.tab_group = to_widget(self.settings_model, i18n) self.tab_group.setObjectName('settings')