pythonpyqtpyqt5qsettings

Saving states of QRadioButtons using QSettings not persisting values correctly in PyQt5


I'm baffled by the results of using QSettings to save the state of my radio buttons from one app session to another using PyQt5. (OS = Ubuntu Linux 18.04)

These are the steps to produce baffling results:

  1. Run the app; see both radio buttons selected; both buttons fire as if mouse selected
  2. Deselect the top radio button
  3. Close the app; see the 'correct' saving of the radio button checked states
  4. Run the app; both radio buttons are selected even though different states were saved where only one button is selected
  5. Deselect the top radio button
  6. Close the app; see the 'correct' saving of the radio button checked states
  7. In the code, in line 18, change QSettings('LoneProgrammer2', 'qsettingsTest1') to QSettings('ChangeThis', 'qsettingsTest1'); save the code
  8. Run the app; !!The correct saved values are reflected by the radio button selection!!
  9. Close the app; 10 Run the app and now there are two radio buttons selected again!

Can anyone please explain what's going on here? I just want the QSettings to work.

import sys

from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QDialogButtonBox, QGroupBox, QHBoxLayout, QRadioButton


class MyApp(QWidget):
    def __init__(self):
        super().__init__()

        self.oAutoPromoteRadioBtn = QRadioButton("Automatically Promote to Queen")
        self.oAutoPromoteRadioBtn.toggled.connect(self.on_selected)

        self.oUsePopupRadioBtn = QRadioButton("Promote Pawn Using Popup Window")
        self.oUsePopupRadioBtn.toggled.connect(self.on_selected)
        self.oUsePopupRadioBtn.setChecked(True)

        self.settings = QSettings('LoneProgrammer2', 'qsettingsTest1')  # companyname, appname 
        self.settings.beginGroup("Promotion Dialog")
        self.oAutoPromoteRadioBtn.setChecked(
            bool(self.settings.value('auto-promote radio button checked state')))
        self.oUsePopupRadioBtn.setChecked(
            bool(self.settings.value('promote using popup radio button checked state')))
        self.settings.endGroup()
        print('AT APP OPENING:')
        print('self.oAutoPromoteRadioBtn.isChecked() = ', self.oAutoPromoteRadioBtn.isChecked())
        print('self.oUsePopupRadioBtn.isChecked() = ', self.oUsePopupRadioBtn.isChecked())

        oVertLayoutForRadioButtons = QVBoxLayout()
        oVertLayoutForRadioButtons.addWidget(self.oAutoPromoteRadioBtn)
        oVertLayoutForRadioButtons.addWidget(self.oUsePopupRadioBtn)

        self.groupbox = QGroupBox("Select pawn promotion method:")
        self.groupbox.setFixedWidth(300)
        self.groupbox.setFixedHeight(95)
        self.groupbox.setLayout(oVertLayoutForRadioButtons)

        self.oVertLayout = QVBoxLayout()
        self.oVertLayout.addWidget(self.groupbox)
        self.setLayout(self.oVertLayout)

    def on_selected(self):
        radio_button = self.sender()

        if radio_button.isChecked():
            if radio_button.text() == 'Automatically Promote to Queen':
                print('set to auto-promote to queen')
            elif radio_button.text() == 'Promote Pawn Using Popup Window':
                print('set to promote pawn to queen using popup window')

    def closeEvent(self, event):
        # save the vars from this session
        self.settings.beginGroup("Promotion Dialog")
        self.settings.setValue('auto-promote radio button checked state', self.oAutoPromoteRadioBtn.isChecked())
        self.settings.setValue('promote using popup radio button checked state', self.oUsePopupRadioBtn.isChecked())
        self.settings.endGroup()

        print()
        print('AT APP CLOSE:')
        print('self.oAutoPromoteRadioBtn.isChecked() = ', self.oAutoPromoteRadioBtn.isChecked())
        print('self.oUsePopupRadioBtn.isChecked() = ', self.oUsePopupRadioBtn.isChecked())


if __name__ == "__main__":
    app = QApplication(sys.argv)

    demo = MyApp()
    demo.show()
    sys.exit(app.exec_())

Solution

  • When reading settings values it is good to indicate the type as there may be confusion (most likely value will return the strings "true" and "false" that are converted to True when using bool since they are truly). On the other hand to add readability it is better to create specialized methods.

    from functools import cached_property
    import sys
    
    from PyQt5.QtCore import QSettings
    from PyQt5.QtWidgets import (
        QApplication,
        QWidget,
        QVBoxLayout,
        QDialogButtonBox,
        QGroupBox,
        QHBoxLayout,
        QRadioButton,
    )
    
    
    class MyApp(QWidget):
        def __init__(self):
            super().__init__()
    
            self.oAutoPromoteRadioBtn = QRadioButton("Automatically Promote to Queen")
            self.oAutoPromoteRadioBtn.toggled.connect(self.on_selected)
    
            self.oUsePopupRadioBtn = QRadioButton("Promote Pawn Using Popup Window")
            self.oUsePopupRadioBtn.toggled.connect(self.on_selected)
    
            self.settings = QSettings(
                "LoneProgrammer2", "qsettingsTest1"
            )  # companyname, appname
    
            oVertLayoutForRadioButtons = QVBoxLayout()
            oVertLayoutForRadioButtons.addWidget(self.oAutoPromoteRadioBtn)
            oVertLayoutForRadioButtons.addWidget(self.oUsePopupRadioBtn)
    
            self.groupbox = QGroupBox("Select pawn promotion method:")
            self.groupbox.setFixedSize(300, 95)
            self.groupbox.setLayout(oVertLayoutForRadioButtons)
    
            self.oVertLayout = QVBoxLayout(self)
            self.oVertLayout.addWidget(self.groupbox)
    
            self.read_settings()
    
        def on_selected(self):
            radio_button = self.sender()
    
            if radio_button.isChecked():
                if radio_button.text() == "Automatically Promote to Queen":
                    print("set to auto-promote to queen")
                elif radio_button.text() == "Promote Pawn Using Popup Window":
                    print("set to promote pawn to queen using popup window")
    
        @cached_property
        def settings(self):
            return QSettings("LoneProgrammer2", "qsettingsTest1")
    
        @property
        def mapping_settings(self):
            return {
                "Promotion Dialog": [
                    ("auto-promote radio button checked state", self.oAutoPromoteRadioBtn),
                    (
                        "promote using popup radio button checked state",
                        self.oUsePopupRadioBtn,
                    ),
                ]
            }
    
        def read_settings(self):
            for group_key, values in self.mapping_settings.items():
                self.settings.beginGroup(group_key)
                for (key, checkbox) in values:
                    value = self.settings.value(key, type=bool)
                    checkbox.setChecked(value)
                self.settings.endGroup()
    
        def write_settings(self):
            for group_key, values in self.mapping_settings.items():
                self.settings.beginGroup(group_key)
                for (key, checkbox) in values:
                    self.settings.setValue(key, checkbox.isChecked())
                self.settings.endGroup()
    
        def closeEvent(self, event):
            super().closeEvent(event)
            self.write_settings()
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
    
        demo = MyApp()
        demo.show()
        sys.exit(app.exec_())