erweitertes SqlQueryModel für QML mit PySide

Ich benötige für QML ein Model, dass Daten von Sql-Abfragen liefert. Da das durchaus umfangreich werden kann, habe ich nach einer Möglichkeit gesucht, beliebige Abfragen, die auch mehr als eine Ergebnisspalte beinhalten, in ein Model zu bekommen und die Ergebnisspalten jeweils als eigene Userrole im Model zu definieren. Grundlage war dieser Link, der das Ganze allerdings in C++ löste. Ich habe es also nach Python übertragen und kann jetzt bequem eine Abfrage an die Sqlite-DB stellen und habe alle Spaltennamen nachher in QML zur Verfügung.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
from PySide import QtSql, QtGui, QtDeclarative, QtCore

class SqlTest(QtCore.QObject):
    def __init__(self):
        QtCore.QObject.__init__(self)
        db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName(":memory:")
        db.open()
        # create table and put in some data
        query = QtSql.QSqlQuery()
        query.exec_("CREATE TABLE table1 (pkey INTEGER PRIMARY KEY,\
                                                    name TEXT, number INT)")
        query.exec_("INSERT INTO table1 (name,number) values ('Alice',12345)")
        query.exec_("INSERT INTO table1 (name,number) values ('Bob',67890)")
        query.exec_("INSERT INTO table1 (name,number) values ('Carl',133742)")
        # create view
        self.view = QtDeclarative.QDeclarativeView()
        self.context = self.view.rootContext()
        self.pkey = 1
        self.sql_model = SqlQueryModel(self)
        sql_query = "SELECT name, number FROM table1\
                          WHERE table1.pkey={pkey}".format(pkey=self.pkey)
        self.sql_model.setQuery(sql_query)
        self.context.setContextProperty('sqlModel', self.sql_model)
        self.view.setSource("main.qml")
        self.root = self.view.rootObject()
        self.view.show()

        self.root.populate_sql_model.connect(self.populate_sql_model)

    def populate_sql_model(self):
        # cycle through your data, just for demo
        self.pkey = (self.pkey % 3) + 1
        sql_query = "SELECT name, number FROM table1\
                          WHERE table1.pkey={pkey}".format(pkey=self.pkey)
        self.sql_model.setQuery(sql_query)

class SqlQueryModel(QtSql.QSqlQueryModel):
    def __init__(self, parent=None):
        super(SqlQueryModel, self).__init__(parent)

    def setQuery(self, query):
        super(SqlQueryModel, self).clear()
        super(SqlQueryModel, self).setQuery(query)
        self.generateRoleNames()

    def generateRoleNames(self):
        roleNames = super(SqlQueryModel, self).roleNames()
        for i in range(self.record().count()):
            roleNames[QtCore.Qt.UserRole + i + 1] =\
                                            str(self.record().fieldName(i))
        self.setRoleNames(roleNames)

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role < QtCore.Qt.UserRole:
            value = super(SqlQueryModel, self).data(index, role)
        else:
            columnIdx = role - QtCore.Qt.UserRole - 1
            modelIndex = QtCore.QModelIndex(self.index(index.row(),
                                                       columnIdx))
            value = super(SqlQueryModel, self).data(modelIndex,
                                                      QtCore.Qt.DisplayRole)
        return value

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    sql_test = SqlTest()
    app.exec_()

Und der Inhalt der QML-Datei:

 import QtQuick 1.1

Rectangle {
    id: root
    width: 500
    height: 200

    signal populate_sql_model()

    ListView {
         id: list_view
         anchors.fill: parent
         focus: true
         model: sqlModel
         delegate: sqlDelegate
     }

    Component {
         id: sqlDelegate

         Item {
            width: 300; height: 100

            Text {
                 width: parent.width
                 height: parent.heigth
                 id: text_name
                 text: "Name: " + name
                 font.pointSize: 50;
             }
             Text {
                 width: parent.width
                 height: parent.heigth
                 anchors { top: text_name.bottom}
                 text: "Number: " + number
                 font.pointSize: 50;

             }
         }
     }

     Timer {
         interval: 1000; running: true; repeat: true
         onTriggered: populate_sql_model()
     }
}

Zum Testen habe ich einfach einen Timer gesetzt, der im Prinzip nichts anderes macht, als durch die drei Datensätze durchzugehen. So sieht dann das fertige Produkt aus, natürlich erscheint jede Sekunde ein neuer Datensatz 😉

qml

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.