Seite 1 von 2

QSqlTableModel Speicherverbrauch

Verfasst: 9. März 2009 18:10
von webglider
Hallo zusammen!

Ich verwende ein QSqlTableModel um Daten aus einer Sql-Tabelle in einem QTableView darzustellen. Ich rufe das ganze über meine Funktion selectTable auf:

Code: Alles auswählen

void MainWindow::selectTable(QString sqlTable, QString sqlFilter)
{
    ui->tabWidget->setTabText(0, sqlTable);

    QSqlTableModel *model = new QSqlTableModel(ui->tableView);
    model->setTable(sqlTable);

    if (sqlFilter != "") {
        model->setFilter(sqlFilter);
    }

    if (sqlTable == "aufwahlhistorie")
    {
        model->setSort(0, Qt::DescendingOrder);
    }
    else
    {
        model->setSort(1, Qt::AscendingOrder);
    }

    model->select();

    ui->tableView->setModel(model);
    ui->tableView->setColumnHidden(0, true);
    ui->tableView->setAlternatingRowColors(true);
    ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);

    ui->statusBar->showMessage(tr("Fertig!"));

    ui->textEdit->insertPlainText("Tabelle " + sqlTable + " ausgelesen.\n");
}
Klappt auch ganz toll, jedoch nimmt der Speicherverbrauch meiner Programm.exe bei jedem neuen Aufruf dieser Funktion zu (am Beispiel meiner größten Tabelle mit ca. 60.000 Datensätzen um 6 MB pro Aufruf)

Mir scheint der Fehler liegt darin, dass ich das QSqlTableModel jedes mal neu erzeuge.

Wie kann ich das besser machen, ohne dass der Speicherverbrauch bei jedem Aufruf der Funktion steigt?

Michael

Verfasst: 9. März 2009 18:17
von CLRS530
Du machst QSqlTableModel *model als private Variable der Klasse und rufst das new einmal bei Erstellung der Klasse auf.
Nicht vergessen es bei Zerstören der Klasse mit delete model; wieder freizugeben.

Also du schreibst unter dem public: Block, der Headerdatei von MainWindow:
private:
QSqlTableModel *model;

womit das include <QSqlTableModel> schon in dem Header gesetzt sein muss. Dann gehst du zu Konstruktor von MainWindow::MainWindow(/*...*/) und fügst dort model = new QSqlTableModel(ui.treeView); an. Im Dekonstraktor delete model;

Verfasst: 9. März 2009 18:21
von RHBaum
richtig
QSqlTableModel *model = new QSqlTableModel(ui->tableView);

du erzeugst jedesmal bei nem Aufruf ein neues Model, welches (siehe parent) erst geloescht wird, wenn ui->tableView geloescht wird, also wahrscheinlich wenn dein MainWindow geschlossen wird.

besser:
Model nur einmal erzeugen und einstellen

und in deiner Methode einfach mit
virtual void QSqlTableModel::setTable ( const QString & tableName )
die tabelle umschalten.
das
bool QSqlTableModel::select ()
hinterher nicht vergessen
Das Model sollte sich dann intern eh um den Rest kuemmern.

Mit setfilter und setsort kannst zur laufzeit auch die filter und sortierung umschalten ....

Ciao ...

Verfasst: 9. März 2009 18:22
von RHBaum
Nicht vergessen es bei Zerstören der Klasse mit delete model; wieder freizugeben.
Brauch er nicht, wenn er als parent fuer das Model einfach das Fenster angibt. QSqlTableModel ist auch ein QObject und macht damit den autodelete mechanismus mit.

Ciao ...

Verfasst: 9. März 2009 18:27
von CLRS530
Achso ok, das war mir auch noch gar nicht bekannt.

Verfasst: 9. März 2009 19:05
von webglider
Super, danke sehr. Klappt einwandfrei.

Verfasst: 9. März 2009 19:21
von webglider
Ein Problem hat sich scheinbar durch die Änderung eingeschlichen. Wenn ich eine Tabell mehrfach abfrage wird die Zeilenanzahl im QTableView um die Anzahl der Zeilen in der Sql-Tabelle erhöht, der Inhalt ist wird allerdings nur einmal angezeigt:

Beispiel:

Ich frage die Tabelle das erste mal ab, der Inhalt (32 Zeilen) wird im QTableView angezeigt.

Wenn ich dann die Tabelle ein weiteres mal abfrage werden im QTableView 64 Zeilen angezeigt, in den ersten 32 Zeilen steht der Inhalt der Sql-Tabelle.

Egal welche Tabelle ich dann auslese, das QTableView wird immer um die Anzahl der Zeilen in der angefragten Sql-Tabelle erweitert.

So sieht mein Programm nach den Änderungen aus:

programm.h

Code: Alles auswählen

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void exitApp();
    void closeEvent(QCloseEvent*);
    void openDatabase();
    void selectTable(QString sqlTable, QString sqlFilter);
    QString getUserLogin();

private:
    Ui::MainWindowClass *ui;
    QSqlTableModel *model;

private slots:
    void on_actionHosts_erstellen_triggered();
    void on_actionBerechtigung_triggered();
    void on_actionAufwahlsperre_triggered();
    void on_actionBenutzer_triggered();
    void on_actionStandorte_triggered();
    void on_actionServer_triggered();
    void on_actionBeenden_triggered();
    void on_actionAufwahlhistorie_triggered();
    void on_actionKunden_triggered();
};
programm.cpp

Code: Alles auswählen

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindowClass)
{
    ui->setupUi(this);
    ui->tabWidget->setTabText(0, "Datenausgabe");
    ui->tabWidget->setTabText(1, "Logbuch");
    this->openDatabase();
    ui->textEdit->insertPlainText("Benutzer " + this->getUserLogin() + " angemeldet.\n");
    model = new QSqlTableModel(this);
}

void MainWindow::selectTable(QString sqlTable, QString sqlFilter)
{
    ui->tabWidget->setTabText(0, sqlTable);

    model->setTable(sqlTable);

    if (sqlFilter != "") {
        model->setFilter(sqlFilter);
    }

    if (sqlTable == "aufwahlhistorie")
    {
        model->setSort(0, Qt::DescendingOrder);
    }
    else
    {
        model->setSort(1, Qt::AscendingOrder);
    }

    model->select();

    ui->tableView->setModel(model);
    ui->tableView->setColumnHidden(0, true);
    ui->tableView->setAlternatingRowColors(true);
    ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);

    ui->statusBar->showMessage(tr("Fertig!"));

    ui->textEdit->insertPlainText("Tabelle " + sqlTable + " ausgelesen.\n");
}
Habt ihr da noch eine Idee was da falsch läuft?

Verfasst: 9. März 2009 19:53
von CLRS530
Ich sehe 3 Fehler. Zuerst einmal schreibe model = new QSqlTableModel(ui->tabWidget); anstelle von this.

Desweiteren verschiebe:

Code: Alles auswählen

ui->tableView->setModel(model); 
ui->tableView->setColumnHidden(0, true); 
ui->tableView->setAlternatingRowColors(true); 
ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
Alle in den Konstruktor, es sei denn, du setzt die 1. Spalte manchmal auf Visible. Aber ich vermute eher nicht.
Ich denke zwar nicht, dass eins von denen der Fehler ist, aber etwas anderes kann ich gerade nicht dazu beitragen.

Der wiederholte setModel aufruf jedenfalls könnte doch schwerwiegender sein.

Verfasst: 9. März 2009 20:14
von webglider
Ich habe deine Änderung eingepflegt, allerdings bleibt das QTableView leer wenn ich das Model bereits im Konstruktor setze.

Code: Alles auswählen

ui->tableView->setModel(model);
Wenn ich das wie vorher in meiner Methode selectTable() mache wird der Inhalt zwar angezeigt, der Fehler mit dem hochzählen der QTableView-Zeilen bleibt aber bestehen.

So sieht mein Programm nach den letzten Änderungen aus:

Code: Alles auswählen

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindowClass)
{
    ui->setupUi(this);
    ui->tabWidget->setTabText(0, "Datenausgabe");
    ui->tabWidget->setTabText(1, "Logbuch");
    this->openDatabase();
    ui->textEdit->insertPlainText("Benutzer " + this->getUserLogin() + " angemeldet.\n");
    model = new QSqlTableModel(ui->tabWidget);
    ui->tableView->setColumnHidden(0, true);
    ui->tableView->setAlternatingRowColors(true);
    ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
}

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

    if (sqlFilter != "") {
        model->setFilter(sqlFilter);
    }

    if (sqlTable == "aufwahlhistorie")
    {
        model->setSort(0, Qt::DescendingOrder);
    }
    else
    {
        model->setSort(1, Qt::AscendingOrder);
    }

    ui->tableView->setModel(model);
    ui->tabWidget->setTabText(0, sqlTable);
    ui->statusBar->showMessage(tr("Fertig!"));
    ui->textEdit->insertPlainText("Tabelle " + sqlTable + " ausgelesen.\n");
}
Habt ihr noch einen Vorschlag?

Verfasst: 10. März 2009 10:17
von RHBaum
allerdings bleibt das QTableView leer wenn ich das Model bereits im Konstruktor setze.
Bleibt das immer leer ? oder nur bis du das erste mal deine selectTable(QString sqlTable, QString sqlFilter) ausfuehrst. Normal sollte das model mit seinem select() sich die daten holen und dem view dann selbststaendig actualisieren ....

Irgendwie iss da noch was faul.

Wenn du das selectTable(QString sqlTable, QString sqlFilter) mehrfach ausfuehrst, erhoeht sich dann die zahl der Zeilen, oder bleibt die gleich ?

eigentlich solltest das Model fuer den view im Konstruktor setzen

und spaetestens nach dem setTable() und dem select() sollte der daten anzeigen !

Noch 2 Anmerkungen:

Deine QT Object hirarchie is komplett unabhaengig von der Anzeigehirarchie oder Funktionalitaet.
Das jemand nen GUI-Element ausm designer als Parent fuer haendisch erzeugte Objecte nimmt, hab ich noch nie gesehen :-) sollte zwar auch funktionieren .... sieht aber komisch irgendwie aus ^^
Du kannst als Parent fuer dein QSqlTableModel also ruhig das mainwindow nehmen ....

Und 2. verleg so viel wie moeglich in die init liste ....

Code: Alles auswählen

MainWindow::MainWindow(QWidget *parent)
:QMainWindow(parent), ui(new Ui::MainWindowClass),
model(new QSqlTableModel(this))
{
    ....
so wuerd ich das Model erzeugen

Ciao ...

Verfasst: 10. März 2009 10:48
von CLRS530
Ich denke mal, du musst die Änderungen am Model dem Table noch klarmachen. Ich mache das bei setRootIndex(QModelIndex());
Also schreib mal ans Ende deiner Funktion
ui->tableView->setRootIndex(QModelIndex());
sowas hilft bei mir immer, auch wenn sicherlich nicht wunderbar sauber 8)

Verfasst: 10. März 2009 18:44
von webglider
Ich habe jetzt die Vorschläge von RHBaum umgesetzt, aber wie du RHBaum schon vermutet - da läuft noch was schief.

Wenn ich das model direkt in der init-Liste (nennt man die so?) erzeuge und das model direkt im Konstruktor dem QTableView zuweise (setModel) wird mir leider nur ein leeres QTableView angezeigt.
RHBaum hat geschrieben:Bleibt das immer leer ? oder nur bis du das erste mal deine selectTable(QString sqlTable, QString sqlFilter) ausfuehrst. Normal sollte das model mit seinem select() sich die daten holen und dem view dann selbststaendig actualisieren ....

Irgendwie iss da noch was faul.
Ja, das QTableView bleibt immer leer, auch nach mehrmaligem selectTable().

So sieht der Code im aktuell aus:

mainwindow.h

Code: Alles auswählen

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtGui/QMainWindow>
#include <QSqlTableModel>

namespace Ui
{
    class MainWindowClass;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindowClass *ui;
    QSqlTableModel *model;
    QString getUserLogin();
    void exitApp();
    void closeEvent(QCloseEvent*);
    void openDatabase();
    void selectTable(QString sqlTable, QString sqlFilter);

private slots:
    void on_actionHosts_erstellen_triggered();
    void on_actionBerechtigung_triggered();
    void on_actionAufwahlsperre_triggered();
    void on_actionBenutzer_triggered();
    void on_actionStandorte_triggered();
    void on_actionServer_triggered();
    void on_actionBeenden_triggered();
    void on_actionAufwahlhistorie_triggered();
    void on_actionKunden_triggered();
};

#endif // MAINWINDOW_H
mainwindow.cpp

Code: Alles auswählen

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

    openDatabase();

    ui->tableView->setModel(model);
    ui->tableView->setColumnHidden(0, true);
    ui->tableView->setAlternatingRowColors(true);
    ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);

    ui->tabWidget->setTabText(0, "Datenausgabe");
    ui->tabWidget->setTabText(1, "Logbuch");

    ui->textEdit->insertPlainText("Benutzer " + this->getUserLogin() + " angemeldet.\n");
}

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

    if (sqlFilter != "")
    {
        model->setFilter(sqlFilter);
    }

    if (sqlTable == "aufwahlhistorie")
    {
        model->setSort(0, Qt::DescendingOrder);
    }
    else
    {
        model->setSort(1, Qt::AscendingOrder);
    }

    model->select();

    ui->tabWidget->setTabText(0, sqlTable);
    ui->statusBar->showMessage(tr("Fertig!"));
    ui->textEdit->insertPlainText("Tabelle " + sqlTable + " ausgelesen.\n");
}
Wäre klasse wenn ihr da noch mal drüber schauen könnt.

Verfasst: 10. März 2009 18:55
von CLRS530
Einmal versucht auf meinen Tip einzugehen?

Verfasst: 10. März 2009 19:00
von webglider
CLRS530 hat geschrieben:Einmal versucht auf meinen Tip einzugehen?
Ja, allerdings ohne Erfolg. Auch damit bleibt das QTableView leer.

Hier nochmal die Zusammenfassung der verschiedenen Ansätze:

1. model in init-Liste erzeugen, model im Konstruktor setzen: QTableView bleibt leer.

2. model im Konstruktor erzeugt, model im Konstruktor gesetzt: Es werden nur Leerzeilen mit Zeilennummern in der Anzahl der Tabellenzeilen im QTableView angezeigt, bei erneutem Aufruf der selectTable werden wiederholt sich das Ganze (Zeilen nur mit Zeilennummern werden hinzugefügt)...

3. model im Konstruktor erzeugt, model in der Methode selectTable dem QTableView zugewiesen: Tabelle wird korrekt angezeigt, es werden jedoch bei erneutem Aufruf der Methode die gleiche Anzahl Leerzeilen hinzugefügt.

Verwirrend...

Verfasst: 12. März 2009 18:09
von webglider
Doch noch vielleicht jemand eine Idee?