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