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 ;)