PDA

View Full Version : Table model / view editing issue



Caius Aérobus
4th April 2006, 11:42
Hello,
I have subclassed QSqlQueryModel but for the time my current model just creates a simple 3rowsx2columns table, as QStandardItemModel would do. Then I have also subclassed QTableView for 2 purposes:
- make items centered (I rewrite the viewOptions() method but nothing changed: the items are still not centered, as for the standard QTableView class)
- make the items editable: I use a so-called SpinBoxDelegate I found in the itemviews section of the examples, which works with a QStandardItemModel but not with my model, so I suppose that my model is not editable but how can I change it?
Thanks in advance for your help (code is below).


///////////////////////////////////////////////////////////////////////////////
// File : vadTableModel.h //
///////////////////////////////////////////////////////////////////////////////


#ifndef _VAD_TABLE_MODEL_H
#define _VAD_TABLE_MODEL_H

#include <qsqlquerymodel.h>
#include <qvector.h>
#include <visCore.h>

#define R 3
#define C 2

/** vadTableModel est la classe definissant le modele de donnees de type
table utilise dans le cadre de l'applciation de visualisation
d'informations dans le projet de VAD.
*/
class vadTableModel : public QSqlQueryModel, public visCore
{
Q_OBJECT

public:
vadTableModel(char *filename=NULL, QObject *parent=NULL);

int rowCount(const QModelIndex &parent=QModelIndex()) const;
int columnCount(const QModelIndex &parent=QModelIndex()) const;
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
// QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index);
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole);

private:
/// La table des données.
QVector<int> table;
};

#endif

///////////////////////////////////////////////////////////////////////////////
// File : vadTableModel.cxx //
///////////////////////////////////////////////////////////////////////////////


#include "vadTableModel.h"

static int coords[R*C] = { 25, 25, 75, 25, 50, 75 };

vadTableModel::vadTableModel(char *filename, QObject *parent)
: QSqlQueryModel(parent)
{
if (filename == NULL) {
this->table.resize(R*C);
for (int i=0 ; i<R*C ; i++)
this->table[i] = coords[i];
this->setHeaderData(0, Qt::Horizontal, tr("X"));
this->setHeaderData(1, Qt::Horizontal, tr("Y"));
}
}

int
vadTableModel::rowCount(const QModelIndex &parent) const
{
return R;
}

int
vadTableModel::columnCount(const QModelIndex &parent) const
{
return C;
}

QVariant
vadTableModel::data(const QModelIndex &index, int role) const
{
return this->table[index.row()*C+index.column()];
}

bool
vadTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.row() < 0 || index.row() >= this->rowCount() ||
index.column() < 0 || index.column() >= this->columnCount())
return FALSE;
this->table[index.row()*C+index.column()] = value.toInt();
emit dataChanged(index, index);
return TRUE;
}

Qt::ItemFlags
vadTableModel::flags(const QModelIndex &index)
{
return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
}

///////////////////////////////////////////////////////////////////////////////
// File : vadTableView.h //
///////////////////////////////////////////////////////////////////////////////


#ifndef _VAD_TABLE_VIEW_H
#define _VAD_TABLE_VIEW_H

#include <qtableview.h>

class vadTableView : public QTableView
{
Q_OBJECT

public:
vadTableView(QWidget * parent = 0);

protected:
QStyleOptionViewItem viewOptions() const;
};

#endif

///////////////////////////////////////////////////////////////////////////////
// File : vadTableView.cxx //
///////////////////////////////////////////////////////////////////////////////


#include "vadTableView.h"
#include "delegate.h"
//#include "vadTableViewDelegate.h"

vadTableView::vadTableView(QWidget *parent)
: QTableView(parent)
{
//this->setEditTriggers(QAbstractItemView::AllEditTriggers );
SpinBoxDelegate *delegate = new SpinBoxDelegate();
this->setItemDelegate(delegate);
}

QStyleOptionViewItem
vadTableView::viewOptions() const
{
QStyleOptionViewItem option = QTableView::viewOptions();
option.displayAlignment = Qt::AlignHCenter;
return option;
}

jacek
4th April 2006, 14:31
make items centered (I rewrite the viewOptions() method but nothing changed
Make your model's data() method return alignment when invoked with role == Qt::TextAlignmentRole.


make the items editable
QSqlQueryModel is read-only. There is no way to update the database when you have only a single query. You can try QSqlTableModel or QSqlRelationalTableModel model.

jacek
4th April 2006, 14:34
QVariant
vadTableModel::data(const QModelIndex &index, int role) const
{
return this->table[index.row()*C+index.column()];
}
Why do you inherit QSqlQueryModel if you don't use it?

Caius Aérobus
5th April 2006, 10:44
Hello,
thanks for your help, I did not note that QSqlQueryModel was not editable. So if I replace it by QStandardItemModel it works BUT it does not work with QAbstractTableModel. Does it mean that QAbstractTableModel. is also not editable?

Now I have another problem: I have the same model visualized by two views: a table and a graphic. I can edit the values in the table and I would like the graphic be updated accordingly each time I change a value. I expected that the Model/View model does this automatically, that is when a view is connected to a model by the setModel() function, the dataChanged() signal be automatically connected to the appropriate slot of all the views, which does not seem to be how it is handled. Any solution ?

jacek
5th April 2006, 13:29
it does not work with QAbstractTableModel. Does it mean that QAbstractTableModel. is also not editable?
QAbstractTableModel is only an interface to any kind of a tabular model.


I can edit the values in the table and I would like the graphic be updated accordingly each time I change a value.
Provided that you have implemented your model properly, it should happen automatically.


void QAbstractItemModel::dataChanged ( const QModelIndex & topLeft, const QModelIndex & bottomRight ) [signal]
This signal is emitted whenever the data in an existing item changes. The affected items are those between topLeft and bottomRight inclusive (of the same parent).
Note that this signal must be emitted explicitly when reimplementing the setData() function.

Caius Aérobus
5th April 2006, 14:32
This signal is properly emitted in my model:

bool
vadTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
this->table[index.row()*C+index.column()] = value.toInt();
emit dataChanged(index, index);
return TRUE;
}
return FALSE;
}

but the question is: which slot of the QAbstractItemView subclass is this signal connected to?
because I have to overwrite this slot code to handle the modifications properly.

jacek
5th April 2006, 16:39
which slot of the QAbstractItemView subclass is this signal connected to?
This one, I guess:
void QAbstractItemView::dataChanged ( const QModelIndex & topLeft, const QModelIndex & bottomRight ) [virtual protected slot]
This slot is called when items are changed in the model. The changed items are those from topLeft to bottomRight inclusive. If just one item is changed topLeft == bottomRight.

Caius Aérobus
7th April 2006, 09:28
Yes, I have read this in the doc and below is my code BUT:
- if I iconize the window and then deiconize it, it works and the changes in coordinates appear, which means that the paintEvent() is properly handled,
- but if I change the coordinates in the table view, the coordinates are really changed in the model but the graphic is not updated, and this message appears:

QPainter::begin: Widget painting can only begin as a result of a paintEvent
QPainter::setWindow(), painter not active

I really do not understand how it works! please help!

void
vadControlPolygon::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
this->paint();
}

void
vadControlPolygon::paintEvent(QPaintEvent *event)
{
this->paint();
}

void
vadControlPolygon::paint()
{
QPainter painter(this->viewport());
painter.setWindow(0, 0, 100, 100);
QStyleOptionViewItem option;
this->polygon.setPoints(3,
this->model()->data(this->model()->index(0, 0)).toInt(),
this->model()->data(this->model()->index(0, 1)).toInt(),
this->model()->data(this->model()->index(1, 0)).toInt(),
this->model()->data(this->model()->index(1, 1)).toInt(),
this->model()->data(this->model()->index(2, 0)).toInt(),
this->model()->data(this->model()->index(2, 1)).toInt()
);
painter.drawPolygon(this->polygon);
for (int i=0 ; i<3 ; i++)
this->itemDelegate()->paint(&painter, option, this->model()->index(i, 0));
}

Caius Aérobus
7th April 2006, 10:33
My problems have been solved simply by adding the following line:
setDirtyRegion(viewport()->rect());
Regards.

jacek
7th April 2006, 11:03
void
vadControlPolygon::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
this->paint();
}
This should be something like:
void
vadControlPolygon::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
QRect rect( calculateChangeRegion( topLeft, bottmRight ) );
update( rect );
}