Richie_Eloh
Richie_Eloh

Reputation: 45

How to display items in a list in ListView in qml

The code is supposed to get the number of rows in the table of the database and use it as the number of models and also display all rows on the ListView after the btnShowList is clicked However, Nothing occurs upon clicking. There are two files.

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    property var numberOfModels: 1
    property var listofCust: []

    Rectangle{
        id: listViewContainer
        color: "pink"
        anchors.fill: parent
        ListModel {
            id: displayCustomers
            ListElement{
                firstName: "First Name"
                LastName: "Last Name"
                age: "Age"
                Sex: "Sex"
            }
        }
        Component{
            id:customersDelegate
            Row{
                spacing: 50
                Text{
                    text: firstName
                }
                Text {
                    text: LastName
                }
                Text {
                    text: age
                }
                Text {
                    text: Sex
                }
            }
        }

        ListView{
            anchors.fill: parent
            anchors.bottomMargin: 52
            model: displayCustomers
            delegate: customersDelegate
        }

        Button {
            id: btnShowList
            x: 518
            y: 440
            text: qsTr("show list")
            onClicked: {

                displayCustomers.append(backend.getCount)
            }
        }
    }

    Connections {
        target: backend

    function onGetCount(total, listofCust){
       count = total
       numberOfModels = total
       listofCus = listofCust
       return listofCust
        }
    }
}

main.py

import sys
import os
import mysql.connector


from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QObject, Slot, Signal

class MainWindow(QObject):
    def __init__(self):
        QObject.__init__(self)

    count = Signal(int)
    listOfCus = Signal([])
    @Slot()
    def getCount(self):
        db = mysql.connector.connect(
               host = "localhost",
               user = "root",
               passwd = "",
               database = "test"
           )
        mycursor = db.cursor()

        mycursor.execute("SELECT * FROM customer")
        listofCustomers = []
        total = 0
        for x in mycursor:
            total = total + 1
            print(x)
            listofCustomers.append(x)

        self.count.emit(total)
        print(total)
        values = mycursor
        print(values)
        print(listofCustomers)
        self.listOfCus.emit()


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    #Get Context
    main = MainWindow()
    engine.rootContext().setContextProperty("backend", main)
    engine.load(os.path.join(os.path.dirname(__file__), "main.qml"))

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

There is no error upon execution. When I print the listofCustomers and total i get the intended output. But the listView does not display anything after clicking the button

Upvotes: 0

Views: 2373

Answers (2)

eyllanesc
eyllanesc

Reputation: 244301

The OP expects the database information to magically show up in the view without at least sending that information to .qml.

On the other hand the line "displayCustomers.append (backend.getCount)" is confusing, what do you intend to happen with that code? In that code the getCount function is being added to the model which is obviously illogical.

The idea is to have a model (in python or QML) that has the roles that are the same as those used by the delegate, the slot should only serve to fill the model, in this case I will use a python model.

import os
from pathlib import Path
import sys

import mysql.connector

from PySide2.QtCore import Property, QCoreApplication, QObject, Qt, QUrl, Slot
from PySide2.QtGui import QGuiApplication, QStandardItem, QStandardItemModel
from PySide2.QtQml import QQmlApplicationEngine


CURRENT_DIRECTORY = Path(__file__).resolve().parent

FIRSTNAME_ROLE = Qt.UserRole
LASTNAME_ROLE = Qt.UserRole + 1
AGE_ROLE = Qt.UserRole + 2
SEX_ROLE = Qt.UserRole + 3


class Backend(QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._model = QStandardItemModel()
        self._model.setItemRoleNames(
            {
                FIRSTNAME_ROLE: b"firstname",
                LASTNAME_ROLE: b"lastname",
                AGE_ROLE: b"age",
                SEX_ROLE: b"sex",
            }
        )
        self.add_row("Fistname", "LastName", "Age", "Sex")

    def get_model(self):
        return self._model

    model = Property(QObject, fget=get_model, constant=True)

    @Slot()
    def loadFromDB(self):
        self._model.clear()

        db = mysql.connector.connect(
            host="localhost", user="root", passwd="", database="test"
        )
        cursor = db.cursor()
        cursor.execute("SELECT * FROM customer")
        for row in cursor.fetchall():
            firstname = row[0]
            lastname = row[1]
            age = row[2]
            sex = row[3]
            self.add_row(firstname, lastname, age, sex)

    def add_row(self, firstname, lastname, age, sex):
        item = QStandardItem()
        item.setData(firstname, FIRSTNAME_ROLE)
        item.setData(lastname, LASTNAME_ROLE)
        item.setData(age, AGE_ROLE)
        item.setData(sex, SEX_ROLE)

        self._model.appendRow(item)


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    # Get Context
    backend = Backend(app)
    engine.rootContext().setContextProperty("backend", backend)
    filename = os.fspath(CURRENT_DIRECTORY / "main.qml")
    url = QUrl.fromLocalFile(filename)

    def handle_object_created(obj, obj_url):
        if obj is None and url == obj_url:
            QCoreApplication.exit(-1)

    engine.objectCreated.connect(handle_object_created, Qt.QueuedConnection)
    engine.load(url)
    sys.exit(app.exec_())

main.qml

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15

Window {
    property var numberOfModels: 1
    property var listofCust: []

    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: listViewContainer

        color: "pink"
        anchors.fill: parent

        Component {
            id: customersDelegate

            Row {
                spacing: 50

                Text {
                    text: firstname
                }

                Text {
                    text: lastname
                }

                Text {
                    text: age
                }

                Text {
                    text: sex
                }

            }

        }

        ListView {
            anchors.fill: parent
            anchors.bottomMargin: 52
            model: backend.model
            delegate: customersDelegate
        }

        Button {
            id: btnShowList

            x: 518
            y: 440
            text: qsTr("show list")
            onClicked: {
                backend.loadFromDB();
            }
        }
    }
}

Upvotes: 3

JarMan
JarMan

Reputation: 8277

I think you've confused what a model and delegate are. Your model should contain WHAT data is to be displayed. Your delegate defines HOW that data is to be displayed. In your example, your delegate is not a visual item. It looks like it's retrieving data, which should go into the model. Here is an example of what it should look like:

ListView {
    // The model is the data
    model: [2,4,6,8]

    // The delegate is the view
    delegate: Text {
        text: modelData
    }
}

Upvotes: 1

Related Questions