[Anfängerproblem] Konstruktur TableView / TableModel
[Anfängerproblem] Konstruktur TableView / TableModel
Hallo,
ich weiß gar nicht, ob man in so einem Forum eine derartige Frage stellen darf, da diese weniger ein Problem ist, sondern vielmehr eine Verständnisfrage eines Anfängers ist (plädiere für einen Newbie Bereich).
Aber zu meiner Verständnisfrage:
Von einem Beispiel abgeschaut, habe ich eine Klasse von QAbstractTableModel abgeleitet.
Ich weiß, dass Konstrukturen beim ersten Mal automatisch aufgerufen werden. Ferner dachte ich, das Konstruktoren denselben Namen wie die Klasse haben. Mir ist nun an dem Beispiel aufgefallen, dass die beiden Funktionen rowCount als auch columnCount ebenfalls AUTOMATISCH aufgerufen werden.
Frage: woher weiß ich, wann welche Funktion automatisch zu Beginn ausgeführt wird und welche nicht. Ich dachte, dies wäre nur bei Konstruktoren der Fall.
Wahrscheinlich kann man bei abgeleiteten Klassen dies irgendwie aus der Doku erkennen - doch wie?
Hier noch die ersten Zeile der Klasse, wo ich das Verhalten beobachtet haben:
class StringTableModel : public QAbstractTableModel {
Q_OBJECT
public:
StringTableModel(const QStringList &strings, QObject *parent = 0)
: QAbstractTableModel(parent), stringList(strings) {}
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
[...]
Danke vorab für alle hilfreichen Antworten
ich weiß gar nicht, ob man in so einem Forum eine derartige Frage stellen darf, da diese weniger ein Problem ist, sondern vielmehr eine Verständnisfrage eines Anfängers ist (plädiere für einen Newbie Bereich).
Aber zu meiner Verständnisfrage:
Von einem Beispiel abgeschaut, habe ich eine Klasse von QAbstractTableModel abgeleitet.
Ich weiß, dass Konstrukturen beim ersten Mal automatisch aufgerufen werden. Ferner dachte ich, das Konstruktoren denselben Namen wie die Klasse haben. Mir ist nun an dem Beispiel aufgefallen, dass die beiden Funktionen rowCount als auch columnCount ebenfalls AUTOMATISCH aufgerufen werden.
Frage: woher weiß ich, wann welche Funktion automatisch zu Beginn ausgeführt wird und welche nicht. Ich dachte, dies wäre nur bei Konstruktoren der Fall.
Wahrscheinlich kann man bei abgeleiteten Klassen dies irgendwie aus der Doku erkennen - doch wie?
Hier noch die ersten Zeile der Klasse, wo ich das Verhalten beobachtet haben:
class StringTableModel : public QAbstractTableModel {
Q_OBJECT
public:
StringTableModel(const QStringList &strings, QObject *parent = 0)
: QAbstractTableModel(parent), stringList(strings) {}
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
[...]
Danke vorab für alle hilfreichen Antworten
Die Frage ist, was du unter 'beim ersten mal' verstehst.
Wenn du ein neues Objekt mit new erzeugst, wird automatisch der Konstruktor deiner Klasse aufgerufen. Innerhalb dieses Konstruktors können natürlich weiter Klassenfunktionen aufgerufen werden.
rowCount(), columnCount() usw. werden natürlich 'automatisch' aufgerufen wenn du einer View dann dein Model zuweist.
Außerdem stellt sich die Frage, wie du dieses Verhalten beobachtest? Mit einem Debugger oder eingebauten Debugging-Ausgaben?
Wenn du ein neues Objekt mit new erzeugst, wird automatisch der Konstruktor deiner Klasse aufgerufen. Innerhalb dieses Konstruktors können natürlich weiter Klassenfunktionen aufgerufen werden.
rowCount(), columnCount() usw. werden natürlich 'automatisch' aufgerufen wenn du einer View dann dein Model zuweist.
Außerdem stellt sich die Frage, wie du dieses Verhalten beobachtest? Mit einem Debugger oder eingebauten Debugging-Ausgaben?
besten Dank für die erste Antwort:
das erste mal sieht bei mir so aus (steht in der main.cpp):
QAbstractTableModel *model = new StringTableModel(Zellwerte);
QTableView *viewT = new QTableView;
viewT->setModel(model);
viewT->show();
.... um ehrlich zu sein, ich als Anfänger bin jetzt verwundet über "rowCount(), columnCount() usw. werden natürlich 'automatisch' aufgerufen wenn du einer View dann dein Model zuweist. "
So richtig habe ich nicht verstanden warum dem so ist? Werden beim zuweisen eines Views immer alle Funktionen einer Klasse aufgerufen? Sorry, dass ich hier so Fragen muss ... aber ich habe die Doku zum Model/View Programming mehrmals gelesen, aber anscheined muss ich diesbezüglich nochmals "nachsitzen".
das erste mal sieht bei mir so aus (steht in der main.cpp):
QAbstractTableModel *model = new StringTableModel(Zellwerte);
QTableView *viewT = new QTableView;
viewT->setModel(model);
viewT->show();
.... um ehrlich zu sein, ich als Anfänger bin jetzt verwundet über "rowCount(), columnCount() usw. werden natürlich 'automatisch' aufgerufen wenn du einer View dann dein Model zuweist. "
So richtig habe ich nicht verstanden warum dem so ist? Werden beim zuweisen eines Views immer alle Funktionen einer Klasse aufgerufen? Sorry, dass ich hier so Fragen muss ... aber ich habe die Doku zum Model/View Programming mehrmals gelesen, aber anscheined muss ich diesbezüglich nochmals "nachsitzen".
ich hätte erwartet, dass der Aufbau (also auch die Festlegung der Zeilen/Spalten) über den Konstruktor erfolgt. Der ist jedoch beschränkt auf die wenigen Zeilen in der Header Datei (keine weitere Implementierung in der cpp-Datei):
class StringTableModel : public QAbstractTableModel {
Q_OBJECT
public:
StringTableModel(const QStringList &strings, QObject *parent = 0)
: QAbstractTableModel(parent), stringList(strings) {}
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
bool insertColumn(int position, int column, const QModelIndex &index = QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());
bool removeColumn(int position, int column, const QModelIndex &index = QModelIndex());
private:
QStringList stringList;
};
Durch Try-and-Error habe ich auch erkannt, dass die Zeilen in rowCount und columnCount festgelegt werden:
int StringTableModel::rowCount(const QModelIndex &parent) const {
return stringList.count();
}
int StringTableModel::columnCount(const QModelIndex &parent) const {
return 10;
}
Ich hätte halt nicht erwartet das columnCount/rowCount automatisch aufgerufen werden ... und verstehe dass auch immer noch nicht so ganz. Werden nun durch die View-Zuweisung alle Funktionen einer Klasse aufgerufen ... oder nur bestimmte vorab definierte (wie erkenne ich dies dann aus der Doku?).
class StringTableModel : public QAbstractTableModel {
Q_OBJECT
public:
StringTableModel(const QStringList &strings, QObject *parent = 0)
: QAbstractTableModel(parent), stringList(strings) {}
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
bool insertColumn(int position, int column, const QModelIndex &index = QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());
bool removeColumn(int position, int column, const QModelIndex &index = QModelIndex());
private:
QStringList stringList;
};
Durch Try-and-Error habe ich auch erkannt, dass die Zeilen in rowCount und columnCount festgelegt werden:
int StringTableModel::rowCount(const QModelIndex &parent) const {
return stringList.count();
}
int StringTableModel::columnCount(const QModelIndex &parent) const {
return 10;
}
Ich hätte halt nicht erwartet das columnCount/rowCount automatisch aufgerufen werden ... und verstehe dass auch immer noch nicht so ganz. Werden nun durch die View-Zuweisung alle Funktionen einer Klasse aufgerufen ... oder nur bestimmte vorab definierte (wie erkenne ich dies dann aus der Doku?).
also, ich stehe gerade vor diesem "Wald":
ich erkenne nicht, dass der Konstruktor die beiden Funktionen columnCount/rowCount aufruft.
Also schließe ich daraus, dass bei der View-Zuweisung diese beiden Funktionen automatisch aufgerufen werden.
Und nun stellt sich mir die Frage, woher weiß man, welche Funktionen automatisch aufgerufen werden (... was habe ich in der Doku überlesen).
ich erkenne nicht, dass der Konstruktor die beiden Funktionen columnCount/rowCount aufruft.
Also schließe ich daraus, dass bei der View-Zuweisung diese beiden Funktionen automatisch aufgerufen werden.
Und nun stellt sich mir die Frage, woher weiß man, welche Funktionen automatisch aufgerufen werden (... was habe ich in der Doku überlesen).
Hast du dir das Kapitel Model/View-Programming schon mal durchgelesen?
DEIN Konstruktor ist leer, aber nicht der von QAbstractTableModel!!!
Und auch im Konstruktor kann man Methoden aufrufen...
columnCount() und rowCount() sind virtuell. Das heißt es wird auch bei einem Aufruf durch die Basisklasse (fast) immer die Implementierung der abgeleiteten Version aufgerufen.
Warum das fast?
Im Konstruktor wird eben nicht die abgeleitete Version aufgerufen, sondern die der Basisklasse. Grund ist, dass dein Objekt erst den Typ deiner Klasse annimmt, nachdem der Konstruktor vollständig durchgelaufen ist. Bis dahin ist das nur ein Basistyp. Also bitte nimm mal das setModel() raus, damit man sieht ob wirklich im Konstruktor deiner Klasse schon die virtuellen Methoden aufgerufen werden, das wäre nämlich Mist!
// Nachtrag:
Ein kleines Beispiel, übersetzen einfach mit "g++ test.cpp -o test" und dann aufrufen.
Und sag mir, ob du das erwartet hast.
Und auch im Konstruktor kann man Methoden aufrufen...
columnCount() und rowCount() sind virtuell. Das heißt es wird auch bei einem Aufruf durch die Basisklasse (fast) immer die Implementierung der abgeleiteten Version aufgerufen.
Warum das fast?
Im Konstruktor wird eben nicht die abgeleitete Version aufgerufen, sondern die der Basisklasse. Grund ist, dass dein Objekt erst den Typ deiner Klasse annimmt, nachdem der Konstruktor vollständig durchgelaufen ist. Bis dahin ist das nur ein Basistyp. Also bitte nimm mal das setModel() raus, damit man sieht ob wirklich im Konstruktor deiner Klasse schon die virtuellen Methoden aufgerufen werden, das wäre nämlich Mist!
// Nachtrag:
Ein kleines Beispiel, übersetzen einfach mit "g++ test.cpp -o test" und dann aufrufen.
Code: Alles auswählen
#include <iostream>
using namespace std;
class A
{
public:
A();
virtual void test();
};
class B : public A
{
public:
void test();
};
A::A()
{
test();
}
void A::test()
{
cout << "A" << endl;
}
void B::test()
{
cout << "B" << endl;
}
int main()
{
B b;
b.test();
return 0;
}
okay,
habe jetzt an dem Beispiel von franzf ganz gut verstanden, dass bei Aufruf der abgeleiteten Klasse auch der Konstruktor der Basisklasse durchlaufen wird.
Wie ich an meinem Beispiel ja sehe, werden automatisch meine beiden virtuellen Funktionen rowCount() und columCount() der abgeleiteten Klasse ausgeführt.
Da der Konstruktor der abgeleiteten Klasse leer ist, müsste rowCount() und rowColumn() also nach dem oben gesagten durch den Konstruktor der Basisklasse aufgerufen worden sein, da dieser ja auch durchlaufen wird.
nteressant finde ich hier, dass dann die virtuellen Funktionen der abgeleiteten Klasse bereits zu diesem Zeitpunkt die Funktionen der Basisklasse überdecken).
Mir stellt sich immer noch die Frage, woher weiß ich, welche Funktionen durch den Konstruktor einer Qt-Basisklasse aufgeruft wird? Alle seine zugehörigen Funktionen?
Ich hatte gehofft, dass ich es wie folgt herausfinden könnte:
--> Doku: ich habe keine Hinweise gefunden, dass rowCount() und columCount() durch den Konstruktur aufgerufen werden
--> Quellcode von QAbstractTableModel: ich habe mal angefangen mich auf der Festplatte durch die QT-"includes" durchzuhangeln und mir fällt auf, dass ich von einer Inlcude-Datei zur nächsten muss. Irgendwann habe ich damit aufgehört, da ich denke, das dies der Falsche Weg ist
habe jetzt an dem Beispiel von franzf ganz gut verstanden, dass bei Aufruf der abgeleiteten Klasse auch der Konstruktor der Basisklasse durchlaufen wird.
Wie ich an meinem Beispiel ja sehe, werden automatisch meine beiden virtuellen Funktionen rowCount() und columCount() der abgeleiteten Klasse ausgeführt.
Da der Konstruktor der abgeleiteten Klasse leer ist, müsste rowCount() und rowColumn() also nach dem oben gesagten durch den Konstruktor der Basisklasse aufgerufen worden sein, da dieser ja auch durchlaufen wird.
Mir stellt sich immer noch die Frage, woher weiß ich, welche Funktionen durch den Konstruktor einer Qt-Basisklasse aufgeruft wird? Alle seine zugehörigen Funktionen?
Ich hatte gehofft, dass ich es wie folgt herausfinden könnte:
--> Doku: ich habe keine Hinweise gefunden, dass rowCount() und columCount() durch den Konstruktur aufgerufen werden
--> Quellcode von QAbstractTableModel: ich habe mal angefangen mich auf der Festplatte durch die QT-"includes" durchzuhangeln und mir fällt auf, dass ich von einer Inlcude-Datei zur nächsten muss. Irgendwann habe ich damit aufgehört, da ich denke, das dies der Falsche Weg ist
Bitte sag uns mal, wie du denn darauf kommst, dass die virtuellen Methoden IM KONSTRUKTOR aufgerufen werden? Wie sieht dein Quelltext aus?
Denn hier ist mal die definition der beiden beerbten Klassen:
Wie du siehst sind alle entscheidenden Teile LEER! Insbesondere nix mit columnCount() oder rowCount().
Und Implementierungen findet man eigentlich auch nie in den Header-Dateien, sondern eher in den *.cpp's...
Grüße
Franz
// edit
Und warum sollte
1) Beim Konstruieren immer ALLE METHODEN einer Klasse aufgerufen werden? Was hätte das für einen Sinn?
2) Warum sollte man dokumentieren, welche Methoden beim Konstruieren aufgerufen werden? In der Doku sollte vermerkt sein, was passiert (also welche Argumente, evtl. was bei "0" als default verwendet wird), ob es auf übergebene (non-const) Objekte Auswirkungen hat, usw. aber nicht mehr.
Denn hier ist mal die definition der beiden beerbten Klassen:
Code: Alles auswählen
/*!
Constructs an abstract item model with the given \a parent.
*/
QAbstractItemModel::QAbstractItemModel(QObject *parent)
: QObject(*new QAbstractItemModelPrivate, parent)
{
}
/*!
\internal
*/
QAbstractItemModel::QAbstractItemModel(QAbstractItemModelPrivate &dd, QObject *parent)
: QObject(dd, parent)
{
}
////////////////
/*!
Constructs an abstract table model for the given \a parent.
*/
QAbstractTableModel::QAbstractTableModel(QObject *parent)
: QAbstractItemModel(parent)
{
}
/*!
\internal
Constructs an abstract table model with \a dd and the given \a parent.
*/
QAbstractTableModel::QAbstractTableModel(QAbstractItemModelPrivate &dd, QObject *parent)
: QAbstractItemModel(dd, parent)
{
}
Und Implementierungen findet man eigentlich auch nie in den Header-Dateien, sondern eher in den *.cpp's...
Grüße
Franz
// edit
Und warum sollte
1) Beim Konstruieren immer ALLE METHODEN einer Klasse aufgerufen werden? Was hätte das für einen Sinn?
2) Warum sollte man dokumentieren, welche Methoden beim Konstruieren aufgerufen werden? In der Doku sollte vermerkt sein, was passiert (also welche Argumente, evtl. was bei "0" als default verwendet wird), ob es auf übergebene (non-const) Objekte Auswirkungen hat, usw. aber nicht mehr.
@franzf:
zunächst einmal: ich bin um jede Antwort dankbar. Ist ja nicht gerade selbstverständlich
ich habe doch nur ein Problem:
ich verstehe nicht, wodurch rowCount() und columnCount aufgerufen werden. Das ist alles.
Ich weiß, dass Konstruktoren beim ersten Aufruf einer Klasse automatisch aufgerufen werden. Auch weiß ich, dass Konstruktoren denselben Namen wie die Klasse haben.
franzf hat mir gut verdeutlicht, dass ich auch bedenken muss, dass der Konstruktor der Basisklasse aufgerufen wird. Also sah ich hierin die einzige Möglichkeit, dass in dem Konstruktor der Basisklasse die virtuellen Funktionen rowCount() und rowColumn() aufgerufen werden. Was ich total verwirrend gefunden hätte, aber schien mir momentan die einzige Antwort.
Okay, kein Thema, irgendwas grundsätzliches habe ich nicht verstanden
... aber auch schon das eine oder andere gelernt.
Nochmals, ich checke einfach nicht, wie es sein kann dass rowCount() und columnCount aufgerufen werden.
Ich wollte eigentlich niemanden mit dem kompletten Code hier beschäftigen, aber ich poste ihn jetzt doch mal (alles aus der Qt-Doku oder von Qt-Beispielen).
main.cpp
tableModel.h
tableModel.cpp
zunächst einmal: ich bin um jede Antwort dankbar. Ist ja nicht gerade selbstverständlich
ich habe doch nur ein Problem:
ich verstehe nicht, wodurch rowCount() und columnCount aufgerufen werden. Das ist alles.
Ich weiß, dass Konstruktoren beim ersten Aufruf einer Klasse automatisch aufgerufen werden. Auch weiß ich, dass Konstruktoren denselben Namen wie die Klasse haben.
franzf hat mir gut verdeutlicht, dass ich auch bedenken muss, dass der Konstruktor der Basisklasse aufgerufen wird. Also sah ich hierin die einzige Möglichkeit, dass in dem Konstruktor der Basisklasse die virtuellen Funktionen rowCount() und rowColumn() aufgerufen werden. Was ich total verwirrend gefunden hätte, aber schien mir momentan die einzige Antwort.
Okay, kein Thema, irgendwas grundsätzliches habe ich nicht verstanden
Nochmals, ich checke einfach nicht, wie es sein kann dass rowCount() und columnCount aufgerufen werden.
Ich wollte eigentlich niemanden mit dem kompletten Code hier beschäftigen, aber ich poste ihn jetzt doch mal (alles aus der Qt-Doku oder von Qt-Beispielen).
main.cpp
Code: Alles auswählen
#include <QApplication>
#include <QStringList>
#include <QAbstractItemModel>
#include <QAbstractTableModel>
#include <QTableView>
#include "tabelModel.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QList<QString> Zellwerte;
Zellwerte << "One" << "Two" << "Three" << "Four" << "Five" << "Six";
QAbstractTableModel *model = new StringTableModel(Zellwerte);
QTableView *viewT = new QTableView;
viewT->setModel(model);
viewT->show();
return app.exec();
}tableModel.h
Code: Alles auswählen
#ifndef TABELMODEL_H
#define TABELMODEL_H
#include <QAbstractTableModel>
#include <QStringList>
class StringTableModel : public QAbstractTableModel {
Q_OBJECT
public:
StringTableModel(const QStringList &strings, QObject *parent = 0)
: QAbstractTableModel(parent), stringList(strings) {}
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
bool insertColumn(int position, int column, const QModelIndex &index = QModelIndex());
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());
bool removeColumn(int position, int column, const QModelIndex &index = QModelIndex());
private:
QStringList stringList;
};
#endif // TABELMODEL_H
Code: Alles auswählen
#include "tabelModel.h"
int StringTableModel::rowCount(const QModelIndex &parent) const { // Defines the number of rows in the table
return stringList.count();
}
int StringTableModel::columnCount(const QModelIndex &parent) const { //Defines the number of columns in the table
return 10;
}
QVariant StringTableModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
if (index.row() >= stringList.size())
return QVariant();
if (role == Qt::DisplayRole)
return stringList.at(index.row());
else
return QVariant();
}
QVariant StringTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
return QString("Column %1").arg(section);
else
return QString("Row %1").arg(section);
}
Qt::ItemFlags StringTableModel::flags(const QModelIndex &index) const {
if (!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}
bool StringTableModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (index.isValid() && role == Qt::EditRole) {
stringList.replace(index.row(), value.toString());
emit dataChanged(index, index);
return true;
}
return false;
}
bool StringTableModel::insertRows(int position, int rows, const QModelIndex &parent) {
beginInsertRows(QModelIndex(), position, position+rows-1);
for (int row = 0; row < rows; ++row) {
stringList.insert(position, "");
}
endInsertRows();
return true;
}
bool StringTableModel::removeRows(int position, int rows, const QModelIndex &parent) {
beginRemoveRows(QModelIndex(), position, position+rows-1);
for (int row = 0; row < rows; ++row) {
stringList.removeAt(position);
}
endRemoveRows();
return true;
}