From a7d758c85d810108cf2c23ec10f15fa74d2764f7 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Tue, 31 Dec 2024 17:25:38 +0100 Subject: [PATCH 1/3] PICARD-3019: Move setting_changed signal to ConfigSection class --- picard/config.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/picard/config.py b/picard/config.py index fbc6e4c6d3..a321741752 100644 --- a/picard/config.py +++ b/picard/config.py @@ -59,6 +59,9 @@ class ConfigSection(QtCore.QObject): """Configuration section.""" + # Signal emitted when the value of a setting has changed. + setting_changed = QtCore.pyqtSignal(str, object, object) + def __init__(self, config, name): super().__init__() self.__qt_config = config @@ -76,9 +79,12 @@ def __getitem__(self, name): return self.value(name, opt, opt.default) def __setitem__(self, name, value): + old_value = self.__getitem__(name) key = self.key(name) self.__qt_config.setValue(key, value) self._memoization[key].dirty = True + if value != old_value: + self.setting_changed.emit(name, old_value, value) def __contains__(self, name): return self.__qt_config.contains(self.key(name)) @@ -136,9 +142,6 @@ class SettingConfigSection(ConfigSection): PROFILES_KEY = 'user_profiles' SETTINGS_KEY = 'user_profile_settings' - # Signal emitted when the value of a setting has changed. - setting_changed = QtCore.pyqtSignal(str, object, object) - @classmethod def init_profile_options(cls): ListOption.add_if_missing('profiles', cls.PROFILES_KEY, []) From c688d1e693f343f228c1b8a82160b1d2ab03d393 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Tue, 31 Dec 2024 17:27:19 +0100 Subject: [PATCH 2/3] PICARD-3019: Rebuild options menu on setting profile changes --- picard/ui/mainwindow/__init__.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/picard/ui/mainwindow/__init__.py b/picard/ui/mainwindow/__init__.py index d8c1545ad3..a35e244c94 100644 --- a/picard/ui/mainwindow/__init__.py +++ b/picard/ui/mainwindow/__init__.py @@ -256,6 +256,7 @@ def setupUi(self): function(self) get_config().setting.setting_changed.connect(self.handle_settings_changed) + get_config().profiles.setting_changed.connect(self.handle_profiles_changed) def handle_settings_changed(self, name, old_value, new_value): if name == 'rename_files': @@ -267,6 +268,10 @@ def handle_settings_changed(self, name, old_value, new_value): elif name in {'file_renaming_scripts', 'selected_file_naming_script_id'}: self._make_script_selector_menu() + def handle_profiles_changed(self, name, old_value, new_value): + if name == SettingConfigSection.PROFILES_KEY: + self._make_profile_selector_menu() + def set_processing(self, processing=True): self.panel.set_processing(processing) @@ -497,6 +502,9 @@ def _create_cd_lookup_menu(self): menu.setIcon(icontheme.lookup('media-optical')) menu.triggered.connect(self.tagger.lookup_cd) self.cd_lookup_menu = menu + self._init_cd_lookup_menu() + + def _init_cd_lookup_menu(self): if discid is None: log.warning("CDROM: discid library not found - Lookup CD functionality disabled") self.enable_action(MainAction.CD_LOOKUP, False) @@ -566,6 +574,14 @@ def toggle_tag_saving(self, checked): config = get_config() config.setting['dont_write_tags'] = not checked + def _reset_option_menu_state(self): + config = get_config() + self.actions[MainAction.ENABLE_RENAMING].setChecked(config.setting['rename_files']) + self.actions[MainAction.ENABLE_MOVING].setChecked(config.setting['move_files']) + self.actions[MainAction.ENABLE_TAG_SAVING].setChecked(not config.setting['dont_write_tags']) + self._make_script_selector_menu() + self._init_cd_lookup_menu() + def _get_selected_or_unmatched_files(self): if self.selected_objects: files = list(iter_files_from_objects(self.selected_objects)) @@ -1589,7 +1605,8 @@ def _update_profile_selection(self, profile_id): for profile in option_profiles: if profile['id'] == profile_id: profile['enabled'] = not profile['enabled'] - self._make_script_selector_menu() + config.profiles[SettingConfigSection.PROFILES_KEY] = option_profiles + self._reset_option_menu_state() return def show_new_user_dialog(self): From 71ca7afd7f850536a2fc11d0a8bb4c8657e957b6 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Wed, 1 Jan 2025 12:47:38 +0100 Subject: [PATCH 3/3] Ensure options are registered in profiles on startup Move option dialog setting registration out of constructors. Fixes option profiles only working after options dialog has been opened. --- picard/extension_points/options_pages.py | 4 ++- picard/options.py | 12 +++---- picard/ui/options/__init__.py | 13 +++---- picard/ui/options/advanced.py | 24 +++++++------ picard/ui/options/cdlookup.py | 6 ++-- picard/ui/options/cover.py | 28 ++++++++------- picard/ui/options/cover_processing.py | 37 +++++++++++--------- picard/ui/options/fingerprinting.py | 16 +++++---- picard/ui/options/general.py | 24 +++++++------ picard/ui/options/genres.py | 20 ++++++----- picard/ui/options/interface.py | 30 ++++++++-------- picard/ui/options/interface_colors.py | 8 +++-- picard/ui/options/interface_toolbar.py | 7 ++-- picard/ui/options/interface_top_tags.py | 6 ++-- picard/ui/options/maintenance.py | 6 ++-- picard/ui/options/matching.py | 10 +++--- picard/ui/options/metadata.py | 28 ++++++++------- picard/ui/options/network.py | 26 +++++++------- picard/ui/options/ratings.py | 10 +++--- picard/ui/options/releases.py | 10 +++--- picard/ui/options/renaming.py | 18 +++++----- picard/ui/options/renaming_compat.py | 16 +++++---- picard/ui/options/scripting.py | 8 +++-- picard/ui/options/tags.py | 20 ++++++----- picard/ui/options/tags_compatibility_aac.py | 8 +++-- picard/ui/options/tags_compatibility_ac3.py | 8 +++-- picard/ui/options/tags_compatibility_id3.py | 14 ++++---- picard/ui/options/tags_compatibility_wave.py | 10 +++--- 28 files changed, 240 insertions(+), 187 deletions(-) diff --git a/picard/extension_points/options_pages.py b/picard/extension_points/options_pages.py index f1231c0a24..d9f966299f 100644 --- a/picard/extension_points/options_pages.py +++ b/picard/extension_points/options_pages.py @@ -4,7 +4,7 @@ # # Copyright (C) 2006-2007 Lukáš Lalinský # Copyright (C) 2009 Nikolai Prokoschenko -# Copyright (C) 2009, 2019-2022 Philipp Wolfer +# Copyright (C) 2009, 2019-2022, 2025 Philipp Wolfer # Copyright (C) 2013, 2015, 2018-2024 Laurent Monin # Copyright (C) 2016-2017 Sambhav Kothari # @@ -30,3 +30,5 @@ def register_options_page(page_class): ext_point_options_pages.register(page_class.__module__, page_class) + for opt_name, opt_highlights in page_class.OPTIONS: + page_class.register_setting(opt_name, opt_highlights) diff --git a/picard/options.py b/picard/options.py index 710e95ca0a..55d9add5d7 100644 --- a/picard/options.py +++ b/picard/options.py @@ -80,17 +80,15 @@ # The translated title will be displayed in Profiles option page. # # 2. If the option is a 'setting' which is edited in one of the option pages, -# then the option can be registered in the `__init__()` method of the -# matching `OptionPage` declaration with a call to the page's -# `register_setting()` method. +# then the option must be added to the OPTIONS tuple in the class. The +# first parameter is the option name, the second is a list of UI elements +# to highlight if the option is part of an option profile. If the setting +# can be overridden in profiles, the `highlights` has to be a list of +# widget names associated with the option. # # Registering a setting allows it to be reset to the default when the user # asks for it on the corresponding option page. # -# If the setting can be overriden in profiles, the `highlights` parameter -# has to be set a list of widget names associated with the option. -# -# # Please, try to keep options ordered by section and name in their own group. diff --git a/picard/ui/options/__init__.py b/picard/ui/options/__init__.py index 4551dd3c2e..031e3e4515 100644 --- a/picard/ui/options/__init__.py +++ b/picard/ui/options/__init__.py @@ -4,7 +4,7 @@ # # Copyright (C) 2006-2007 Lukáš Lalinský # Copyright (C) 2009 Nikolai Prokoschenko -# Copyright (C) 2009, 2019-2022 Philipp Wolfer +# Copyright (C) 2009, 2019-2022, 2025 Philipp Wolfer # Copyright (C) 2013, 2015, 2018-2024 Laurent Monin # Copyright (C) 2016-2017 Sambhav Kothari # @@ -53,7 +53,9 @@ class OptionsPage(QtWidgets.QWidget): HELP_URL = None STYLESHEET_ERROR = "QWidget { background-color: #f55; color: white; font-weight:bold; padding: 2px; }" STYLESHEET = "QLabel { qproperty-wordWrap: true; }" + OPTIONS = () + _registered_settings = [] initialized = False loaded = False @@ -72,8 +74,6 @@ def on_destroyed(obj=None): self.deleted = True self.destroyed.connect(on_destroyed) - self._registered_settings = [] - def set_dialog(self, dialog): self.dialog = dialog @@ -131,12 +131,13 @@ def live_checker(text): regex_edit.textChanged.connect(live_checker) - def register_setting(self, name, highlights=None): + @classmethod + def register_setting(cls, name, highlights=None): """Register a setting edited in the page, used to restore defaults and to highlight when profiles are used""" option = Option.get('setting', name) if option is None: raise Exception(f"Cannot register setting for non-existing option {name}") - self._registered_settings.append(option) + OptionsPage._registered_settings.append(option) if highlights is not None: - profile_groups_add_setting(self.NAME, name, tuple(highlights), title=self.TITLE) + profile_groups_add_setting(cls.NAME, name, tuple(highlights), title=cls.TITLE) diff --git a/picard/ui/options/advanced.py b/picard/ui/options/advanced.py index d3c0fece8b..8986df059f 100644 --- a/picard/ui/options/advanced.py +++ b/picard/ui/options/advanced.py @@ -39,23 +39,25 @@ class AdvancedOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_advanced.html" + OPTIONS = ( + ('ignore_regex', ['ignore_regex']), + ('ignore_hidden_files', ['ignore_hidden_files']), + ('recursively_add_files', ['recursively_add_files']), + ('ignore_track_duration_difference_under', ['ignore_track_duration_difference_under', 'label_track_duration_diff']), + ('query_limit', ['query_limit', 'label_query_limit']), + ('completeness_ignore_videos', ['completeness_ignore_videos']), + ('completeness_ignore_pregap', ['completeness_ignore_pregap']), + ('completeness_ignore_data', ['completeness_ignore_data']), + ('completeness_ignore_silence', ['completeness_ignore_silence']), + ('compare_ignore_tags', ['groupBox_ignore_tags']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_AdvancedOptionsPage() self.ui.setupUi(self) self.init_regex_checker(self.ui.ignore_regex, self.ui.regex_error) - self.register_setting('ignore_regex', ['ignore_regex']) - self.register_setting('ignore_hidden_files', ['ignore_hidden_files']) - self.register_setting('recursively_add_files', ['recursively_add_files']) - self.register_setting('ignore_track_duration_difference_under', ['ignore_track_duration_difference_under', 'label_track_duration_diff']) - self.register_setting('query_limit', ['query_limit', 'label_query_limit']) - self.register_setting('completeness_ignore_videos', ['completeness_ignore_videos']) - self.register_setting('completeness_ignore_pregap', ['completeness_ignore_pregap']) - self.register_setting('completeness_ignore_data', ['completeness_ignore_data']) - self.register_setting('completeness_ignore_silence', ['completeness_ignore_silence']) - self.register_setting('compare_ignore_tags', ['groupBox_ignore_tags']) - def load(self): config = get_config() self.ui.ignore_regex.setText(config.setting['ignore_regex']) diff --git a/picard/ui/options/cdlookup.py b/picard/ui/options/cdlookup.py index 4d0e74c2e2..7b6b3a5894 100644 --- a/picard/ui/options/cdlookup.py +++ b/picard/ui/options/cdlookup.py @@ -52,6 +52,10 @@ class CDLookupOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_cdlookup.html" + OPTIONS = ( + ('cd_lookup_device', None), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_CDLookupOptionsPage() @@ -60,8 +64,6 @@ def __init__(self, parent=None): self._device_list = get_cdrom_drives() self.ui.cd_lookup_device.addItems(self._device_list) - self.register_setting('cd_lookup_device') - def load(self): config = get_config() device = config.setting['cd_lookup_device'] diff --git a/picard/ui/options/cover.py b/picard/ui/options/cover.py index 6951fce0bd..3e0726fdab 100644 --- a/picard/ui/options/cover.py +++ b/picard/ui/options/cover.py @@ -59,6 +59,21 @@ class CoverOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_cover.html" + OPTIONS = ( + ('save_images_to_tags', ['save_images_to_tags']), + ('embed_only_one_front_image', ['cb_embed_front_only']), + ('dont_replace_with_smaller_cover', ['cb_dont_replace_with_smaller']), + ('dont_replace_cover_of_types', ['cb_never_replace_types']), + ('dont_replace_included_types', ['dont_replace_included_types']), + ('dont_replace_excluded_types', ['dont_replace_excluded_types']), + ('save_images_to_files', ['save_images_to_files']), + ('cover_image_filename', ['cover_image_filename']), + ('save_images_overwrite', ['save_images_overwrite']), + ('save_only_one_front_image', ['save_only_one_front_image']), + ('image_type_as_filename', ['image_type_as_filename']), + ('ca_providers', ['ca_providers_list']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_CoverOptionsPage() @@ -72,19 +87,6 @@ def __init__(self, parent=None): self.move_view = MoveableListView(self.ui.ca_providers_list, self.ui.up_button, self.ui.down_button) - self.register_setting('save_images_to_tags', ['save_images_to_tags']) - self.register_setting('embed_only_one_front_image', ['cb_embed_front_only']) - self.register_setting('dont_replace_with_smaller_cover', ['dont_replace_with_smaller_cover']) - self.register_setting('dont_replace_cover_of_types', ['dont_replace_cover_of_types']) - self.register_setting('dont_replace_included_types', ['dont_replace_included_types']) - self.register_setting('dont_replace_excluded_types', ['dont_replace_excluded_types']) - self.register_setting('save_images_to_files', ['save_images_to_files']) - self.register_setting('cover_image_filename', ['cover_image_filename']) - self.register_setting('save_images_overwrite', ['save_images_overwrite']) - self.register_setting('save_only_one_front_image', ['save_only_one_front_image']) - self.register_setting('image_type_as_filename', ['image_type_as_filename']) - self.register_setting('ca_providers', ['ca_providers_list']) - def restore_defaults(self): # Remove previous entries self.ui.ca_providers_list.clear() diff --git a/picard/ui/options/cover_processing.py b/picard/ui/options/cover_processing.py index b2bd7495f1..94d458cc24 100644 --- a/picard/ui/options/cover_processing.py +++ b/picard/ui/options/cover_processing.py @@ -49,27 +49,30 @@ class CoverProcessingOptionsPage(OptionsPage): PARENT = 'cover' SORT_ORDER = 0 + OPTIONS = ( + ('filter_cover_by_size', None), + ('cover_minimum_width', None), + ('cover_minimum_height', None), + ('cover_tags_enlarge', None), + ('cover_tags_resize', None), + ('cover_tags_resize_target_width', None), + ('cover_tags_resize_target_height', None), + ('cover_tags_resize_mode', None), + ('cover_tags_convert_images', None), + ('cover_tags_convert_to_format', None), + ('cover_file_enlarge', None), + ('cover_file_resize', None), + ('cover_file_resize_target_width', None), + ('cover_file_resize_target_height', None), + ('cover_file_resize_mode', None), + ('cover_file_convert_images', None), + ('cover_file_convert_to_format', None), + ) + def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_CoverProcessingOptionsPage() self.ui.setupUi(self) - self.register_setting('filter_cover_by_size') - self.register_setting('cover_minimum_width') - self.register_setting('cover_minimum_height') - self.register_setting('cover_tags_enlarge') - self.register_setting('cover_tags_resize') - self.register_setting('cover_tags_resize_target_width') - self.register_setting('cover_tags_resize_target_height') - self.register_setting('cover_tags_resize_mode') - self.register_setting('cover_tags_convert_images') - self.register_setting('cover_tags_convert_to_format') - self.register_setting('cover_file_enlarge') - self.register_setting('cover_file_resize') - self.register_setting('cover_file_resize_target_width') - self.register_setting('cover_file_resize_target_height') - self.register_setting('cover_file_resize_mode') - self.register_setting('cover_file_convert_images') - self.register_setting('cover_file_convert_to_format') for resize_mode in COVER_RESIZE_MODES: self.ui.tags_resize_mode.addItem(resize_mode.title, resize_mode.mode.value) diff --git a/picard/ui/options/fingerprinting.py b/picard/ui/options/fingerprinting.py index 29a08782b3..a24adb6ba0 100644 --- a/picard/ui/options/fingerprinting.py +++ b/picard/ui/options/fingerprinting.py @@ -66,6 +66,15 @@ class FingerprintingOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_fingerprinting.html" + OPTIONS = ( + ('fingerprinting_system', None), + ('acoustid_fpcalc', None), + ('acoustid_apikey', None), + ('ignore_existing_acoustid_fingerprints', None), + ('save_acoustid_fingerprints', None), + ('fpcalc_threads', None), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self._fpcalc_valid = True @@ -79,13 +88,6 @@ def __init__(self, parent=None): self.ui.acoustid_apikey_get.clicked.connect(self.acoustid_apikey_get) self.ui.acoustid_apikey.setValidator(ApiKeyValidator()) - self.register_setting('fingerprinting_system') - self.register_setting('acoustid_fpcalc') - self.register_setting('acoustid_apikey') - self.register_setting('ignore_existing_acoustid_fingerprints') - self.register_setting('save_acoustid_fingerprints') - self.register_setting('fpcalc_threads') - def load(self): config = get_config() if config.setting['fingerprinting_system'] == 'acoustid': diff --git a/picard/ui/options/general.py b/picard/ui/options/general.py index 7057a675c0..c52d277443 100644 --- a/picard/ui/options/general.py +++ b/picard/ui/options/general.py @@ -60,6 +60,19 @@ class GeneralOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_general.html" + OPTIONS = ( + ('server_host', ['server_host']), + ('server_port', ['server_port']), + ('analyze_new_files', ['analyze_new_files']), + ('cluster_new_files', ['cluster_new_files']), + ('ignore_file_mbids', ['ignore_file_mbids']), + ('check_for_plugin_updates', ['check_for_plugin_updates']), + ('check_for_updates', ['check_for_updates']), + ('update_check_days', ['update_check_days']), + ('update_level', ['update_level']), + ('use_server_for_submission', ['use_server_for_submission']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_GeneralOptionsPage() @@ -74,17 +87,6 @@ def __init__(self, parent=None): self.ui.login_error.hide() self.update_login_logout() - self.register_setting('server_host', ['server_host']) - self.register_setting('server_port', ['server_port']) - self.register_setting('analyze_new_files', ['analyze_new_files']) - self.register_setting('cluster_new_files', ['cluster_new_files']) - self.register_setting('ignore_file_mbids', ['ignore_file_mbids']) - self.register_setting('check_for_plugin_updates', ['check_for_plugin_updates']) - self.register_setting('check_for_updates', ['check_for_updates']) - self.register_setting('update_check_days', ['update_check_days']) - self.register_setting('update_level', ['update_level']) - self.register_setting('use_server_for_submission') - def load(self): config = get_config() self.ui.server_host.setEditText(config.setting['server_host']) diff --git a/picard/ui/options/genres.py b/picard/ui/options/genres.py index d87206d9e1..a561112dc8 100644 --- a/picard/ui/options/genres.py +++ b/picard/ui/options/genres.py @@ -87,6 +87,17 @@ class GenresOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_genres.html" + OPTIONS = ( + ('use_genres', None), + ('only_my_genres', ['only_my_genres']), + ('artists_genres', ['artists_genres']), + ('folksonomy_tags', ['folksonomy_tags']), + ('min_genre_usage', ['min_genre_usage']), + ('max_genres', ['max_genres']), + ('join_genres', ['join_genres']), + ('genres_filter', ['genres_filter']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_GenresOptionsPage() @@ -108,15 +119,6 @@ def __init__(self, parent=None): self.fmt_clear = QTextBlockFormat() self.fmt_clear.clearBackground() - self.register_setting('use_genres', []) - self.register_setting('only_my_genres', ['only_my_genres']) - self.register_setting('artists_genres', ['artists_genres']) - self.register_setting('folksonomy_tags', ['folksonomy_tags']) - self.register_setting('min_genre_usage', ['min_genre_usage']) - self.register_setting('max_genres', ['max_genres']) - self.register_setting('join_genres', ['join_genres']) - self.register_setting('genres_filter', ['genres_filter']) - def load(self): config = get_config() self.ui.use_genres.setChecked(config.setting['use_genres']) diff --git a/picard/ui/options/interface.py b/picard/ui/options/interface.py index da2eb13ec2..9f4b98975a 100644 --- a/picard/ui/options/interface.py +++ b/picard/ui/options/interface.py @@ -68,6 +68,22 @@ class InterfaceOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_interface.html" + OPTIONS = ( + ('toolbar_show_labels', ['toolbar_show_labels']), + ('show_menu_icons', ['show_menu_icons']), + ('ui_language', ['ui_language']), + ('ui_theme', ['ui_theme']), + ('allow_multi_dirs_selection', ['allow_multi_dirs_selection']), + ('builtin_search', ['builtin_search']), + ('use_adv_search_syntax', ['use_adv_search_syntax']), + ('show_new_user_dialog', ['new_user_dialog']), + ('quit_confirmation', ['quit_confirmation']), + ('file_save_warning', ['file_save_warning']), + ('filebrowser_horizontal_autoscroll', ['filebrowser_horizontal_autoscroll']), + ('starting_directory', ['starting_directory']), + ('starting_directory_path', ['starting_directory_path']), + ) + # Those are labels for theme display _UI_THEME_LABELS = { UiTheme.DEFAULT: { @@ -126,20 +142,6 @@ def fcmp(x): self.ui.allow_multi_dirs_selection.stateChanged.connect(self.multi_selection_warning) - self.register_setting('toolbar_show_labels', ['toolbar_show_labels']) - self.register_setting('show_menu_icons', ['show_menu_icons']) - self.register_setting('ui_language', ['ui_language', 'label']) - self.register_setting('ui_theme', ['ui_theme', 'label_theme']) - self.register_setting('allow_multi_dirs_selection', ['allow_multi_dirs_selection']) - self.register_setting('builtin_search', ['builtin_search']) - self.register_setting('use_adv_search_syntax', ['use_adv_search_syntax']) - self.register_setting('show_new_user_dialog', ['new_user_dialog']) - self.register_setting('quit_confirmation', ['quit_confirmation']) - self.register_setting('file_save_warning', ['file_save_warning']) - self.register_setting('filebrowser_horizontal_autoscroll', ['filebrowser_horizontal_autoscroll']) - self.register_setting('starting_directory', ['starting_directory']) - self.register_setting('starting_directory_path', ['starting_directory_path']) - def load(self): # Don't display the multi-selection warning when loading values. # This is required because loading a different option profile could trigger the warning. diff --git a/picard/ui/options/interface_colors.py b/picard/ui/options/interface_colors.py index 9fde2ac92e..2d6fe87486 100644 --- a/picard/ui/options/interface_colors.py +++ b/picard/ui/options/interface_colors.py @@ -100,6 +100,11 @@ class InterfaceColorsOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_interface_colors.html" + OPTIONS = ( + ('interface_colors', ['colors']), + ('interface_colors_dark', ['colors']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_InterfaceColorsOptionsPage() @@ -108,9 +113,6 @@ def __init__(self, parent=None): self.colors_list = QtWidgets.QVBoxLayout() self.ui.colors.setLayout(self.colors_list) - self.register_setting('interface_colors', ['colors']) - self.register_setting('interface_colors_dark', ['colors']) - def update_color_selectors(self): if self.colors_list: delete_items_of_layout(self.colors_list) diff --git a/picard/ui/options/interface_toolbar.py b/picard/ui/options/interface_toolbar.py index d45abc45c7..ac7337d768 100644 --- a/picard/ui/options/interface_toolbar.py +++ b/picard/ui/options/interface_toolbar.py @@ -72,6 +72,11 @@ class InterfaceToolbarOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_interface_toolbar.html" SEPARATOR = '—' * 5 + + OPTIONS = ( + ('toolbar_layout', ['toolbar_layout_list']), + ) + TOOLBAR_BUTTONS = { MainAction.ADD_DIRECTORY: ToolbarButtonDesc( N_("Add Folder"), @@ -148,8 +153,6 @@ def __init__(self, parent=None): self.ui.down_button, self.update_action_buttons) self.update_buttons = self.move_view.update_buttons - self.register_setting('toolbar_layout', ['toolbar_layout_list']) - def load(self): self.populate_action_list() self.ui.toolbar_layout_list.setCurrentRow(0) diff --git a/picard/ui/options/interface_top_tags.py b/picard/ui/options/interface_top_tags.py index 4542ebd9ca..f2edf59026 100644 --- a/picard/ui/options/interface_top_tags.py +++ b/picard/ui/options/interface_top_tags.py @@ -39,13 +39,15 @@ class InterfaceTopTagsOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_interface_top_tags.html" + OPTIONS = ( + ('metadatabox_top_tags', ['top_tags_groupBox']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_InterfaceTopTagsOptionsPage() self.ui.setupUi(self) - self.register_setting('metadatabox_top_tags', ['top_tags_groupBox']) - def load(self): config = get_config() tags = config.setting['metadatabox_top_tags'] diff --git a/picard/ui/options/maintenance.py b/picard/ui/options/maintenance.py index c3125fbf75..726b830b73 100644 --- a/picard/ui/options/maintenance.py +++ b/picard/ui/options/maintenance.py @@ -77,6 +77,10 @@ class MaintenanceOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_maintenance.html" + OPTIONS = ( + ('autobackup_directory', ['autobackup_dir']), + ) + signal_reload = QtCore.pyqtSignal() def __init__(self, parent=None): @@ -113,8 +117,6 @@ def __init__(self, parent=None): self.ui.config_file.setPalette(palette_readonly) self.last_valid_path = _safe_autobackup_dir('') - self.register_setting('autobackup_directory', ['autobackup_dir']) - def get_current_autobackup_dir(self): return _safe_autobackup_dir(self.ui.autobackup_dir.text()) diff --git a/picard/ui/options/matching.py b/picard/ui/options/matching.py index 3b78265e34..3b98150016 100644 --- a/picard/ui/options/matching.py +++ b/picard/ui/options/matching.py @@ -39,6 +39,12 @@ class MatchingOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_matching.html" + OPTIONS = ( + ('file_lookup_threshold', ['file_lookup_threshold']), + ('cluster_lookup_threshold', ['cluster_lookup_threshold']), + ('track_matching_threshold', ['track_matching_threshold']), + ) + _release_type_sliders = {} def __init__(self, parent=None): @@ -46,10 +52,6 @@ def __init__(self, parent=None): self.ui = Ui_MatchingOptionsPage() self.ui.setupUi(self) - self.register_setting('file_lookup_threshold', ['file_lookup_threshold']) - self.register_setting('cluster_lookup_threshold', ['cluster_lookup_threshold']) - self.register_setting('track_matching_threshold', ['track_matching_threshold']) - def load(self): config = get_config() self.ui.file_lookup_threshold.setValue(int(config.setting['file_lookup_threshold'] * 100)) diff --git a/picard/ui/options/metadata.py b/picard/ui/options/metadata.py index 820a0b19a7..87066fd1c0 100644 --- a/picard/ui/options/metadata.py +++ b/picard/ui/options/metadata.py @@ -85,6 +85,21 @@ class MetadataOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_metadata.html" + OPTIONS = ( + ('translate_artist_names', ['translate_artist_names']), + ('artist_locales', ['selected_locales']), + ('translate_artist_names_script_exception', ['translate_artist_names_script_exception']), + ('script_exceptions', ['selected_scripts']), + ('standardize_artists', ['standardize_artists']), + ('standardize_instruments', ['standardize_instruments']), + ('convert_punctuation', ['convert_punctuation']), + ('release_ars', ['release_ars']), + ('track_ars', ['track_ars']), + ('guess_tracknumber_and_title', ['guess_tracknumber_and_title']), + ('va_name', ['va_name']), + ('nat_name', ['nat_name']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_MetadataOptionsPage() @@ -96,19 +111,6 @@ def __init__(self, parent=None): self.ui.translate_artist_names.stateChanged.connect(self.set_enabled_states) self.ui.translate_artist_names_script_exception.stateChanged.connect(self.set_enabled_states) - self.register_setting('translate_artist_names', ['translate_artist_names']) - self.register_setting('artist_locales', ['selected_locales']) - self.register_setting('translate_artist_names_script_exception', ['translate_artist_names_script_exception']) - self.register_setting('script_exceptions', ['selected_scripts']) - self.register_setting('standardize_artists', ['standardize_artists']) - self.register_setting('standardize_instruments', ['standardize_instruments']) - self.register_setting('convert_punctuation', ['convert_punctuation']) - self.register_setting('release_ars', ['release_ars']) - self.register_setting('track_ars', ['track_ars']) - self.register_setting('guess_tracknumber_and_title', ['guess_tracknumber_and_title']) - self.register_setting('va_name', ['va_name']) - self.register_setting('nat_name', ['nat_name']) - def load(self): config = get_config() self.ui.translate_artist_names.setChecked(config.setting['translate_artist_names']) diff --git a/picard/ui/options/network.py b/picard/ui/options/network.py index a11fb34489..9d403a1a78 100644 --- a/picard/ui/options/network.py +++ b/picard/ui/options/network.py @@ -40,23 +40,25 @@ class NetworkOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_network.html" + OPTIONS = ( + ('use_proxy', ['web_proxy']), + ('proxy_type', ['proxy_type_socks', 'proxy_type_http']), + ('proxy_server_host', ['server_host']), + ('proxy_server_port', ['server_port']), + ('proxy_username', ['username']), + ('proxy_password', ['password']), + ('network_transfer_timeout_seconds', ['transfer_timeout']), + ('network_cache_size_bytes', ['network_cache_size']), + ('browser_integration', ['browser_integration']), + ('browser_integration_port', ['browser_integration_port']), + ('browser_integration_localhost_only', ['browser_integration_localhost_only']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_NetworkOptionsPage() self.ui.setupUi(self) - self.register_setting('use_proxy', []) - self.register_setting('proxy_type', ['proxy_type_socks', 'proxy_type_http']) - self.register_setting('proxy_server_host', ['server_host']) - self.register_setting('proxy_server_port', ['server_port']) - self.register_setting('proxy_username', ['username']) - self.register_setting('proxy_password', ['password']) - self.register_setting('network_transfer_timeout_seconds', ['transfer_timeout']) - self.register_setting('network_cache_size_bytes', ['network_cache_size']) - self.register_setting('browser_integration', []) - self.register_setting('browser_integration_port', ['browser_integration_port']) - self.register_setting('browser_integration_localhost_only', ['browser_integration_localhost_only']) - def load(self): config = get_config() self.ui.web_proxy.setChecked(config.setting['use_proxy']) diff --git a/picard/ui/options/ratings.py b/picard/ui/options/ratings.py index ef4d9a4b0b..bb9ae98d5b 100644 --- a/picard/ui/options/ratings.py +++ b/picard/ui/options/ratings.py @@ -38,15 +38,17 @@ class RatingsOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_ratings.html" + OPTIONS = ( + ('enable_ratings', ['enable_ratings']), + ('rating_user_email', ['rating_user_email']), + ('submit_ratings', ['submit_ratings']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_RatingsOptionsPage() self.ui.setupUi(self) - self.register_setting('enable_ratings', []) - self.register_setting('rating_user_email', ['rating_user_email']) - self.register_setting('submit_ratings', ['submit_ratings']) - def load(self): config = get_config() self.ui.enable_ratings.setChecked(config.setting['enable_ratings']) diff --git a/picard/ui/options/releases.py b/picard/ui/options/releases.py index ddced369b9..856649084c 100644 --- a/picard/ui/options/releases.py +++ b/picard/ui/options/releases.py @@ -161,6 +161,12 @@ class ReleasesOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_releases.html" + OPTIONS = ( + ('release_type_scores', ['type_group']), + ('preferred_release_countries', ['country_group']), + ('preferred_release_formats', ['format_group']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_ReleasesOptionsPage() @@ -214,10 +220,6 @@ def add_slider(name, griditer, context): self.ui.format_list.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection) self.ui.preferred_format_list.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection) - self.register_setting('release_type_scores', ['type_group']) - self.register_setting('preferred_release_countries', ['country_group']) - self.register_setting('preferred_release_formats', ['format_group']) - def restore_defaults(self): # Clear lists self.ui.preferred_country_list.clear() diff --git a/picard/ui/options/renaming.py b/picard/ui/options/renaming.py index a2352df85a..75ac10bc59 100644 --- a/picard/ui/options/renaming.py +++ b/picard/ui/options/renaming.py @@ -72,6 +72,16 @@ class RenamingOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_filerenaming.html" + OPTIONS = ( + ('move_files', ['move_files']), + ('move_files_to', ['move_files_to']), + ('move_additional_files', ['move_additional_files']), + ('move_additional_files_pattern', ['move_additional_files_pattern']), + ('delete_empty_dirs', ['delete_empty_dirs']), + ('rename_files', ['rename_files']), + ('selected_file_naming_script_id', ['naming_script_selector']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.script_text = "" @@ -114,14 +124,6 @@ def __init__(self, parent=None): self.current_row = -1 - self.register_setting('move_files', ['move_files']) - self.register_setting('move_files_to', ['move_files_to']) - self.register_setting('move_additional_files', ['move_additional_files']) - self.register_setting('move_additional_files_pattern', ['move_additional_files_pattern']) - self.register_setting('delete_empty_dirs', ['delete_empty_dirs']) - self.register_setting('rename_files', ['rename_files']) - self.register_setting('selected_file_naming_script_id', ['naming_script_selector']) - def update_selector_from_editor(self): """Update the script selector combo box from the script editor page. """ diff --git a/picard/ui/options/renaming_compat.py b/picard/ui/options/renaming_compat.py index 9a386b3b7c..4c68db6e7f 100644 --- a/picard/ui/options/renaming_compat.py +++ b/picard/ui/options/renaming_compat.py @@ -70,6 +70,15 @@ class RenamingCompatOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_filerenaming_compat.html" + OPTIONS = ( + ('ascii_filenames', ['ascii_filenames']), + ('windows_compatibility', ['windows_compatibility']), + ('win_compat_replacements', ['win_compat_replacements']), + ('windows_long_paths', ['windows_long_paths']), + ('replace_spaces_with_underscores', ['replace_spaces_with_underscores']), + ('replace_dir_separator', ['replace_dir_separator']), + ) + options_changed = QtCore.pyqtSignal(dict) def __init__(self, parent=None): @@ -86,13 +95,6 @@ def __init__(self, parent=None): self.ui.replace_dir_separator.setValidator(NoDirectorySeparatorValidator()) self.ui.btn_windows_compatibility_change.clicked.connect(self.open_win_compat_dialog) - self.register_setting('ascii_filenames', ['ascii_filenames']) - self.register_setting('windows_compatibility', ['windows_compatibility']) - self.register_setting('win_compat_replacements', ['win_compat_replacements']) - self.register_setting('windows_long_paths', ['windows_long_paths']) - self.register_setting('replace_spaces_with_underscores', ['replace_spaces_with_underscores']) - self.register_setting('replace_dir_separator', ['replace_dir_separator']) - def load(self): config = get_config() self.win_compat_replacements = config.setting['win_compat_replacements'] diff --git a/picard/ui/options/scripting.py b/picard/ui/options/scripting.py index be1d758bf8..e1e6392656 100644 --- a/picard/ui/options/scripting.py +++ b/picard/ui/options/scripting.py @@ -106,6 +106,11 @@ class ScriptingOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_scripting.html" + OPTIONS = ( + ('enable_tagger_scripts', ['enable_tagger_scripts']), + ('list_of_scripts', ['script_list']), + ) + default_script_directory = os.path.normpath(QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.StandardLocation.DocumentsLocation)) default_script_extension = "ptsp" @@ -132,9 +137,6 @@ def __init__(self, parent=None): self.ui.script_list.signal_reset_selected_item.connect(self.reset_selected_item) - self.register_setting('enable_tagger_scripts', ['enable_tagger_scripts']) - self.register_setting('list_of_scripts', ['script_list']) - def show_scripting_documentation(self): ScriptingDocumentationDialog.show_instance(parent=self) diff --git a/picard/ui/options/tags.py b/picard/ui/options/tags.py index c637708dde..8e4de02d8e 100644 --- a/picard/ui/options/tags.py +++ b/picard/ui/options/tags.py @@ -45,20 +45,22 @@ class TagsOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_tags.html" + OPTIONS = ( + ('dont_write_tags', ['write_tags']), + ('preserve_timestamps', ['preserve_timestamps']), + ('clear_existing_tags', ['clear_existing_tags']), + ('preserve_images', ['preserve_images']), + ('remove_id3_from_flac', ['remove_id3_from_flac']), + ('remove_ape_from_mp3', ['remove_ape_from_mp3']), + ('fix_missing_seekpoints_flac', ['fix_missing_seekpoints_flac']), + ('preserved_tags', ['preserved_tags']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_TagsOptionsPage() self.ui.setupUi(self) - self.register_setting('dont_write_tags', ['write_tags']) - self.register_setting('preserve_timestamps', ['preserve_timestamps']) - self.register_setting('clear_existing_tags', ['clear_existing_tags']) - self.register_setting('preserve_images', ['preserve_images']) - self.register_setting('remove_id3_from_flac', ['remove_id3_from_flac']) - self.register_setting('remove_ape_from_mp3', ['remove_ape_from_mp3']) - self.register_setting('fix_missing_seekpoints_flac', ['fix_missing_seekpoints_flac']) - self.register_setting('preserved_tags', ['preserved_tags']) - def load(self): config = get_config() self.ui.write_tags.setChecked(not config.setting['dont_write_tags']) diff --git a/picard/ui/options/tags_compatibility_aac.py b/picard/ui/options/tags_compatibility_aac.py index ea642b3bab..c99bd9f64e 100644 --- a/picard/ui/options/tags_compatibility_aac.py +++ b/picard/ui/options/tags_compatibility_aac.py @@ -40,15 +40,17 @@ class TagsCompatibilityAACOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_tags_compatibility_aac.html" + OPTIONS = ( + ('aac_save_ape', ['aac_save_ape', 'aac_no_tags']), + ('remove_ape_from_aac', ['remove_ape_from_aac']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_TagsCompatibilityOptionsPage() self.ui.setupUi(self) self.ui.aac_no_tags.toggled.connect(self.ui.remove_ape_from_aac.setEnabled) - self.register_setting('aac_save_ape', ['aac_save_ape', 'aac_no_tags']) - self.register_setting('remove_ape_from_aac', ['remove_ape_from_aac']) - def load(self): config = get_config() if config.setting['aac_save_ape']: diff --git a/picard/ui/options/tags_compatibility_ac3.py b/picard/ui/options/tags_compatibility_ac3.py index 26edba46c8..af54f0d733 100644 --- a/picard/ui/options/tags_compatibility_ac3.py +++ b/picard/ui/options/tags_compatibility_ac3.py @@ -40,15 +40,17 @@ class TagsCompatibilityAC3OptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_tags_compatibility_ac3.html" + OPTIONS = ( + ('ac3_save_ape', ['ac3_save_ape', 'ac3_no_tags']), + ('remove_ape_from_ac3', ['remove_ape_from_ac3']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_TagsCompatibilityOptionsPage() self.ui.setupUi(self) self.ui.ac3_no_tags.toggled.connect(self.ui.remove_ape_from_ac3.setEnabled) - self.register_setting('ac3_save_ape', ['ac3_save_ape', 'ac3_no_tags']) - self.register_setting('remove_ape_from_ac3', ['remove_ape_from_ac3']) - def load(self): config = get_config() if config.setting['ac3_save_ape']: diff --git a/picard/ui/options/tags_compatibility_id3.py b/picard/ui/options/tags_compatibility_id3.py index 422aeb0b43..e2efafab1b 100644 --- a/picard/ui/options/tags_compatibility_id3.py +++ b/picard/ui/options/tags_compatibility_id3.py @@ -42,6 +42,14 @@ class TagsCompatibilityID3OptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_tags_compatibility_id3.html" + OPTIONS = ( + ('write_id3v23', ['write_id3v23', 'write_id3v24']), + ('id3v2_encoding', ['enc_utf8', 'enc_utf16', 'enc_iso88591']), + ('id3v23_join_with', ['id3v23_join_with']), + ('itunes_compatible_grouping', ['itunes_compatible_grouping']), + ('write_id3v1', ['write_id3v1']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_TagsCompatibilityOptionsPage() @@ -49,12 +57,6 @@ def __init__(self, parent=None): self.ui.write_id3v23.clicked.connect(self.update_encodings) self.ui.write_id3v24.clicked.connect(partial(self.update_encodings, force_utf8=True)) - self.register_setting('write_id3v23', ['write_id3v23', 'write_id3v24']) - self.register_setting('id3v2_encoding', ['enc_utf8', 'enc_utf16', 'enc_iso88591']) - self.register_setting('id3v23_join_with', ['id3v23_join_with']) - self.register_setting('itunes_compatible_grouping', ['itunes_compatible_grouping']) - self.register_setting('write_id3v1', ['write_id3v1']) - def load(self): config = get_config() self.ui.write_id3v1.setChecked(config.setting['write_id3v1']) diff --git a/picard/ui/options/tags_compatibility_wave.py b/picard/ui/options/tags_compatibility_wave.py index d7ee6028d6..08e68ff3e1 100644 --- a/picard/ui/options/tags_compatibility_wave.py +++ b/picard/ui/options/tags_compatibility_wave.py @@ -41,15 +41,17 @@ class TagsCompatibilityWaveOptionsPage(OptionsPage): ACTIVE = True HELP_URL = "/config/options_tags_compatibility_wave.html" + OPTIONS = ( + ('write_wave_riff_info', ['write_wave_riff_info']), + ('remove_wave_riff_info', ['remove_wave_riff_info']), + ('wave_riff_info_encoding', ['wave_riff_info_enc_cp1252', 'wave_riff_info_enc_utf8']), + ) + def __init__(self, parent=None): super().__init__(parent=parent) self.ui = Ui_TagsCompatibilityOptionsPage() self.ui.setupUi(self) - self.register_setting('write_wave_riff_info', ['write_wave_riff_info']) - self.register_setting('remove_wave_riff_info', ['remove_wave_riff_info']) - self.register_setting('wave_riff_info_encoding', ['wave_riff_info_enc_cp1252', 'wave_riff_info_enc_utf8']) - def load(self): config = get_config() self.ui.write_wave_riff_info.setChecked(config.setting['write_wave_riff_info'])