Search code examples
pythonsavereloadqlistwidget

How can I save and reload items in QListWidgets after closing app?


I'm quite new in the world of programming in python. I'm coding a GUI that allows users to insert some data. I'm using PyQt5 and trying to use some functions to save and reload widgets state from the .ini file after reopening the app. That's just an example of code:

from PyQt5.QtCore import *
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(366, 376)
        self.pushButton = QtWidgets.QPushButton(Form)
        self.pushButton.setGeometry(QtCore.QRect(60, 70, 93, 28))
        self.pushButton.setObjectName("pushButton")
        self.listWidget = QtWidgets.QListWidget(Form)
        self.listWidget.setGeometry(QtCore.QRect(60, 110, 256, 192))
        self.listWidget.setObjectName("listWidget")
        self.lineEdit = QtWidgets.QLineEdit(Form)
        self.lineEdit.setGeometry(QtCore.QRect(170, 70, 113, 21))
        self.lineEdit.setObjectName("lineEdit")

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.pushButton.setText(_translate("Form", "ADD"))

class MyForm(QtWidgets.QWidget):
    settings = QSettings("gui.ini", QSettings.IniFormat)

    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.ui = Ui_Form()
        self.ui.setupUi(self)

        restore(self.settings)

        self.ui.pushButton.clicked.connect(self.addbutton)

    def addbutton(self):
        if self.ui.lineEdit.text():
            self.ui.listWidget.addItem(self.ui.lineEdit.text().upper())
            self.ui.lineEdit.setText("")

    def closeEvent(self, event):
        save(self.settings)
        QtWidgets.QWidget.closeEvent(self, event)

def value_is_valid(val):
    if isinstance(val, QtGui.QPixmap):
        return not val.isNull()
    return True


def restore(settings):
    finfo = QtCore.QFileInfo(settings.fileName())

    if finfo.exists() and finfo.isFile():
        for w in QtWidgets.qApp.allWidgets():
            if w.objectName():
                mo = w.metaObject()
                for i in range(mo.propertyCount()):
                    prop = mo.property(i)
                    name = prop.name()
                    last_value = w.property(name)
                    key = "{}/{}".format(w.objectName(), name)
                    if not settings.contains(key):
                        continue
                    val = settings.value(key, type=type(last_value),)
                    if (
                        val != last_value
                        and value_is_valid(val)
                        and prop.isValid()
                        and prop.isWritable()
                    ):
                        w.setProperty(name, val)


def save(settings):
    for w in QtWidgets.qApp.allWidgets():
        if w.objectName():
            mo = w.metaObject()
            for i in range(mo.propertyCount()):
                prop = mo.property(i)
                name = prop.name()
                key = "{}/{}".format(w.objectName(), name)
                val = w.property(name)
                if value_is_valid(val) and prop.isValid() and prop.isWritable():
                    settings.setValue(key, w.property(name))

if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    form = MyForm()

    form.show()
    sys.exit(app.exec_())

I'd like to save and reload items from the QListWidgets in my form. At the moment it doesn't happen. Any help and suggestion is appreciated. Thanks

#

UPDATE:

Thanks @NicholasTJ I think that's very usefull. Just one last question. As first parameter of def gui_save(ui: QWidget, settings: QSettings, uiName="uiwidget") when I call this method and use "self", it seems is not working. That's an example linking to my first answer:

gui_save(self, self.settings, MyForm)

These are the definitions I'm trying to use:

from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import *
import inspect


def GetHandledTypes():
    return QComboBox, QLineEdit, QCheckBox, QRadioButton, QSpinBox, QSlider, QListWidget


def IsHandledType(widget):
    return any(isinstance(widget, t) for t in GetHandledTypes())


# ===================================================================
# save "ui" controls and values to registry "setting"
# ===================================================================

def gui_save(ui: QWidget, settings: QSettings, uiName="uiwidget"):
    namePrefix = f"{uiName}/"
    settings.setValue(namePrefix + "geometry", ui.saveGeometry())

    for name, obj in QtWidgets.qApp.allWidgets():
        if not IsHandledType(obj):
            continue

        name = obj.objectName()
        value = None
        if isinstance(obj, QComboBox):
            index = obj.currentIndex()  # get current index from combobox
            value = obj.itemText(index)  # get the text for current index

        if isinstance(obj, QLineEdit):
            value = obj.text()

        if isinstance(obj, QCheckBox):
            value = obj.isChecked()

        if isinstance(obj, QRadioButton):
            value = obj.isChecked()

        if isinstance(obj, QSpinBox):
            value = obj.value()

        if isinstance(obj, QSlider):
            value = obj.value()

        if isinstance(obj, QListWidget):
            settings.beginWriteArray(name)
            for i in range(obj.count()):
                settings.setArrayIndex(i)
                settings.setValue(namePrefix + name, obj.item(i).text())
            settings.endArray()
        elif value is not None:
            settings.setValue(namePrefix + name, value)


# ===================================================================
# restore "ui" controls with values stored in registry "settings"
# ===================================================================

def gui_restore(ui: QWidget, settings: QSettings, uiName="uiwidget"):
    from distutils.util import strtobool

    namePrefix = f"{uiName}/"
    geometryValue = settings.value(namePrefix + "geometry")
    if geometryValue:
        ui.restoreGeometry(geometryValue)

    for name, obj in inspect.getmembers(ui):
        if not IsHandledType(obj):
            continue

        name = obj.objectName()
        value = None
        if not isinstance(obj, QListWidget):
            value = settings.value(namePrefix + name)
            if value is None:
                continue

        if isinstance(obj, QComboBox):
            index = obj.findText(value)  # get the corresponding index for specified string in combobox

            if index == -1:  # add to list if not found
                obj.insertItems(0, [value])
                index = obj.findText(value)
                obj.setCurrentIndex(index)
            else:
                obj.setCurrentIndex(index)  # preselect a combobox value by index

        if isinstance(obj, QLineEdit):
            obj.setText(value)

        if isinstance(obj, QCheckBox):
            obj.setChecked(strtobool(value))

        if isinstance(obj, QRadioButton):
            obj.setChecked(strtobool(value))

        if isinstance(obj, QSlider):
            obj.setValue(int(value))

        if isinstance(obj, QSpinBox):
            obj.setValue(int(value))

        if isinstance(obj, QListWidget):
            size = settings.beginReadArray(namePrefix + name)
            for i in range(size):
                settings.setArrayIndex(i)
                value = settings.value(namePrefix + name)
                if value is not None:
                    obj.addItem(value)
            settings.endArray()

Solution

  • I resolved finally using these functions. Hope this could be usefull for someone is needing a working gui save and restore.

    def gui_save(self):
        save_settings_message()
        for name, obj in inspect.getmembers(self.ui):
            if isinstance(obj, QComboBox):
                name = obj.objectName()
                index = obj.currentIndex()
                text = obj.itemText(index)
                self.settings.setValue(name, text)
            if isinstance(obj, QLineEdit):
                name = obj.objectName()
                value = obj.text()
                self.settings.setValue(name, value)
            if isinstance(obj, QCheckBox):
                name = obj.objectName()
                state = obj.isChecked()
                self.settings.setValue(name, state)
            if isinstance(obj, QRadioButton):
                name = obj.objectName()
                value = obj.isChecked()
                self.settings.setValue(name, value)
            if isinstance(obj, QSpinBox):
                name = obj.objectName()
                value = obj.value()
                self.settings.setValue(name, value)
            if isinstance(obj, QSlider):
                name = obj.objectName()
                value = obj.value()
                self.settings.setValue(name, value)
            if isinstance(obj, QListWidget):
                name = obj.objectName()
                self.settings.beginWriteArray(name)
                for i in range(obj.count()):
                    self.settings.setArrayIndex(i)
                    self.settings.setValue(name, obj.item(i).text())
                self.settings.endArray()
            if isinstance(obj, QDateEdit):
                name = obj.objectName()
                value = obj.date()
                self.settings.setValue(name, value)
    
    def gui_restore(self):
        from distutils.util import strtobool
        for name, obj in inspect.getmembers(self.ui):
            if isinstance(obj, QComboBox):
                name = obj.objectName()
                value = (self.settings.value(name))
                if value == "":
                    continue
                index = obj.findText(value)
                if index == -1:
                    obj.insertItems(0, [value])
                    index = obj.findText(value)
                    obj.setCurrentIndex(index)
                else:
                    obj.setCurrentIndex(index)
            if isinstance(obj, QLineEdit):
                name = obj.objectName()
                value = (self.settings.value(name))
                obj.setText(value)
            if isinstance(obj, QCheckBox):
                name = obj.objectName()
                value = self.settings.value(name)
                if value is not None:
                    obj.setChecked(strtobool(value))
            if isinstance(obj, QRadioButton):
                name = obj.objectName()
                value = self.settings.value(name)
                if value is not None:
                    obj.setChecked(strtobool(value))
            if isinstance(obj, QSlider):
                name = obj.objectName()
                value = self.settings.value(name)
                if value is not None:
                    obj.setValue(int(value))
            if isinstance(obj, QSpinBox):
                name = obj.objectName()
                value = self.settings.value(name)
                if value is not None:
                    obj.setValue(int(value))
            if isinstance(obj, QListWidget):
                name = obj.objectName()
                size = self.settings.beginReadArray(name)
                for i in range(size):
                    self.settings.setArrayIndex(i)
                    value = self.settings.value(name)
                    if value is not None:
                        obj.addItem(value)
                self.settings.endArray()
            if isinstance(obj, QDateEdit):
                name = obj.objectName()
                value = self.settings.value(name)
                if value is not None:
                    obj.setDate(value)