MVC bei Tabellenprojekt

Alles rund um die Programmierung mit Qt
Antworten
Jannilein
Beiträge: 19
Registriert: 13. Juni 2014 13:38

MVC bei Tabellenprojekt

Beitrag von Jannilein »

Hallo,
ich möchte in mein Programm MVC einbringen, habe das vorher noch nie gemacht.
Momentan habe ich eine Header und eine Main und eine cpp in der der ganze Code steht.
Ich habe schon rumprobiert und mir sämtliches über MVC durchgelesen und an kleinen Beispielprogrammen geübt, da ging alles gut.
Aber bei meinem Programm weiß ich nicht recht wie ich anfangen soll. Ich wollte es mit einem QAbstractTableModel und einer QMap machen.
Wenn man auf meinen Button klickt, gehen imoment einfach die aktuellen Strings aus den QTextEdit feldern in die Tabelle, vorher werden sie überprüft ob es sie schon gibt oder nicht.
Nun soll wenn man auf den Button klickt, von meinem Controller eine add(name,value) an mein Model aufgerufen werden. das Model soll dann die Methoden haben isContained(name,value) ( ob es schon existiert oder nicht) , addToMap(name,value) die es zur Map hinzufügt) und notify() ( die benachrichtigt ) dann die update() Methode von meinem Controller ( oder der View ? ) aufruft. Die/Der sich dann von der QMap getData() die Inhalte der QMap holt und dann mit setItem() setzt.
Soweit ist der Plan, aber ich weiß irgendwie nicht genau wie ich das Implementieren soll. Ich denke ich brauche nur 2 cpp's , einmal für das Model und einmal für den Controller/View kann man das so machen ?
Kann mir jemand einen groben Tipp geben wie ich das implementieren aufbauen soll ?
Ich denke ich muss die MyTableModel::data , columnCount,rowCount Methoden überschreiben.
Bitte sagt mir nun nicht das ich mir das Prinzip nochmal angucken soll, ich lese die ganze Zeit etwas darüber und habe in meinem Buch "Patterns kompakt" von Karl Eilebracht auch schon das Kapitel zu MVC gelesen.
Ich verstehe nur grade nicht wie ich es in meinem Beispiel aufbauen muss.
hier meine cpp in der noch alles zusammen ist. Ich habe schon einiges ausprobiert, aber es klappte nicht. Ich denke es lag einfach daran, das ich es falsch aufgebaut habe, darum sind die Fehler hier wohl ehr irrelevant.

Code: Alles auswählen

#include "Tabelle.h"

Tabelle::Tabelle()
{
	 strname = "strname";
	 strvalue = "strvalue";
	 coucount = 0;
	 cou = 0;
}
  
 void Tabelle::init()
 {
	 // Baut erstmal alles
	 Tabelle tabellen;
	 table = new QTableWidget();
	 lblname = new QLabel ("Name: ", this );
	 lblvalue = new QLabel("Value",this);
	 txtname = new QLineEdit();
	 txtvalue = new QLineEdit();
	 QPushButton* btnaddtotable = new QPushButton( "Add to Table", this);
         QVBoxLayout *layout = new QVBoxLayout (this);
	 //setzt es auf die Spalten und Zeilen die am Anfang drin sein sollen
	 table->setRowCount( 10 );
	 table->setColumnCount( 2 );
	// Labels setzen von der tabelle
	  QStringList hLabels, vLabels;
	 hLabels << "Name" << "Value" ;
	
    table->setHorizontalHeaderLabels( hLabels );
    lblname->setGeometry (40 , 50 , 150 , 60);
	lblname->setFont(QFont("Times", 10, QFont::Bold));
	lblvalue->setGeometry (40 , 50 , 250 , 60);
	lblvalue->setFont(QFont("Times", 10, QFont::Bold));
	btnaddtotable->setGeometry(50,20,100,40);
	 this->setGeometry(800,400,800,650);
	
	 //label und felder zum Layout hinzufügen ( reihnfolge in der sie dann auch zu sehen sind ) 
	layout->addWidget(lblname);
	layout->addWidget(txtname);
	layout->addWidget(lblvalue);
	layout->addWidget(txtvalue);	
	layout->addWidget(btnaddtotable);
	layout->addWidget(table);

	// macht den Indicator sichtbar
	table->horizontalHeader()->setSortIndicatorShown(true);
	// macht sortieren möglich 
	table->setSortingEnabled(true);
	// SIgnal / slot  mit dem Signal wenn der Sortindicator wechselt ( was beim draufklicken geschiet ) 
	// QT:SortOrder macht das es von AAA - ZZZ Sortiert wird.
	connect(table->horizontalHeader(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)),
             this,  SLOT(sortIndicatorChanged(int, Qt::SortOrder)));	
	QObject::connect ( btnaddtotable, SIGNAL ( clicked() ), this , SLOT(pushButtonClicked())) ;

	QPalette Pal(palette());
	Pal.setColor(QPalette::Background, Qt::green);
	this->setAutoFillBackground(true);
	this->setPalette(Pal);	
	table->setColumnWidth( 1, 119);
	table->setColumnWidth( 0, 119);
	this->setMaximumSize( 301,450 );
	this->show();
	txtname->show();
	txtvalue->show();
}
 
void Tabelle::pushButtonClicked()
{	
	table->setSortingEnabled(false);
	int i = -1;
	Tabelle tabellen;
	strname = ( txtname ->text ());
	strvalue = ( txtvalue ->text ());
	QList<QTableWidgetItem *> ItemList = table->findItems(strname, Qt::MatchExactly); 
	QMessageBox msb;
	valueitem = new QTableWidgetItem(0);
	nameitem = new QTableWidgetItem(0);

 if ( ItemList.count() == false )
 {
	valueitem->setText(strvalue);
	
	nameitem->setText(strname);
	coucount = table->columnCount();

	if (  cou >coucount )
	{
		table->insertRow(table->rowCount());
	}
	table->setItem( cou,1, valueitem );
	table->setItem( cou, 0,  nameitem); 
	
	cou++;
 }
 else {
	 msb.setText( " ' "+ strname + " ' "+ " alredy exist");
	   msb.setInformativeText("Do you want to add it ?");
	    msb.setStandardButtons(QMessageBox::Save | QMessageBox::Discard );
		 int ret = msb.exec();
		 switch (ret) {
   case QMessageBox::Save:
      valueitem->setText(strvalue);
	  nameitem->setText(strname);
	  coucount = table->columnCount();

	if (  cou >coucount )
	{
		table->insertRow(table->rowCount());
	}
	table->setItem( cou,1, valueitem );
	table->setItem( cou, 0,  nameitem); 
	nameitem->setTextColor(Qt::red);
	valueitem->setTextColor(Qt::red);
	cou++;
	 
       break;
   case QMessageBox::Discard:
	   txtname->clear();
	   txtvalue->clear();
       break;
   default:
       return;
       break;
 }	
}
 table->setSortingEnabled(true);
}
MichaelS
Beiträge: 240
Registriert: 27. Dezember 2005 12:49

Re: MVC bei Tabellenprojekt

Beitrag von MichaelS »

Warum benutzt Du nicht die Model/View Klassen von Qt (QAbstractTableModel und QTableView) statt den ganzen Kram mit QTableWidget zusammenzufriemeln?

Gruß Michael
Jannilein
Beiträge: 19
Registriert: 13. Juni 2014 13:38

Re: MVC bei Tabellenprojekt

Beitrag von Jannilein »

Naja das QTableWidget brauche ich doch, um die Daten die ich rüber "schicken" will , in einer Tabelle auszugeben, nicht ?
Das QAbstractTableModel brauche ich doch um das Model von Model - View - Controll dar zu stellen wenn ich mich nicht irre ( was gut sein kann)
Somit gehört das QTablewidget zu meinem View und das QAbstractTableModel zu meinem Model , dachte ich.
MichaelS
Beiträge: 240
Registriert: 27. Dezember 2005 12:49

Re: MVC bei Tabellenprojekt

Beitrag von MichaelS »

Jannilein hat geschrieben:Somit gehört das QTablewidget zu meinem View und das QAbstractTableModel zu meinem Model , dachte ich.
QAbstractTableModel ist das Model und QTableView stellt die Daten tabellarisch dar. Das ist der Sinn des Model/View Konzeptes. Du leitest Dein
Model von QAbstractTableModel ab, implementierst data(), headerData(), rowCount() und columnCount(), den Rest macht dann QTableView, nach dem Du diesem mit setModel() einen Pointer auf Dein Model übergeben hast. QTableWidget brauchst Du da nicht.

Gruß MIchael
Jannilein
Beiträge: 19
Registriert: 13. Juni 2014 13:38

Re: MVC bei Tabellenprojekt

Beitrag von Jannilein »

Danke,
also mit map.insert kann ich ja meine Strings in die Map packen, aber ich möchte ja das ALLE Strings in der Tabelle stehen, das heißt wenn 10x ein String hinzugefügt wurde, sollen in der Tabelle auch 10 verschiedene Strings stehen.
Ich hab mir eine MEthode gemacht :

Code: Alles auswählen

void Model::addToMap(QString,QString)
{	
	map->insert(name,value);	
}

die ich aufrufen will, immer mit anderen values versteht sich.
Muss ich nun eine schleife programmieren ? eigendlich nicht oder.
AM ende will ich , wenn sich die Daten in der Map geändert haben, ein update bei meiner View aufrufen. Wie hole ich denn nun alle Daten aus der Map raus ?
Muss ich immer alle wieder neu einfügen oder kann ich auch immer nur das, was neu ist hinzufügen ( bestimmt wesendlich schneller ) ?
MichaelS
Beiträge: 240
Registriert: 27. Dezember 2005 12:49

Re: MVC bei Tabellenprojekt

Beitrag von MichaelS »

Hallo,

hast Du Dir eigentlich einmal die Qt-Doku zu den Model/View-Klassen angeschaut? Die beschreiben das doch recht gut und Beispiele sind in der Regel auch dabei. So schwer ist das doch nicht.
Muss ich nun eine schleife programmieren ? eigendlich nicht oder.
Natürlich nicht. Dafür ist die data()-Methode Deiner Model-Klasse verantwortlich:

Code: Alles auswählen

QVariant myTableModel::data ( const QModelIndex &index, int role ) const
{
    if ( role==Qt::DisplayRole )
    {
        int x = index.row();

        switch ( index.column )
        {
             case 0: /* Erste Spalte (Name)
                /* x.ten Namen aus der Map holen *
                QString name=.... /*Das musst Du machen */
                return name;
              break;

              case 1: /* Zweite Spalte (Value)
                /* x.ten "Wert aus der Map holen *
                QString value=.... /*Das musst Du machen */
                return value;
              break;
        }
    }

    return QAbstractTableModel::data ( index, role );
}
Damit sorgt das Model selbst für die Darstellung aller Einträge, sofern das Model die korrekte Anzahl Zeilen und Spalten liefert:

Code: Alles auswählen

int myTableModel::rowCount(const QModelIndex & parent = QModelIndex()) const
{
    Q_UNUSED( parent );
    return map->size();
}

int myTableModel::columnCount(const QModelIndex & parent = QModelIndex()) const
{
    Q_UNUSED( parent );
    return 2;
}
AM ende will ich , wenn sich die Daten in der Map geändert haben, ein update bei meiner View aufrufen. Wie hole ich denn nun alle Daten aus der Map raus ?
Letzteres macht Deine data()-Methode ( siehe oben ). Damit auch die View mitbekommt, dass die Daten verändert wurden, muss entweder die
insertRow()-Methode der Model-Klasse bemüht werden, oder das Model 'resetted' werden, was dazu führt, dass die View vollständig neu gezeichnet wird:

Code: Alles auswählen

void myTableModel::addToMap(QString,QString)
{
        beginResetModel(); 	
	map->insert(name,value);
        endResetModel();	
}
Gruß Michael
Jannilein
Beiträge: 19
Registriert: 13. Juni 2014 13:38

Re: MVC bei Tabellenprojekt

Beitrag von Jannilein »

Ja, aber wie schon gesagt kann ich das irgendwie nicht auf mein Project "übersetzen"..
Danke.
Ich habe mich nun übrigens doch für 2 QList entschieden , mein Projekt sieht momentan so aus :
model.h

Code: Alles auswählen

class Model : public QAbstractTableModel
{
public:
     Model();
	 int rowCount(const QModelIndex& parent) const;
	 int columnCount(const QModelIndex& parent) const;
	 QVariant data(const QModelIndex &index, int role) const;
	 QVariant headerData(int section, Qt::Orientation orientation, int role) const;
	 void addToList(QString,QString);
	 void getData();
	 QString name;
	 QString value;
	 QList<QString> names;
	 QList<QString> values;
private:
   View *view;

};

#endif 
Model.cpp

Code: Alles auswählen

Model::Model()
{
	
}
void Model::addToList(QString name,QString value )
{
	
	this->names.push_back(name);
	this->values.push_back(value);
}

QVariant Model::data(const QModelIndex &index, int role) const
{
	QString returnValue;
	if(0 == index.column())
	{
		this->names.at(index.row() + 1);
	} else if (0 == index.row())
	{
		this->values.at(index.column() );
	}

	return QVariant();
}
    int Model::rowCount(const QModelIndex & parent = QModelIndex()) const
    {
        Q_UNUSED( parent );
		return names.size();
    }

    int Model::columnCount(const QModelIndex & parent = QModelIndex()) const
    {
        Q_UNUSED( parent );
        return 2;
    }
	QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role == Qt::DisplayRole)
    {
        if (orientation == Qt::Horizontal) {
            switch (section)
            {
            case 0:
                return QString("name");
            case 1:
                return QString("value");
            }
        }
    }
    return QVariant();
}
view.h

Code: Alles auswählen

class View : public QTableView
{
    Q_OBJECT

public:
  View();
  void init();
  void setItem(QString,QString);
  void get();
  QTableView tableview;
  QString strname;
  QString strvalue;
  QTableWidgetItem * valueitem;
 QTableWidgetItem * nameitem;
 public slots:
    void pushButtonClicked();
};
#endif 
view.cpp

Code: Alles auswählen

View::View()
{
	strname = "strname";
	strvalue = "strvalue";
}
void View::init()
{
	 Model model ;
	 QPushButton* btnaddtotable = new QPushButton( "Add to Table",this);
	 btnaddtotable->setGeometry(50,20,100,40);
	 btnaddtotable->show();
	 QObject::connect ( btnaddtotable, SIGNAL ( clicked() ), this , SLOT(pushButtonClicked())) ;
	 tableview.setModel(&model);
	 this->show();
	 
}
void View::pushButtonClicked()
{
	Model model;
	model.addToList(strname,strvalue);
}
void View::setItem(QString,QString)
{
	valueitem = new QTableWidgetItem(0);
	nameitem = new QTableWidgetItem(0);
	/*valueitem->setText(value);
	tableview->setItem( cou,1, valueitem );	*/
}
void View::get()
{
}
Muss morgen mal weiter gucken, wie ich text aus dem Qlist in die Tableview schreibe, setItem besitzt das ja nicht so wie ein QTableWidget .
Liebe Grüße
MichaelS
Beiträge: 240
Registriert: 27. Dezember 2005 12:49

Re: MVC bei Tabellenprojekt

Beitrag von MichaelS »

Hilfe, was machst Du denn mit dem QTableView? Hast Du schon mal die Doku dazu gelesen? QTableView ist ein GUI-Widget und sorgt selbst für die Darstellung, da brauchst Du kein QTableWidget. Du zimmerst in eine Klasse, die bereits eine Tabelle darstellt noch einmal händisch eine Tabelle und füllst die dann manuell mit Daten, was schon das Model macht?

Code: Alles auswählen

Model *meinModel= new Model( this );
QTableView *meineView=new QTableView( this );
meineView->setModel( meinModel );
Das war's. Mehr nicht. Den Rest machen das Model und das QTableView. Und da QTableView eine GUI-Klasse ist, bringst Du diese sinnvollerweise mit dem Qt-Designer auf einem Formular ( QWidget/QDialog) unter.

Außerdem solltest Du Deinem Model statt des Standardkonstruktors einen Konstruktur mit Parent verpassen:

In der h-Datei:

Code: Alles auswählen

 Model ( QObject * parent = 0 );


und in der cpp-Datei:

Code: Alles auswählen

 Model::Model ( QObject * parent ):QAbstractTableModel( parent )
{
  /* Diverse Initialiierungen */
}


Damit ist sichergestellt, dass das Model beim Löschen des Elternobjekts automatisch mitgelöscht wird.

Und noch eins:

Code: Alles auswählen

this->names.push_back(name);
kann auch einfach so geschrieben werden:

Code: Alles auswählen

names.push_back(name);
Gruß Michael
Jannilein
Beiträge: 19
Registriert: 13. Juni 2014 13:38

Re: MVC bei Tabellenprojekt

Beitrag von Jannilein »

Ja hab ich, aber irgendwie habe ich nicht verstanden das es das so "automatisch" macht. Ich habde das problem das ich immer zu kompliziert denke, und darum die lösung vor meinen Augen nicht sehe.
Mein Programm funktioniert nun auch größtenteils, das einzige woran es hängt, ist das es nicht die Daten ändert, wesshalb es auch nich die Data öffnet. Fehler zeigt das PGR keine an.
Die methode wird ja aufgerufen wenn ich auf den Button drücke.

Code: Alles auswählen

void View::pushButtonClicked()
{
	Model model;
	model.addToList(txtname->text(),txtvalue->text());

}
names und values sind meine QListen.

Code: Alles auswählen

void Model::addToList(QString name,QString value )
{
	names.push_back(name);
	values.push_back(value);
	std::cout << names.size() << std::endl;
}
Muss ich da statt push_back etwas anderes nehmen ?
LG & Dankeschön
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: MVC bei Tabellenprojekt

Beitrag von Christian81 »

Hier fehlen eindeutig C++ Grundlagen... Du erzeugst ein neues Model-Objekt auf den Stack, änderst etwas und es wird dann sofort wieder gelöscht. Was sollte da denn anderes als 'nichts' passieren?
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Antworten