QSqlTableModel Speicherverbrauch

Alles rund um die Programmierung mit Qt
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Das Problem ist, dass wir noch etwas wenig Information haben... so wissen wir z.B. nicht, was "openDatabase()" genau macht.. oder auch, wie "selectTable(...)" aufgerufen wird.. und ob dazwischen alles sauber läuft..

Lange Rede kurzer Sinn: reduziere dein Beispiel. Erstelle mal in einem separaten Projekt eine einzelne main.cpp, wo du
1. Datenbankverbindung herstelltst
2. QTableView erstellst
3. QSqlTableModel erstellst und mit setModel() der View beibringst...
4. am Ende die view anzeigst und den Eventloop startest

Das sind je nach dem etwa 10 Zeile Code und du kannst relativ schnell ein paar Tests machen (z.B. mehrmals setModel() aufrufen).
Auf die Ergebnisse wären wir auf jeden Fall gespannt ;)
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag von webglider »

Ich habe nun ein neues Test-Projekt erstellt, dabei stellt sich mir die Frage was packe ich eigentlich in die main.cpp und was in die mainwindows.cpp?

Ich habe die notwendigen Schritte jetzt in der mainwindow.cpp realisiert:

Code: Alles auswählen

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindowClass)
{
    ui->setupUi(this);

    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("localhost");
    db.setDatabaseName("meinedb");
    db.setUserName("meinuser");
    db.setPassword("meinpasswort");
    db.open();

    model = new QSqlTableModel(this);

    ui->tableView->setModel(model);

    QString sqlTable = "kunden";

    model->setTable(sqlTable);
    model->select();
}
Wenn ich nun mehrfach hintereinander model->setTable und model->select einfüge klappt es, es werden keine zusätzlichen Leerzeilen im tableView angezeigt. Selbst wenn ich setModel mehrfach anwende kommt es nicht zu dem Leerzeilen-Fehler.

Code: Alles auswählen

    model = new QSqlTableModel(this);

    ui->tableView->setModel(model);
    QString sqlTable = "kunden";
    model->setTable(sqlTable);
    model->select();

    ui->tableView->setModel(model);
    model->setTable(sqlTable);
    model->select();

    ui->tableView->setModel(model);
    model->setTable(sqlTable);
    model->select();
So wie es aussieht muss der Fehler dann im Aufruf oder im Design der Methoden selectTable liegen.

selectTable erwarten zwei Variablen, zum einen den Tabellennamen (QString sqlTable) und zum anderen einein evtl. selectFilter (QString sqlFilter). Also eigentlich nichts ungewöhliches.

Ich werde nun mein Test-Projekt um eine Methode selectTable erweitern, um zu sehen ob der Fehler dann wieder auftritt.

Hast du evtl. schon mal eine Antwort auf die Frage was ich in die main.cpp und was ich in die mainwindow.cpp packe?
Michael
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Eigentlich dachte ich daran, dass _aller_ Code in's main.cpp kommt (man braucht ja kein MainView) :wink: Aber ist ja egal, jetzt sind wir immerhin gleich zwei Schritte weiter.

steht genau dieser Code auch in "openDatabase()"? Ich frage nur, weil der Code in zweierlei Hinsichten gefährlich ist:

1. addDatabase() ersetzt alte Verbindungen... solltest du also diese Methode mehr als einmal aufrufen, muss du vorher prüfen, ob da schon was offen ist... siehe "Warning" unter http://doc.trolltech.com/4.5/qsqldataba ... ddDatabase

2. Qt betreibt bei den Datenbankverbindungen Reference-Counting. D.h. alle Instanzen mit dem gleichen "connectionName" von QSqlDatabase verwenden die gleiche Datenbank-Verbindung und die letzte(!) Instanz schliesst die Verbindung. Deine lokale Variabel in "db" ist die erste _und_ letzte Instanz.... keine Ahnung wie sich das auf die nachfolgenden Models auswirkt... siehe http://doc.trolltech.com/4.5/qsqldataba ... qlDatabase
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag von webglider »

Ich habe nun mein Test-Projekt um eine Methode selectTable erweitert.

Code: Alles auswählen

void MainWindow::selectTable(QString sqlTable)
{
    model = new QSqlTableModel(this);
    ui->tableView->setModel(model);
    model->setTable(sqlTable);
    model->select();
}
Wenn ich diese Methode direkt beim Start des Programms aufrufe wird die Methode auch korrekt ausgeführt und die Sql-Tabelle wird im tableView angezeigt.

So sieht meine mainwindow.cpp jetzt aus:

Code: Alles auswählen

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QtSql>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindowClass)
{
    ui->setupUi(this);

    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("localhost");
    db.setDatabaseName("meineDB");
    db.setUserName("meinUser");
    db.setPassword("meinPasswort");
    db.open();

    selectTable("kunden"); <-- klappt
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::selectTable(QString sqlTable)
{
    model = new QSqlTableModel(this);
    ui->tableView->setModel(model);
    model->setTable(sqlTable);
    model->select();
}

void MainWindow::on_actionKunden_triggered()
{
    selectTable("kunden"); <-- klappt nicht, nur leerzeilen
}
Sobald die Methode selectTable aber über einen Menüpunkt im GUI (im Designer erstellt und mit einer action on_actionKunden_triggered() versehen) ausgelöst wird, erscheint nur ein leeres tableView mit Leerzeilen, die Anzahl der Leerzeilen entspricht jedoch den Zeilen in der Sql-Tabelle.

Das Problem liegt also so wie es aussieht beim Aufruf der Methode selectTable über die GUI-Action.

Was kann denn da falsch laufen?
Zuletzt geändert von webglider am 13. März 2009 16:26, insgesamt 2-mal geändert.
Michael
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag von webglider »

solarix hat geschrieben:steht genau dieser Code auch in "openDatabase()"? Ich frage nur, weil der Code in zweierlei Hinsichten gefährlich ist:

1. addDatabase() ersetzt alte Verbindungen... solltest du also diese Methode mehr als einmal aufrufen, muss du vorher prüfen, ob da schon was offen ist... siehe "Warning" unter http://doc.trolltech.com/4.5/qsqldataba ... ddDatabase
Ja, der Code steht genauso in openDatabase(), die Methode wird allerdings nur einmal aufgerufen.
Michael
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Wieso machst du bei jedem selectTable eine neue Model-Instanz? Hast du vorher Java programmiert? :P
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag von webglider »

Hallo pfid,

nun ja, im Laufe der Problemstellung sind mittlerweile so viele Varianten entstanden... Aber natürlich ist das mehrfache Erzeugen des models überflüssig.

Zusammenfassend hier noch einmal der aktuell Quellcode, mit dieser Variante wird das model nur einmal erzeugt, jedoch besteht das Problem, dass bei jedem Aufruf von selectTable im tableView zwar der Inhalt der jeweiligen Sql-Tabelle angezeigt wird (z.B. 106 Zeilen), zusätzlich aber auch im tableView die gleiche Anzahl an Leerzeilen hinzugefügt wird. So als ob das tableView nicht richtig geleert würde bevor es mit neuen Daten befüllt wird.

Wenn also z.B. die abgefragte Tabelle 106 Zeilen hat werden diese beim ersten Aufruf von selectTable auch brav angezeigt, beim zweiten Aufruf werden diese Zeilen auch wieder angezeigt, zusätzlich werden aber weitere 106 Leerzeilen mit Zeilennummern im tableView unten angehängt.

Code: Alles auswählen

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QtSql>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindowClass)
{
    ui->setupUi(this);

    openDatabase();

    model = new QSqlTableModel(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::openDatabase()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("localhost");
    db.setDatabaseName("meineDB");
    db.setUserName("meinUser");
    db.setPassword("meinPasswort");
    db.open();
}

void MainWindow::selectTable(QString sqlTable)
{
    model->setTable(sqlTable);
    model->select();

    ui->tableView->setModel(model);
}

void MainWindow::on_actionKunden_triggered()
{
    selectTable("kunden");
}

void MainWindow::on_actionServer_triggered()
{
    selectTable("server");
}

void MainWindow::on_actionHistorie_triggered()
{
    selectTable("aufwahlhistorie");
}
Muss ich das tableView noch irgendwie manuell leeren?

P.S.: Ja, ich hab auch schon mal Java programmiert... :wink:
Zuletzt geändert von webglider am 13. März 2009 16:25, insgesamt 1-mal geändert.
Michael
pfid
Beiträge: 535
Registriert: 22. Februar 2008 16:59

Beitrag von pfid »

Ist nur eine Vermutung, ich lehn mich hier etwas aus dem Fenster weil ichs nicht genau weiß, aber:
Note: This function will also create and set a new selection model, replacing any previously set with setSelectionModel(), but the old selection model will not be deleted.
Da würde ich vermuten, dass es nicht unbedingt gewollt ist, dass du für jedes select 1x setModel() aufrufst. Hast du mal das setModel() einfach weggelassen, bzw. nur im Konstruktor gemacht? Also in deiner selectTable() Funktion nur noch setTable + select()?

In meinen Programmen habe ich immer nur 1 TableView + 1 Table der sich nie ändert, daher hab ich deinen Fall nicht. Ich finds aber komisch, bei jedem select der View neu das Model zuzuweisen.
In einem anderen Fall in meinem Programm habe ich eine QTableView und ein QSqlQueryModel, und die View zeigt problemlos unterschiedliche Tabellen an, mit nur einem einmaligen setModel() im Konstruktor.

Ansonsten fällt mir nichts an deinem Code auf.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

ich konnte das nicht glauben, aber ich kann's reproduzieren:

Code: Alles auswählen

  mView = new QTableView();
  mModel= new QSqlTableModel();
  mView->show();
  mView->setModel(mModel);


  mModel->setTable("tabelle1");
  mModel->select();
  mModel->setTable("tabelle2");
  mModel->select();
Das ergibt bei mir (uralt Qt 4.3, Solaris) ein falsches Resultat (leere Zeilen, Zeilenanzahl = Anzahl Zeilen in Tabelle 1)

Das hingegen funktioniert tadellos:

Code: Alles auswählen

void TestView::doSelect()
{
  // altes Model loeschen:
  mView->setModel(NULL);
  delete mModel;

  // neues Aufsetzen.
  mModel = new QSqlTableModel();
  mModel->setTable("tabelle2");
  mModel->select();
  mView->setModel(mModel);
}
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag von webglider »

Hallo solarix, danke fürs überprüfen.

Wenn ich das Model lösche und jedesmal neu erstelle klappts bei mir auch, SQL-Tabellen werden sauber angezeigt und das ganz ohne Leerzeilen:

Code: Alles auswählen

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QtSql>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindowClass)
{
    ui->setupUi(this);

    openDatabase();

    model = new QSqlTableModel(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::openDatabase()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("localhost");
    db.setDatabaseName("meineDB");
    db.setUserName("meinUser");
    db.setPassword("meinPasswort");
    db.open();
}

void MainWindow::selectTable(QString sqlTable)
{
    ui->tableView->setModel(NULL);
    delete model;

    model = new QSqlTableModel(this);

    model->setTable(sqlTable);
    model->select();

    ui->tableView->setModel(model);
}

void MainWindow::on_actionKunden_triggered()
{
    selectTable("kunden");
}

void MainWindow::on_actionServer_triggered()
{
    selectTable("server");
}

void MainWindow::on_actionHistorie_triggered()
{
    selectTable("aufwahlhistorie");
}
Ist das ein Bug in QT oder ein Feature?
Michael
Antworten