QSqlQueryModel -> QML-View liefert Segmentation Fault

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
EmaNymton
User
Beiträge: 174
Registriert: Sonntag 30. Mai 2010, 14:07

Hallo zusammen,
ich versuche momentan Daten einer Sqlite-Datenbank über ein Model an eine View in QML weiterzugeben und dort anzeigen zu lassen. Problem dabei ist, dass es keine Modelklasse gibt, die beliebig viele Spalten einer Abfrage an QML liefert. Da ich aber ein Projekt anstrebe, was viele Abfragen beansprucht, habe ich eine eigene Klasse von QSqlQueryModel abgeleitet und erzeuge die Userroles automatisch beim Erstellen der Abfrage.

Die Oberfläche besteht im wesentlichen aus Listviews, die die Daten anzeigen. Beim Klicken auf ein Element wird in QML die Methode des Controllers aufgerufen update_model(model, keyword, value), wo halt das model, art der abfrage und der jeweilige Wert übergeben werden. Die neue Listview wird in einem neuen Pagestack angezeigt.

Das funktioniert auch soweit, allerdings verabschiedet sich das Programm manchmal mit einem Segmentation Fault und zwar eigentlich immer nur dann, wenn die Noten generiert werden. Ich konnte es soweit eingrenzen, dass der Aufruf super(SqlQueryModel,self).setQuery(query) in der von QSqlQueryModel abgeleiteten Klasse den Fehler auslöst. Das Problem ist auch reproduzierbar, also wenn ich z.B. die Klasse 08A auswähle, dort den ersten Schüler zurückgehe und dann den nächsten Schüler, bricht das Programm ab:

Code: Alles auswählen

Clicked on At
SELECT kurse.kursId as kursId, stufe, fach           FROM kurse, lehrerunterrichtet           WHERE lehrerunterrichtet.lehrerId=13           AND kurse.kursId = lehrerunterrichtet.kursId           ORDER BY stufe, fach
Clicked on 464
SELECT s.schuelerId, vorname, nachname, substr(nachname,1,1) as alphabet                   FROM schueler s, kurswirdbesucht kwb                   WHERE s.schuelerId = kwb.schuelerId                   AND kursId=464 ORDER BY nachname
Clicked on 898
SELECT nachname, s.stufe, fach, note           FROM schueler s, kurse k, kurswirdbesucht kwb           WHERE kwb.schuelerId = s.schuelerId           AND k.kursId = kwb.kursId           AND s.schuelerId = 898
Clicked on 899
SELECT nachname, s.stufe, fach, note           FROM schueler s, kurse k, kurswirdbesucht kwb           WHERE kwb.schuelerId = s.schuelerId           AND k.kursId = kwb.kursId           AND s.schuelerId = 899
Segmentation fault
Komischerweise gibt es keinen Absturz, wenn ich vorher in einer anderen Stufe die Daten abrufe und danach oben beschriebene Schritte durchführe. Dann läuft es ganz normal durch.

Was mache ich falsch bzw. wie kann ich das noch weiter debuggen?
Ein Minimalbeispiel mit meiner Modelklasse funktioniert ohne Fehler, leider kann ich die DB nicht posten, da sie sensible Daten enthält.
Wäre für jeden Hinweis dankbar!

Gruß EmaNymton

Code: Alles auswählen

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

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

querys = {
"stufen": 'SELECT DISTINCT stufe FROM kurse \
           WHERE stufe!="" ORDER BY stufe',

"schueler_stufe" : 'SELECT schuelerId, vorname, nachname, \
                    substr(nachname,1,1) as alphabet \
                    FROM schueler \
                    WHERE stufe="{0}" \
                    ORDER BY nachname',

"schueler_kurs" : 'SELECT s.schuelerId, vorname, nachname, substr(nachname,1,1) as alphabet\
                   FROM schueler s, kurswirdbesucht kwb\
                   WHERE s.schuelerId = kwb.schuelerId\
                   AND kursId={0} ORDER BY nachname',

"noten" : 'SELECT nachname, s.stufe, fach, note\
           FROM schueler s, kurse k, kurswirdbesucht kwb\
           WHERE kwb.schuelerId = s.schuelerId\
           AND k.kursId = kwb.kursId\
           AND s.schuelerId = {0}',

"lehrer" : 'SELECT lehrerId, kuerzel FROM lehrer ORDER BY kuerzel',

"kurse" : 'SELECT kurse.kursId as kursId, stufe, fach\
           FROM kurse, lehrerunterrichtet\
           WHERE lehrerunterrichtet.lehrerId={0}\
           AND kurse.kursId = lehrerunterrichtet.kursId\
           ORDER BY stufe, fach'
}

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

    def setQuery(self, query):
        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

class Controller(QtCore.QObject):
    def __init__(self, querys):
        QtCore.QObject.__init__(self)
        self._querys = querys
        self._last_query = None
          
    @QtCore.Slot(QtCore.QObject, str, str)
    def update_model(self, model, keyword, value):
        query = self._querys[keyword].format(value)
        print query
        if self._last_query != query:
            model.setQuery(query)
            self._last_query = query


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

    db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
    db.setDatabaseName("noten.db")
    if db.open():
        stufen_model = SqlQueryModel()
        schueler_model = SqlQueryModel()
        noten_model = SqlQueryModel()
        lehrer_model = SqlQueryModel()
        kurse_model = SqlQueryModel()

        stufen_model.setQuery(querys["stufen"])
        schueler_model.setQuery(querys["schueler_stufe"].format('10X'))
        noten_model.setQuery(querys["noten"].format('1'))
        lehrer_model.setQuery(querys["lehrer"])
        kurse_model.setQuery(querys["kurse"].format('1'))

        controller = Controller(querys)
        view = QtDeclarative.QDeclarativeView()
        rc = view.rootContext()
        rc.setContextProperty('controller', controller)
        rc.setContextProperty('stufenModel', stufen_model)
        rc.setContextProperty('schuelerModel', schueler_model)
        rc.setContextProperty('notenModel', noten_model)
        rc.setContextProperty('lehrerModel', lehrer_model)
        rc.setContextProperty('kurseModel', kurse_model)

        view.setSource("main.qml")
        view.showFullScreen()

        sys.exit(app.exec_())
    else:
        print "database not open, quit!"
        sys.exit(1)
EmaNymton
User
Beiträge: 174
Registriert: Sonntag 30. Mai 2010, 14:07

Der Absturz lässt sich anscheinend umgehen, indem man vor dem Setzen des neuen Querys das Model mit clear zurücksetzt.

Code: Alles auswählen

def setQuery(self, query):
        super(SqlQueryModel,self).clear()
        super(SqlQueryModel,self).setQuery(query)
        self.generateRoleNames()
In bisherigen Tests lief damit das Programm ohne Absturz. Trotzdem frage ich mich immer noch warum?
Antworten