[gelöst] QSortFilterProxyModel: Fehler bei Sortierung, Bug?

Alles rund um die Programmierung mit Qt
Antworten
gk_17
Beiträge: 37
Registriert: 4. Oktober 2009 19:20

[gelöst] QSortFilterProxyModel: Fehler bei Sortierung, Bug?

Beitrag von gk_17 »

Hallo Leute,

ich habe ein Problem mit der Sortierung von Daten einer SQLite-Tabelle mit Hilfe von QSortFilterProxyModel.

Hier ist ein Minimalbeispiel, dass den Fehler zeigt:

main.cpp

Code: Alles auswählen

#include <QApplication>
#include <QMainWindow>
#include <QTableView>
#include <QSqlQuery>
#include <QSqlQueryModel>
#include <QSortFilterProxyModel>

int main(int argc, char *argv[])
{	    
    //create test data
	QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
	db.setDatabaseName(":memory");
	db.open();
	QSqlQuery query(QSqlDatabase::database());
	//clean up before
	query.exec("DELETE FROM testtable");
	query.exec("DROP TABLE testtable");
	//new table and some manually created rows
	query.exec("CREATE TABLE testtable(col1 INTEGER, col2 INTEGER, col3 INTEGER)");
	query.exec("INSERT INTO testtable VALUES(1234, random(), random())");
	query.exec("INSERT INTO testtable VALUES(4567, random(), random())");
	query.exec("INSERT INTO testtable VALUES(3456, random(), random())");
	query.exec("INSERT INTO testtable VALUES(2345, random(), random())");
	//300 automatically generated data rows
	for(int i = 0; i < 300; i++)
	{
		int c1 = 5000 + i;
		int c2 = rand() % 2000 + 1000;
		int c3 = rand() % 2000 + 1000;
		query.prepare("INSERT INTO testtable (col1, col2, col3) VALUES(:col1, :col2, :col3)");
		query.bindValue(":col1", c1);
		query.bindValue(":col2", c2);
		query.bindValue(":col3", c3);
		query.exec();
	}
	//a last manually data set that should appear in row 2 if sorted
	query.exec("INSERT INTO testtable VALUES(1357, random(), random())");
	
	//create table model
	QSqlQueryModel* sqlQueryModel = new QSqlQueryModel();
	sqlQueryModel->setQuery("SELECT * FROM testtable", QSqlDatabase::database());
	
	//create proxy model
	QSortFilterProxyModel* sortFilterProxyModel = new QSortFilterProxyModel();
	sortFilterProxyModel->setSourceModel(sqlQueryModel);
    sortFilterProxyModel->sort(0);
    
    //create GUI and connect with data
    QApplication app(argc, argv);
    QMainWindow *mainWindow = new QMainWindow();
    QTableView* tableView = new QTableView();
    mainWindow->setCentralWidget(tableView);
    tableView->setModel(sortFilterProxyModel);
    
    mainWindow->showMaximized();
    return app.exec();
}
test.pro

Code: Alles auswählen

SOURCES +=  main.cpp
TEMPLATE = app
LANGUAGE = C++
CONFIG += qt warn_on debug_and_release
TARGET = test
QT += sql
Zuerst wird eine SQLite-Tabelle im Arbeitsspeicher erstellt, die etwas mehr als 300 Zeilen hat und deren letzte Zeile bei Sortierung nach der ersten Spalte in Zeile 2 stehen sollte.
Diese Tabelle wird von einem QSqlQueryModel geladen. Wenn nur dieses in der QTableView angezeigt wird, sieht man die unsortierten Daten.
Ich will aber nach der ersten Spalte sortieren und erzeuge ein QSortFilterProxyModel.

Dieses zeigt die ersten 256 Zeilen auch richtig sortiert an, nur fehlt in Zeile 2 der letzte Datensatz der SQLite-Tabelle. Beim runterscrollen, kurz bevor Zeile 257 sichtbar würde, werden anscheinend Daten nachgeladen, der Scrollbalken wird kleiner und der Rest der Daten ist verfügbar. Wenn ich jetzt wieder hochscrolle, steht dort der verschwundene Datensatz an der richtigen Stelle. Die Daten scheinen also stückweise in 256-Zeilen-Häppchen geladen zu werden und es werden nur die bisher geladenen Daten sortiert.
Ist das ein Fehler? Wenn nicht, wie kann ich erreichen, dass die gesamte Tabelle vollständig geladen und richtig sortiert angezeigt wird?

Bin für jeden Hinweis dankbar!
Grüße,
Gernot
Zuletzt geändert von gk_17 am 12. Juni 2011 10:58, insgesamt 3-mal geändert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Etwas versteckt aber es steht in der Doku:

Code: Alles auswählen

int QSqlQueryModel::rowCount ( const QModelIndex & parent = QModelIndex() ) const [virtual]

Reimplemented from QAbstractItemModel::rowCount().

If the database supports returning the size of a query (see QSqlDriver::hasFeature()), the amount of rows of the current query is returned. Otherwise, returns the amount of rows currently cached on the client.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
gk_17
Beiträge: 37
Registriert: 4. Oktober 2009 19:20

Beitrag von gk_17 »

Vielen Dank!
Es funktioniert. Wenn ich die rowCount()-Methode überschreibe und die echte Tabellengröße zurückliefere (305 im Beispiel), ist das nervige Nachladen weg und die Tabelle ist von Beginn an richtig sortiert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Ja, die Funktion ist auf den ersten Blick schon etwas verwunderlich -aber eigentlich bleibt dem Model ja nicht viel anderes übrig. Musste auch erst ein wenig rumschauen um zu kapieren was los war... :
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
gk_17
Beiträge: 37
Registriert: 4. Oktober 2009 19:20

Beitrag von gk_17 »

zu früh gefreut :(
Es gibt noch mehr Probleme. Ich hab bei obigem Beispiel eine eigene Klasse MyTableModel erstellt, abgeleitet von QSqlQueryModel. In dieser habe ich die rowCount()-Methode so überschrieben, dass sie 305 (die Anzahl der Zeilen im Beispiel) zurückgibt. So weit so gut. Aber wenn ich 805 Zeilen erzeuge und die rowCount() entsprechend anpasse, sieht das Ergebnis ganz schrecklich aus. Die TableView zeigt die Daten zerstückelt an, nach einigen hundert Zeilen gibt es viele Leerzeilen mit der Zeilennummer 0, dann wieder ein paar Datenzeilen, dann wieder leere Zeilen.

Ich habe eine leise Ahnung, dass das mit canFetchMore() und fetchMore() zusammenhängt, und dass diese Methoden alle nicht funktionieren, weil der Qt-SQLite-Treiber nicht die Größe eines Querys ermitteln kann. Ich verstehe nur nicht, warum das so ist? Benutzt denn sonst niemand diese Klassen?

Sollte ich vielleicht lieber versuchen, eine eigene Klasse von QAbstractTableModel abzuleiten, die Daten aus einem großen array darstellt und ich somit auf die SQL-Funktionalität verzichten kann? Oder werde ich dabei ähnliche Probleme zu erwarten haben?

Wird die TableView auch dann die Daten inkrementell laden? Wenn ich das abstellen könnte, wäre mir schon geholfen.

Bin völlig ratlos...
Gernot
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Wenn der Treiber nunmal nichts besseres hergibt kann Qt auch nichts machen. QSqlQueryModel benutzen recht wenige und noch weniger mit sqlite... meistens will man eh was spezielleres so dass man um ein eigenes Model nicht drum rum kommt.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
gk_17
Beiträge: 37
Registriert: 4. Oktober 2009 19:20

Beitrag von gk_17 »

als Nachtrag, falls jemand anderes ein ähnliches Problem hat: ich verwende inzwischen ein eigenes TableModel, das ich von QAbstractTableModel abgeleitet habe. Es reichte aus, die Methoden flags(), setData(), data(), rowCount(), columnCount() und headerData() zu überschreiben, was sehr schnell ging. data() holt jetzt die anzuzeigenden Daten nicht mehr aus einer sqlite-Tabelle, sondern aus meiner eigenen Datenstruktur.
Grüße,
gk_17
Antworten