Search code examples
pythonpyqtpyqt5qlistwidget

Pyqt5 refresh/update QListWidget based on event in custom widget?


I am trying to update/refresh a QListWidget after I click a button in a CustomFormWidget.

What I am trying to do is when I click the button in a CustomFormWidget, it updates the class variable data in the ParentWid and then rebuilds/updated/refreshes ParentWid.ui.listWidget. Where and how should I add this logic. Should I add to my CustomFormWidget.on_click method or should my logic be set in the ParentWid?

So far my code looks like:

from PyQt5 import QtCore, QtGui, QtWidgets


class ParentWid(QtWidgets.QDialog):
    data = [
        {
            'id': 1,
            'name': 'pkg-foo',
            'version': '0.1',
            'installed': False
        },
        {
            'id': 2,
            'name': 'pkg-bar',
            'version': '0.1',
            'installed': False
        }
    ]

    def __init__(self, parent=None):
        super(ParentWid, self).__init__(parent)
        self.ui = Ui_ParentWidget()
        self.ui.setupUi(self)
        self.set_widget_data()

    @classmethod
    def update_installed_packages(cls, value):
        for item in ParentWid.data:
            if item["id"] == value:
                item["installed"] = not item["installed"]

    def set_widget_data(self):
        for item in ParentWid.data:
            custFormItem = CustomFormWidget(item)
            lst_item = QtWidgets.QListWidgetItem()
            self.ui.listWidget.insertItem(
                self.ui.listWidget.count(),
                lst_item
            )
            self.ui.listWidget.setItemWidget(lst_item, custFormItem)
            lst_item.setSizeHint(custFormItem.sizeHint())



class CustomFormWidget(QtWidgets.QWidget):
    def __init__(self, data, parent=None):
        super(CustomFormWidget, self).__init__(parent)

        self.ui = Ui_Form()
        self.ui.setupUi(self)
        self.data = data

        self.ui.pkg_name.setText(self.data['name'])
        self.ui.pkg_version.setText(self.data['version'])

        if self.data["installed"] is True:
            self.ui.install_btn.setText("Installed")
            self.ui.install_btn.setEnabled(False)

        self.ui.install_btn.clicked.connect(self.on_click)

    def on_click(self):
        print('installed pkg')
        ## install LOGIG
        ParentWid.update_installed_packages(self.data["id"])

Solution

  • In this case it is better that each item manages its data, for this we can pass the QListWidget to the custom widget and in it handle through roles. In the following example, each widget modifies its data, and when the application is closed, the updated data is printed:

    class Roles:
        IdRole = QtCore.Qt.UserRole + 1000
        NameRole = QtCore.Qt.UserRole + 1001
        VersionRole = QtCore.Qt.UserRole + 1002
        InstalledRole = QtCore.Qt.UserRole + 1003
    
    class ParentWid(QtWidgets.QDialog, Ui_ParentWidget):
        def __init__(self, data={}, parent=None):
            super(ParentWid, self).__init__(parent)
            self.data = data
            self.setupUi(self)
            self.set_widget_data()
    
        def set_widget_data(self):
            for item in self.data:
                lst_item = QtWidgets.QListWidgetItem()
                self.listWidget.addItem(lst_item)
                custFormItem = CustomFormWidget(item, lst_item)
                lst_item.setSizeHint(custFormItem.sizeHint())
    
        def print_results(self):
            v = (("id", Roles.IdRole), ("name", Roles.NameRole), ("version", Roles.VersionRole), ("installed", Roles.InstalledRole),)
            results = []
            for i in range(self.listWidget.count()):
                it = self.listWidget.item(i)
                d = {}
                for k, r in v:
                    d[k] = it.data(r)
                results.append(d)
            print(results)
    
        # test
        def closeEvent(self, event):
            self.print_results()
            super(ParentWid, self).closeEvent(event)
    
    class CustomFormWidget(QtWidgets.QWidget, Ui_Form):
        def __init__(self, data, item, parent=None):
            super(CustomFormWidget, self).__init__(parent)
            self._item = item
            self._item.listWidget().setItemWidget(self._item, self)
            self.setupUi(self)
            v = (("id", Roles.IdRole), ("name", Roles.NameRole), ("version", Roles.VersionRole), ("installed", Roles.InstalledRole),)
            for k, r in v:
                self._item.setData(r, data[k])
            self.update_view()
            self.install_btn.clicked.connect(self.on_click)
    
        def update_view(self):
            self.pkg_name.setText(self._item.data(Roles.NameRole))
            self.pkg_version.setText(self._item.data(Roles.VersionRole))
            v = self._item.data(Roles.InstalledRole)
            self.install_btn.setText("Installed" if v else "Not Installed")
            self.install_btn.setEnabled(not v)
    
        @QtCore.pyqtSlot()
        def on_click(self):
            self._item.setData(Roles.InstalledRole, not self._item.data(Roles.InstalledRole))
            self.update_view()
    
            v = (("id", Roles.IdRole), ("name", Roles.NameRole), ("version", Roles.VersionRole), ("installed", Roles.InstalledRole),)
            for k, r in v:
                print(k, self._item.data(r))
    
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
    
        data = [
            {
                'id': 1,
                'name': 'pkg-foo',
                'version': '0.1',
                'installed': False
            },
            {
                'id': 2,
                'name': 'pkg-bar',
                'version': '0.1',
                'installed': False
            }
        ]
        w = ParentWid(data)
        w.show()
        sys.exit(app.exec_())