QSqlTableModel Speicherverbrauch

Alles rund um die Programmierung mit Qt
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

QSqlTableModel Speicherverbrauch

Beitrag 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
CLRS530
Beiträge: 155
Registriert: 8. Oktober 2007 18:00

Beitrag 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;
Zuletzt geändert von CLRS530 am 9. März 2009 18:25, insgesamt 1-mal geändert.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag 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 ...
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag 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 ...
CLRS530
Beiträge: 155
Registriert: 8. Oktober 2007 18:00

Beitrag von CLRS530 »

Achso ok, das war mir auch noch gar nicht bekannt.
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag von webglider »

Super, danke sehr. Klappt einwandfrei.
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag 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?
Michael
CLRS530
Beiträge: 155
Registriert: 8. Oktober 2007 18:00

Beitrag 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.
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag 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?
Michael
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag 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 ...
CLRS530
Beiträge: 155
Registriert: 8. Oktober 2007 18:00

Beitrag 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)
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag 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.
Michael
CLRS530
Beiträge: 155
Registriert: 8. Oktober 2007 18:00

Beitrag von CLRS530 »

Einmal versucht auf meinen Tip einzugehen?
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag 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...
Michael
webglider
Beiträge: 21
Registriert: 22. Februar 2009 13:32

Beitrag von webglider »

Doch noch vielleicht jemand eine Idee?
Michael
Antworten