pythonpyqtpyqt5python-3.7qsettings

How to save selected Items to Qsettings from QListWidget, QTableWidget


I am Using PyQt5 and Py3.7, I am trying to loop through all my Qlistwidgets and save their string data, but also save all selected items on that widget. I slightly modified the loop from here, but am having some trouble getting the selected items to save and restore using the listwidget array loop. I checked the docs but can't seem to understand how to add additional options into the array (such as save all selected items), from the Qt docs for SetArrayIndex, here.

My listWidgets have selectionMode set to MultiSelection. I am currently saving using this:

def save_list_data(self):
    self.settings = QSettings("data.ini", QSettings.IniFormat)
    for name, obj in inspect.getmembers(self):
        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()

And then restoring the listWidget data using:

def open_list_data(self):
    self.settings = QSettings("data.ini", QSettings.IniFormat)
    for name, obj in inspect.getmembers(self):
        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 != None:
                    obj.addItem(value)
            self.settings.endArray()

This works fine for the data, but how can I get the selectedItems from the ListWidgets to save and restore as well?


Solution

  • For my solution take into account the following:

    Considering the above, the solution is:

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class Widget(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.listwidget_1 = QtWidgets.QListWidget(
                objectName="listwidget_1", 
                selectionMode=QtWidgets.QAbstractItemView.MultiSelection
            )
            listwidget_2 = QtWidgets.QListWidget(
                objectName="listwidget_2", 
                selectionMode=QtWidgets.QAbstractItemView.MultiSelection
            )
    
            lay = QtWidgets.QVBoxLayout(self)
            lay.addWidget(self.listwidget_1)
            lay.addWidget(listwidget_2)
    
            self.read_settings()
    
        def closeEvent(self, event):
            self.write_settings()
            super().closeEvent(event)
    
        def read_settings(self):
            settings = QtCore.QSettings("data.ini", QtCore.QSettings.IniFormat)
            childrens = self.findChildren(QtWidgets.QWidget)
            for children in childrens:
                if isinstance(children, QtWidgets.QListWidget) and children.objectName():
                    settings.beginGroup(children.objectName())
                    items = settings.value("items")
                    selecteditems = settings.value("selecteditems")
                    selectionMode = settings.value("selectionMode", type=QtWidgets.QAbstractItemView.SelectionMode)
                    children.setSelectionMode(selectionMode)
                    # In the first reading the initial values must be established
                    if items is None:
                        if children.objectName() == "listwidget_1":
                            for i in range(10):
                                children.addItem(QtWidgets.QListWidgetItem(str(i)))
                        elif children.objectName() == "listwidget_2":
                            for i in "abcdefghijklmnopqrstuvwxyz":
                                children.addItem(QtWidgets.QListWidgetItem(i))
                    else:
                        stream = QtCore.QDataStream(items, QtCore.QIODevice.ReadOnly)
                        while not stream.atEnd():
                            it = QtWidgets.QListWidgetItem()
                            stream >> it
                            children.addItem(it)
                        stream = QtCore.QDataStream(selecteditems, QtCore.QIODevice.ReadOnly)
                        while not stream.atEnd():
                            row = stream.readInt()
                            it = children.item(row)
                            it.setSelected(True)
                    settings.endGroup()
    
        def write_settings(self):
            settings = QtCore.QSettings("data.ini", QtCore.QSettings.IniFormat)
            childrens = self.findChildren(QtWidgets.QWidget)
            for children in childrens:
                if isinstance(children, QtWidgets.QListWidget) and children.objectName():
                    settings.beginGroup(children.objectName())
                    items = QtCore.QByteArray()
                    stream = QtCore.QDataStream(items, QtCore.QIODevice.WriteOnly)
                    for i in range(children.count()):
                        stream << children.item(i)
                    selecteditems = QtCore.QByteArray()
                    stream = QtCore.QDataStream(selecteditems, QtCore.QIODevice.WriteOnly)
                    for it in children.selectedItems():
                        stream.writeInt(children.row(it))
                    settings.setValue("items", items)
                    settings.setValue("selecteditems", selecteditems)
                    settings.setValue("selectionMode", children.selectionMode())
                    settings.endGroup()
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        w = Widget()
        w.show()
        sys.exit(app.exec_())
    

    Plus:

    import contextlib
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class SettingsManager:
        def __init__(self, filename):
            self.m_settings = QtCore.QSettings(filename, QtCore.QSettings.IniFormat)
    
        @property
        def settings(self):
            return self.m_settings
    
        def read(self, widget):
            self.settings.beginGroup(widget.objectName())
            if isinstance(widget, QtWidgets.QAbstractItemView):
                selectionMode = self.settings.value(
                    "selectionMode", type=QtWidgets.QAbstractItemView.SelectionMode
                )
                widget.setSelectionMode(selectionMode)
            if isinstance(widget, QtWidgets.QListWidget):
                items = self.settings.value("items")
                selecteditems = self.settings.value("selecteditems")
                # In the first reading the initial values must be established
                if items is None:
                    self.read_defaults(widget)
                else:
                    stream = QtCore.QDataStream(items, QtCore.QIODevice.ReadOnly)
                    while not stream.atEnd():
                        it = QtWidgets.QListWidgetItem()
                        stream >> it
                        widget.addItem(it)
                    stream = QtCore.QDataStream(
                        selecteditems, QtCore.QIODevice.ReadOnly
                    )
                    while not stream.atEnd():
                        row = stream.readInt()
                        it = widget.item(row)
                        if it is not None:
                            it.setSelected(True)
            if isinstance(widget, QtWidgets.QTableWidget):
                rowCount = self.settings.value("rowCount", type=int)
                columnCount = self.settings.value("columnCount", type=int)
                widget.setRowCount(rowCount)
                widget.setColumnCount(columnCount)
                items = self.settings.value("items")
                if items is None:
                    self.read_defaults(widget)
                else:
                    stream = QtCore.QDataStream(items, QtCore.QIODevice.ReadOnly)
                    while not stream.atEnd():
                        it = QtWidgets.QTableWidgetItem()
                        i = stream.readInt()
                        j = stream.readInt()
                        stream >> it
                        widget.setItem(i, j, it)
                    selecteditems = self.settings.value("selecteditems")
                    stream = QtCore.QDataStream(
                        selecteditems, QtCore.QIODevice.ReadOnly
                    )
                    while not stream.atEnd():
                        i = stream.readInt()
                        j = stream.readInt()
                        it = widget.item(i, j)
                        if it is not None:
                            it.setSelected(True)
            self.settings.endGroup()
    
        def write(self, widget):
            self.settings.beginGroup(widget.objectName())
            if isinstance(widget, QtWidgets.QAbstractItemView):
                self.settings.setValue("selectionMode", widget.selectionMode())
            if isinstance(widget, QtWidgets.QListWidget):
                items = QtCore.QByteArray()
                stream = QtCore.QDataStream(items, QtCore.QIODevice.WriteOnly)
                for i in range(widget.count()):
                    stream << widget.item(i)
                self.settings.setValue("items", items)
                selecteditems = QtCore.QByteArray()
                stream = QtCore.QDataStream(
                    selecteditems, QtCore.QIODevice.WriteOnly
                )
                for it in widget.selectedItems():
                    stream.writeInt(widget.row(it))
    
                self.settings.setValue("selecteditems", selecteditems)
            if isinstance(widget, QtWidgets.QTableWidget):
                self.settings.setValue("rowCount", widget.rowCount())
                self.settings.setValue("columnCount", widget.columnCount())
                items = QtCore.QByteArray()
                stream = QtCore.QDataStream(items, QtCore.QIODevice.WriteOnly)
                for i in range(widget.rowCount()):
                    for j in range(widget.columnCount()):
                        it = widget.item(i, j)
                        if it is not None:
                            stream.writeInt(i)
                            stream.writeInt(j)
                            stream << it
                self.settings.setValue("items", items)
                selecteditems = QtCore.QByteArray()
                stream = QtCore.QDataStream(
                    selecteditems, QtCore.QIODevice.WriteOnly
                )
                for it in widget.selectedItems():
                    # print(it.row(), it.column())
                    stream.writeInt(it.row())
                    stream.writeInt(it.column())
                self.settings.setValue("selecteditems", selecteditems)
            self.settings.endGroup()
    
        def release(self):
            self.m_settings.sync()
    
        def read_defaults(self, widget):
            if widget.objectName() == "listwidget_1":
                widget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
                for i in range(10):
                    widget.addItem(QtWidgets.QListWidgetItem(str(i)))
            elif widget.objectName() == "listwidget_2":
                widget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
                for i in "abcdefghijklmnopqrstuvwxyz":
                    widget.addItem(QtWidgets.QListWidgetItem(i))
            elif widget.objectName() == "tablewidget":
                widget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
                widget.setRowCount(10)
                widget.setColumnCount(10)
                for i in range(widget.rowCount()):
                    for j in range(widget.columnCount()):
                        it = QtWidgets.QTableWidgetItem("{}-{}".format(i, j))
                        widget.setItem(i, j, it)
    
    
    @contextlib.contextmanager
    def settingsContext(filename):
        manager = SettingsManager(filename)
        try:
            yield manager
        finally:
            manager.release()
    
    
    class Widget(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
    
            self.listwidget_1 = QtWidgets.QListWidget(objectName="listwidget_1")
            listwidget_2 = QtWidgets.QListWidget(objectName="listwidget_2")
    
            tablewidget = QtWidgets.QTableWidget(objectName="tablewidget")
    
            lay = QtWidgets.QVBoxLayout(self)
            lay.addWidget(self.listwidget_1)
            lay.addWidget(listwidget_2)
            lay.addWidget(tablewidget)
    
            self.read_settings()
    
        def closeEvent(self, event):
            self.write_settings()
            super().closeEvent(event)
    
        def read_settings(self):
            with settingsContext("data.ini") as m:
                for children in self.findChildren(QtWidgets.QWidget):
                    if children.objectName():
                        m.read(children)
    
        def write_settings(self):
            with settingsContext("data.ini") as m:
                for children in self.findChildren(QtWidgets.QWidget):
                    if children.objectName():
                        m.write(children)
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        w = Widget()
        w.resize(640, 480)
        w.show()
        sys.exit(app.exec_())