Save options to first enabled profile:

- Save to first enabled profile, with "User base settings" as fallback
- Display note if any profiles are enabled
- Highlight options for all enabled profiles
- Add tooltip to options showing which profile will be updated
- Update tests to remove deleted functionality
This commit is contained in:
Bob Swift
2021-08-03 11:06:43 -06:00
parent 7e8c0cc923
commit a22cce8c21
6 changed files with 88 additions and 235 deletions

View File

@@ -46,10 +46,7 @@ from picard import (
PICARD_VERSION,
log,
)
from picard.profile import (
USER_SETTINGS_PROFILE_ID,
UserProfileGroups,
)
from picard.profile import UserProfileGroups
from picard.version import Version
@@ -152,21 +149,14 @@ class SettingConfigSection(ConfigSection):
self.__prefix = self.__name + '/'
self._memoization = defaultdict(Memovar)
self.init_profile_options()
self._selected_profile = None
def _get_active_profile_ids(self):
if self._selected_profile is not None:
if self._selected_profile == USER_SETTINGS_PROFILE_ID:
return
# Act as if the selected profile is the only active profile.
yield self._selected_profile
else:
profiles = self.__qt_config.profiles[self.PROFILES_KEY]
if profiles is None:
return
for profile in profiles:
if profile['enabled']:
yield profile["id"]
profiles = self.__qt_config.profiles[self.PROFILES_KEY]
if profiles is None:
return
for profile in profiles:
if profile['enabled']:
yield profile["id"]
def _get_active_profile_settings(self):
for id in self._get_active_profile_ids():
@@ -197,10 +187,9 @@ class SettingConfigSection(ConfigSection):
if name in settings:
self._save_profile_setting(id, name, value)
return
if self._selected_profile is None or self._selected_profile == USER_SETTINGS_PROFILE_ID:
key = self.key(name)
self.__qt_config.setValue(key, value)
self._memoization[key].dirty = True
key = self.key(name)
self.__qt_config.setValue(key, value)
self._memoization[key].dirty = True
def _save_profile_setting(self, profile_id, name, value):
profile_settings = self.__qt_config.profiles[self.SETTINGS_KEY]
@@ -209,9 +198,6 @@ class SettingConfigSection(ConfigSection):
self.__qt_config.setValue(key, profile_settings)
self._memoization[key].dirty = True
def set_profile(self, profile_id=None):
self._selected_profile = profile_id
class Config(QtCore.QSettings):

View File

@@ -27,9 +27,6 @@ from collections import (
from picard import i18n # noqa: F401,E402 # pylint: disable=unused-import
USER_SETTINGS_PROFILE_ID = 'user_settings'
SettingDesc = namedtuple('SettingDesc', ('name', 'title', 'fields'))

View File

@@ -45,10 +45,7 @@ from picard.config import (
TextOption,
get_config,
)
from picard.profile import (
USER_SETTINGS_PROFILE_ID,
UserProfileGroups,
)
from picard.profile import UserProfileGroups
from picard.util import (
restore_method,
webbrowser2,
@@ -193,19 +190,9 @@ class OptionsDialog(PicardDialog, SingletonDialog):
self.first_enter = True
self.installEventFilter(self)
self.USER_SETTINGS_TITLE = _("User base settings")
if config.profiles[SettingConfigSection.PROFILES_KEY]:
self.ui.profile_frame.show()
self.ui.save_to_profile.clear()
self.ui.save_to_profile.addItem(self.USER_SETTINGS_TITLE, USER_SETTINGS_PROFILE_ID)
index = 0
for idx, item in enumerate(config.profiles[SettingConfigSection.PROFILES_KEY], start=1):
self.ui.save_to_profile.addItem(item["title"], item["id"])
if not index and item["enabled"]:
index = idx
self.ui.save_to_profile.currentIndexChanged.connect(self.switch_profile)
self.ui.save_to_profile.setCurrentIndex(index)
self.highlight_enabled_profile_options()
else:
self.ui.profile_frame.hide()
@@ -237,39 +224,31 @@ class OptionsDialog(PicardDialog, SingletonDialog):
profile_dialog.activateWindow()
def _get_profile_title_from_id(self, profile_id):
if profile_id == USER_SETTINGS_PROFILE_ID:
return self.USER_SETTINGS_TITLE
config = get_config()
for item in config.profiles[SettingConfigSection.PROFILES_KEY]:
if item["id"] == profile_id:
return item["title"]
return _('Unknown profile')
def switch_profile(self, index):
def highlight_enabled_profile_options(self):
config = get_config()
if not config.profiles[SettingConfigSection.PROFILES_KEY]:
return
self.ui.notice_text.hide()
HighlightColors = namedtuple('HighlightColors', ('fg', 'bg'))
HIGHLIGHT_FMT = "#%s { color: %s; background-color: %s; }"
if theme.is_dark_theme:
option_colors = HighlightColors('#FFFFFF', '#000080')
profile_colors = HighlightColors('#FFFFFF', '#300000')
else:
option_colors = HighlightColors('#000000', '#F9F906')
profile_colors = HighlightColors('#000000', '#FFA500')
# Highlight profile selector if profile selected.
if self.ui.save_to_profile.currentIndex():
self.ui.save_to_profile.setStyleSheet(HIGHLIGHT_FMT % ('save_to_profile', profile_colors.fg, profile_colors.bg))
else:
self.ui.save_to_profile.setStyleSheet("")
self.ui.notice_text.setStyleSheet(HIGHLIGHT_FMT % ("notice_text", option_colors.fg, option_colors.bg))
self.ui.notice_text.setText(_("Highlighted options will be saved to a profile."))
self.ui.notice_text.setToolTip(_("Hover your cursor over a highlighted option to see which profile will be updated."))
profile_id = self.ui.save_to_profile.currentData()
profile_title = self._get_profile_title_from_id(profile_id)
config = get_config()
config.setting.set_profile(profile_id)
settings = config.profiles[SettingConfigSection.SETTINGS_KEY]
if profile_id in settings:
profile_settings = settings[profile_id]
else:
profile_settings = {}
for page in self.pages:
page.load()
@@ -281,17 +260,24 @@ class OptionsDialog(PicardDialog, SingletonDialog):
obj = getattr(page.ui, opt_field)
except AttributeError:
continue
if opt.name in profile_settings:
style = HIGHLIGHT_FMT % (opt_field, option_colors.fg, option_colors.bg)
tooltip = _("This option is managed by profile: %s") % profile_title
else:
style = ""
tooltip = ""
try:
obj.setStyleSheet(style)
obj.setToolTip(tooltip)
except AttributeError:
pass
for item in config.profiles[SettingConfigSection.PROFILES_KEY]:
if item["enabled"]:
profile_id = item["id"]
profile_title = item["title"]
if profile_id in settings:
profile_settings = settings[profile_id]
else:
profile_settings = {}
if opt.name in profile_settings:
style = HIGHLIGHT_FMT % (opt_field, option_colors.fg, option_colors.bg)
tooltip = _("This option will be saved to profile: %s") % profile_title
try:
obj.setStyleSheet(style)
obj.setToolTip(tooltip)
except AttributeError:
pass
self.ui.notice_text.show()
break
def eventFilter(self, object, event):
"""Process selected events.
@@ -333,17 +319,6 @@ class OptionsDialog(PicardDialog, SingletonDialog):
return url
def accept(self):
profile_id = self.ui.save_to_profile.currentData()
if profile_id and profile_id != USER_SETTINGS_PROFILE_ID:
profile_name = self._get_profile_title_from_id(profile_id)
message_box = QtWidgets.QMessageBox(self)
message_box.setIcon(QtWidgets.QMessageBox.Warning)
message_box.setWindowModality(QtCore.Qt.WindowModal)
message_box.setWindowTitle(_("Save to Profile"))
message_box.setText(_("Changes will only be saved to the profile: \"%s\"\n\nDo you want to continue?") % profile_name)
message_box.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel)
if message_box.exec_() != QtWidgets.QMessageBox.Yes:
return
for page in self.pages:
try:
page.check()
@@ -361,18 +336,8 @@ class OptionsDialog(PicardDialog, SingletonDialog):
log.exception('Failed saving options page %r', page)
self._show_page_error(page, e)
return
self.reset_profile()
super().accept()
def reject(self):
self.reset_profile()
super().reject()
@staticmethod
def reset_profile():
config = get_config()
config.setting.set_profile()
def _show_page_error(self, page, error):
if not isinstance(error, OptionsCheckError):
error = OptionsCheckError(_('Unexpected error'), str(error))
@@ -462,7 +427,7 @@ class AttachedProfilesDialog(PicardDialog):
window_title = _("Profiles Attached to Options in %s Section") % group_title
self.setWindowTitle(window_title)
for name, title in group_options:
for name, title, object_name in group_options:
option_item = QtGui.QStandardItem(_(title))
option_item.setEditable(False)
row = [option_item]

View File

@@ -39,25 +39,24 @@ class Ui_Dialog(object):
self.profile_frame.setLineWidth(0)
self.profile_frame.setObjectName("profile_frame")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.profile_frame)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.notice_text = QtWidgets.QLabel(self.profile_frame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.notice_text.sizePolicy().hasHeightForWidth())
self.notice_text.setSizePolicy(sizePolicy)
font = QtGui.QFont()
font.setPointSize(10)
font.setBold(False)
font.setWeight(50)
self.notice_text.setFont(font)
self.notice_text.setText("")
self.notice_text.setObjectName("notice_text")
self.horizontalLayout_2.addWidget(self.notice_text)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.label = QtWidgets.QLabel(self.profile_frame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
self.label.setObjectName("label")
self.horizontalLayout_2.addWidget(self.label)
self.save_to_profile = QtWidgets.QComboBox(self.profile_frame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.save_to_profile.sizePolicy().hasHeightForWidth())
self.save_to_profile.setSizePolicy(sizePolicy)
self.save_to_profile.setObjectName("save_to_profile")
self.horizontalLayout_2.addWidget(self.save_to_profile)
self.profiles_buttonbox = QtWidgets.QDialogButtonBox(self.profile_frame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
@@ -80,4 +79,3 @@ class Ui_Dialog(object):
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_("Options"))
self.label.setText(_("Save settings to:"))

View File

@@ -199,108 +199,6 @@ class TestUserProfiles(TestPicardProfilesCommon):
self.assertEqual(self.config.setting[self.test_setting_1], False)
self.assertEqual(self.config.setting[self.test_setting_2], 99)
# Test retrieval with profiles disabled and specific profile set
self.config.profiles[self.PROFILES_KEY] = self.get_profiles(enabled=False)
self.config.setting.set_profile("test_key_0")
self.assertEqual(self.config.setting[self.test_setting_0], "def")
self.assertEqual(self.config.setting[self.test_setting_1], True)
self.assertEqual(self.config.setting[self.test_setting_2], 86)
# Test setting with profiles disabled and specific profile set
self.config.setting[self.test_setting_0] = "jkl"
# Stack:
# setting_0 setting_1 setting_2
# --------- --------- ---------
# test_key_0: jkl n/a n/a
# test_key_1: n/a False n/a
# test_key_2: n/a n/a 99
# user_settings: ghi True 86
self.assertEqual(self.config.setting[self.test_setting_0], "jkl")
self.assertEqual(self.config.setting[self.test_setting_1], True)
self.assertEqual(self.config.setting[self.test_setting_2], 86)
# Test retrieval with profiles disabled and no specific profile set
self.config.profiles[self.PROFILES_KEY] = self.get_profiles(enabled=False)
self.config.setting.set_profile()
self.assertEqual(self.config.setting[self.test_setting_0], "ghi")
self.assertEqual(self.config.setting[self.test_setting_1], True)
self.assertEqual(self.config.setting[self.test_setting_2], 86)
# Test retrieval with profiles enabled and no specific profile set
self.config.profiles[self.PROFILES_KEY] = self.get_profiles(enabled=True)
self.config.setting.set_profile()
self.assertEqual(self.config.setting[self.test_setting_0], "jkl")
self.assertEqual(self.config.setting[self.test_setting_1], False)
self.assertEqual(self.config.setting[self.test_setting_2], 99)
# Test save with profiles disabled and specific profile set
self.config.profiles[self.PROFILES_KEY] = self.get_profiles(enabled=False)
self.config.setting.set_profile("test_key_2")
self.config.setting[self.test_setting_0] = "xyz"
self.config.setting[self.test_setting_2] = 1
# Stack:
# setting_0 setting_1 setting_2
# --------- --------- ---------
# test_key_0: jkl n/a n/a
# test_key_1: n/a False n/a
# test_key_2: n/a n/a 1
# user_settings: ghi True 86
self.assertEqual(self.config.setting[self.test_setting_0], "ghi")
self.assertEqual(self.config.setting[self.test_setting_1], True)
self.assertEqual(self.config.setting[self.test_setting_2], 1)
self.config.setting.set_profile()
self.assertEqual(self.config.setting[self.test_setting_0], "ghi")
self.assertEqual(self.config.setting[self.test_setting_1], True)
self.assertEqual(self.config.setting[self.test_setting_2], 86)
# Test save with profiles enabled and specific profile set
self.config.profiles[self.PROFILES_KEY] = self.get_profiles(enabled=True)
self.config.setting.set_profile("test_key_2")
self.config.setting[self.test_setting_0] = "xyz"
self.config.setting[self.test_setting_2] = 2
# Stack:
# setting_0 setting_1 setting_2
# --------- --------- ---------
# test_key_0: jkl n/a n/a
# test_key_1: n/a False n/a
# test_key_2: n/a n/a 2
# user_settings: ghi True 86
self.assertEqual(self.config.setting[self.test_setting_0], "ghi")
self.assertEqual(self.config.setting[self.test_setting_1], True)
self.assertEqual(self.config.setting[self.test_setting_2], 2)
self.config.setting.set_profile()
self.assertEqual(self.config.setting[self.test_setting_0], "jkl")
self.assertEqual(self.config.setting[self.test_setting_1], False)
self.assertEqual(self.config.setting[self.test_setting_2], 2)
# Test save with profiles enabled and "user_settings" profile set
self.config.profiles[self.PROFILES_KEY] = self.get_profiles(enabled=True)
self.config.setting.set_profile("user_settings")
self.config.setting[self.test_setting_0] = "xyz"
self.config.setting[self.test_setting_2] = 3
# Stack:
# setting_0 setting_1 setting_2
# --------- --------- ---------
# test_key_0: jkl n/a n/a
# test_key_1: n/a False n/a
# test_key_2: n/a n/a 2
# user_settings: xyz True 3
self.assertEqual(self.config.setting[self.test_setting_0], "xyz")
self.assertEqual(self.config.setting[self.test_setting_1], True)
self.assertEqual(self.config.setting[self.test_setting_2], 3)
self.config.setting.set_profile()
self.assertEqual(self.config.setting[self.test_setting_0], "jkl")
self.assertEqual(self.config.setting[self.test_setting_1], False)
self.assertEqual(self.config.setting[self.test_setting_2], 2)
def test_config_option_rename(self):
from picard.config_upgrade import rename_option
self.config.setting[self.test_setting_0] = "abc"

View File

@@ -69,6 +69,38 @@
<number>0</number>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="notice_text">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
@@ -82,29 +114,6 @@
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Save settings to:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="save_to_profile">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="profiles_buttonbox">
<property name="sizePolicy">