Hey hey, sorry for the delay been busy on a few things!
The key thing is that you shouldn’t persist the data to the settings store until the dialog is accepted. In my earlier example I did that by connecting the update_settings_from_widgets
method to the dialogs .accept
signal – that only fires when the dialog is accepted. It shouldn’t run in any other case.
Below is an updated version which also handles properly typing the values returned from settings.
from PyQt6.QtWidgets import QWidget, QPushButton, QLineEdit, QLabel, QDialog, QApplication, QVBoxLayout, QGroupBox, QRadioButton, QDialogButtonBox
from PyQt6.QtCore import QSettings, pyqtSignal, QObject
class SettingsManager(QSettings):
"""Create a settings manager for the SuperChess application."""
widget_mappers = {
'QCheckBox': ('checkState', 'setCheckState', bool),
'QLineEdit': ('text', 'setText', str),
'QSpinBox': ('value', 'setValue', int),
'QRadioButton': ('isChecked', 'setChecked', bool),
}
settings_changed = pyqtSignal()
def __init__(self):
super().__init__()
self.settings = QSettings("MyApp", "settings")
print("Settings file located at:", self.settings.fileName())
def update_widgets_from_settings(self, map):
for name, widget in map.items():
cls = widget.__class__.__name__
getter, setter, dtype = self.widget_mappers.get(cls, (None, None))
value = self.settings.value(name, type=dtype)
print("load:", getter, setter, value, type(value), dtype)
if setter and value is not None:
fn = getattr(widget, setter)
try:
fn(value) # Set the widget.
except Exception as e:
print(e) # handle type error
def update_settings_from_widgets(self, map):
for name, widget in map.items():
cls = widget.__class__.__name__
getter, setter, dtype = self.widget_mappers.get(cls, (None, None))
print("save:", getter, setter)
if getter:
fn = getattr(widget, getter)
value = fn()
print("-- value:", value, type(value), dtype)
if value is not None:
self.settings.setValue(name, value) # Set the settings.
# Notify watcher of changed settings.
self.settings_changed.emit()
# Define this in another module, import to use.
settings = SettingsManager()
class SettingsDialog(QDialog):
"""Create a settings dialog to edit all the settings of the application."""
def __init__(self):
super().__init__()
"""Create groups of radio buttons as settings elements."""
self.player = QGroupBox("Player")
self.player_name = QLineEdit()
self.engine = QGroupBox("Chess engine")
self.engine_black = QRadioButton(text="Plays as Black")
self.engine_black.setChecked(True)
self.engine_white = QRadioButton(text="Plays as White")
self.engine_white.setChecked(False)
_buttons = QDialogButtonBox.StandardButtons
self.button_box = QDialogButtonBox(_buttons.Ok | _buttons.Cancel)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
slayout = QVBoxLayout()
slayout.addWidget(self.player)
slayout.addWidget(self.player_name)
slayout.addWidget(self.engine)
slayout.addWidget(self.engine_white)
slayout.addWidget(self.engine_black)
slayout.addWidget(self.button_box)
self.setLayout(slayout)
self.map = {
'player': self.player_name,
'black': self.engine_black,
'white': self.engine_white
}
self.load_settings()
self.accepted.connect(self.save_settings)
def load_settings(self):
""" Reload the settings from the settings store """
settings.update_widgets_from_settings(self.map)
def save_settings(self):
""" Triggered when the dialog is accepted; copys settings values to the settings manager """
settings.update_settings_from_widgets(self.map)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.button = QPushButton("Press for settings")
self.label = QLabel()
self.button.pressed.connect(self.edit_settings)
settings.settings_changed.connect(self.update_label)
self.update_label()
layout = QVBoxLayout()
layout.addWidget(self.button)
layout.addWidget(self.label)
self.setLayout(layout)
def edit_settings(self):
dlg = SettingsDialog()
dlg.exec()
def update_label(self):
data = {
'player': settings.value('player'),
'white': settings.value('white'),
'black': settings.value('black'),
}
self.label.setText(str(data))
app = QApplication([])
w = MainWindow()
w.show()
app.exec()
The important line is here (and that save_settings isn’t called anywhere else!)
self.accepted.connect(self.save_settings)
If we run this you can see that the settings aren’t saved unless I press OK.
I basically merged my settings dialog with my settings manager.
I wouldn’t recommend that, as you’re then mixing your GUI with non-GUI logic. In the code above I’ve turned the manager into a subclass of QSettings
which simplifies things a bit. Ideally I’d recommend you putting your settings object definition in a file by itself, creating a single instance, and then importing it anywhere you need it in your app.