Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Populating a ListView with Python Objects



  • I want to populate a ListView with objects from Python. I've been able to make a ListView work using ListModel+ ListElement in QML but not from Python. From what I've read, it seems I should use a QAbstractListModel but so far I haven't been able to get the values of the python objects to show up. It seems my QAbstractListModel's rowCount() and data() functions don't even get called.

    I posted a (hopefully) lean example in a GitLab project

    What am I missing?
    Thanks,
    -chris


  • Banned

    I cannot speak to your QML code specifically but you are using PyQt4 syntax within your example

    This sys.exit(app.exec_()) is either just this app.exec() or if necessary sys.exit(app.exec())

    Next if you are not using Command Line arguments as you are not then

    This app = QGuiApplication(sys.argv) should be this app = QGuiApplication([])

    Further if you are going to handle Command Line arguments look into argparser as it does a much better job of handling them and it is already been invented

    Lastly always import any none PySide2 libraries after you declare your PySide2 objects as this makes sure that they utilize the correct version of PySide



  • @Denni-0 Thanks for the tips Denni!



  • @christofer
    Having glanced at your code. I can't help you if you say rowCount() or data() are never called. I know nothing about QML so only you know if your code is being called. It looks fine as-is except that your data() method needs work.

        def data(self, index, role=Qt.DisplayRole):
            print("data: " + self.items)
            return self.items
    

    First it must return the correct indvidual element according to the index: QModelIndex parameter, and second it must only do so for the DisplayRole case on the role parameter, not for all cases as you have done. Until you have done those 2 it won't work right.

    If/when that's sorted out, you're trying to have data() return a class MyItem data item, say for the DisplayRole when it wants to show it. I can't imagine what a Qt view is going to make of that. If you're lucky it might use str(instanceMyItem), and I don't know what that looks like.



  • @JonB @Denni-0 I've made changes based on your feedback. Thanks. Still not sure the changes are correct because the functions do not get called. But at least it's closer to correct.



  • @Denni-0 just a side note, but the PySide2 QCoreApplication, QGuiApplication, and QApplication have exec_() functions. The docs say things like "see exec()" but it doesn't exist. I get an AttributeError if I try to use it. I'm using PySide2 version 5.14.1



  • @christofer You cannot expose a Python Object to QML, what you can do is export a QObject through the model as I have implemented in the PR to your repo.



  • @christofer You can also make the model expose each attribute of the items through the roles as I show in my second PR



  • @christofer said in Populating a ListView with Python Objects:

    Still not sure the changes are correct because the functions do not get called

    That sounds like a QML issue. You should heed what @eyllanesc has written, as he seems to know about Python to QML.

    It really does not matter to try to change from exec_() to exec(). PyQt exposes an exec() (as well as exec_()), PySide does not. As I said, nothing in your existing code I saw needed changing, it was not PyQt4 syntax, it was all correct, except for the implementation of data().



  • Thanks @eyllanesc for the MRs! They're very helpful.

    On using roles for a and b (commit 1e5447) vs just using the display role (commit 83b42b) ... which do you recommend? I like the simple access in the QML model.a, model.b but it seems like making custom roles could conflict with Qt expecting the roles in PySide2.QtCore.Qt.ItemDataRole which makes me think model.display.a, model.display.b is a safer long-term solution.


  • Banned

    Note I just glanced at your new code and you have app.exec_() and that requires the sys.exit( ) wrapper remove the underscore and just have app.exec() as that is the Qt5 version of it


  • Banned

    Okay I tried to use your example and create a similar more simplistic example using straight PySide2 but stumbled over using that QAbstractListModel so dropped back to an even more simplistic version the QStandardItemModel -- I am also including a link to where someone explains the QAbstractListModel a bit more clearly. Hopefully these two items in conjunction will help you with your issue.

    from PySide2.QtCore    import QAbstractListModel, Qt, QModelIndex, QVariant
    from PySide2.QtGui     import QStandardItemModel, QStandardItem
    from PySide2.QtWidgets import QApplication, QWidget, QListView, QHBoxLayout, QVBoxLayout
    
    #https://stackoverflow.com/questions/46814961/how-to-insert-edit-qabstractlistmodel-in-python-and-qml-updates-automatically
    
    class MyItem:
        def __init__(self, a, b):
            self.MyName = a
            self.MyAge  = b
    
        @property
        def Name(self):
            return self.MyName
            
        @property
        def Age(self):
            return self.MyAge
    
        @property
        def NameAge(self):
            MyNameAge = str(self.MyName) + ' ' + str(self.MyAge)
            return MyNameAge
    
    class ListView(QListView):
        def __init__(self):
            QListView.__init__(self)
            Hght = 100
            Wdth = 100
            self.setMaximumWidth(Wdth)
            self.setMaximumHeight(Hght)
    
    class ApplicationWindow(QWidget):
        def __init__(self):
            QWidget.__init__(self)
            Hght = 150
            Wdth = 225
            self.resize(Wdth, Hght)
            self.setWindowTitle('ListView')
    
            self.Model = QStandardItemModel()
    
            NamesAges = [['Al', 10],['Bob',20],['Chad',30]]
            for Vals in NamesAges:
                Item = MyItem(Vals[0], Vals[1]).NameAge
                Row = QStandardItem(Item)
                self.Model.appendRow(Row)
    
            self.TheView = ListView()
            self.TheView.setModel(self.Model)
            
            HBox = QHBoxLayout()
            HBox.addWidget(self.TheView)
            HBox.addStretch(1)
            
            VBox = QVBoxLayout()
            VBox.addLayout(HBox)
            VBox.addStretch(1)
    
            self.setLayout(VBox)
    
            print("app initialized")
    
    if __name__ == '__main__':
        MainEvntHndlr = QApplication([])
    
        MainApp = ApplicationWindow()
        MainApp.show()
    
        MainEvntHndlr.exec()
    
      # If anyone wants more extensive free help I run an online lab-like classroom-like 
      # message server feel free and drop by you will not be able to post until I clear 
      # you as a student as this prevents spammers so if interested here is the invite
      # https://discord.gg/3D8huKC
    

Log in to reply