Search code examples
pythonqtpyqtqmlpyside

How to update listview in qml from a python QThread using list?


I created a simple listview in qml, and I would like to update its value from a QThread using QStringListModel. I have tried several ways, but I could not really get it work. I appriciate all help. I am quite new in Qt. (Maybe I should pass the self.hosts variable value into another class?)

Here is my python code:

import os
from pathlib import Path
import sys
import PySide2.QtQml
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QObject, Slot, Signal, Property, QThread, QStringListModel

class Main(QObject):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)
        self.worker1 = Worker1()

    @Slot()
    def startCheck(self):
        self.worker1.start()

class Worker1(QThread):
    updateListView = Signal(list)
    
    def __init__(self):
        QThread.__init__(self)
        self.test_model = QStringListModel()
        self.updateListView.connect(self.updateModel)
        self.hosts = []

    def run(self):
        #Just keep it simple for test
        myList = ["A1", "A2", "A3", "A4", "B1", "B2", "B3"]
        for i in myList:
            if "A" in i:
                self.hosts.append(i)
        self.updateListView.emit(self.hosts)

    @Slot(list)
    def updateModel(self, hosts):
        self.test_model.setStringList(hosts)

    @Property(QObject, notify=updateListView)
    def model(self):
        return self.test_model

if __name__ == "__main__":

    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    main = Main()
    engine.rootContext().setContextProperty("main", main)

    worker1 = Worker1()
    engine.rootContext().setContextProperty("worker1", worker1)

    engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))

    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

Here is my qml code:

import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2


ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Main Program")

    Button {
        text: qsTr("Update")
        anchors.top: parent.top
        anchors.topMargin: 21
        anchors.horizontalCenterOffset: 0
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: main.startCheck()
    }

    ListView{
        id: listView
        width: 200
        height: 150
        anchors.top: parent.top
        anchors.topMargin: 55
        anchors.horizontalCenter: parent.horizontalCenter
        contentWidth: 0
        model: worker1.model
        delegate: Text { text: display; color: "black" }
    }
}

Solution

  • I have figured it out. If anyone knows a better solution, please let me know.

    python code:

    import os
    from pathlib import Path
    import sys
    import PySide2.QtQml
    from PySide2.QtGui import QGuiApplication
    from PySide2.QtQml import QQmlApplicationEngine
    from PySide2.QtCore import QObject, Slot, Signal, Property, QThread, QStringListModel
    
    class Main(QObject):
    
        getUpdateModel = Signal(list)
    
        def __init__(self, parent=None):
            super(Main, self).__init__(parent)
            self.worker1 = Worker1()
            self.worker1.updateListView.connect(self.upDateModel)
            self.test_model = QStringListModel()
    
    
        @Slot()
        def startCheck(self):
            self.worker1.start()
    
        @Property(QObject, notify=getUpdateModel)
        def model(self):
            return self.test_model
    
        @Slot(list)
        def upDateModel(self, hosts):
            self.test_model.setStringList(hosts)
    
    
    class Worker1(QThread):
        updateListView = Signal(list)
    
        def __init__(self):
            QThread.__init__(self)
    
            self.hosts = []
    
        def run(self):
            #Just keep it simple for test
            myList = ["A1", "A2", "A3", "A4", "B1", "B2", "B3"]
            for i in myList:
                if "A" in i:
                    self.hosts.append(i)
            self.updateListView.emit(self.hosts)
    
    
    if __name__ == "__main__":
    
        app = QGuiApplication(sys.argv)
        engine = QQmlApplicationEngine()
    
        main = Main()
        engine.rootContext().setContextProperty("main", main)
    
        engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
    
        if not engine.rootObjects():
            sys.exit(-1)
        sys.exit(app.exec_())
    

    qml code:

    import QtQuick 2.3
    import QtQuick.Controls 1.2
    import QtQuick.Window 2.2
    
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        title: qsTr("Main Program")
    
        Button {
            text: qsTr("Update")
            anchors.top: parent.top
            anchors.topMargin: 21
            anchors.horizontalCenterOffset: 0
            anchors.horizontalCenter: parent.horizontalCenter
            onClicked: main.startCheck()
        }
    
        ListView{
            id: listView
            width: 200
            height: 150
            anchors.top: parent.top
            anchors.topMargin: 55
            anchors.horizontalCenter: parent.horizontalCenter
            contentWidth: 0
            model: main.model
            delegate: Text { text: display; color: "black" }
        }
    }