Implementing QAbstractTableModel
-
wrote on 10 Oct 2011, 05:52 last edited by
Hello everyone!
I'v been trying to implement my own QAbstractTableModel class and display its data with GridView. I'v implemented data, rowCount and columnCount methods. I fill my models data all at once with following method:
@
void myTable::setValues(QByteArray &values){
QList<QByteArray> splits = values.split(' ');
if (splits.count() < 3){
return;
}
int offset = 2;
int rows = splits[0].toInt();
int cols = splits[1].toInt();
QModelIndex idx;
beginInsertColumns(idx, 0, cols-1);
beginInserRows(idx, 0 , rows-1);
for (int i=0; i < rows; i++){
QList<QString> tmp;
for (int j=0; j < cols; j++){
tmp.append(splits[offset + j]);
}
offset += tmp.count();
this->m_data.append(tmp);
}
endInsertColumns();
endInsertRows();
}
@
Where this->m_data is: @QList< QList<QString> >@
My problem is that data method that i implemented from QAbstractTableModel is called just once with index 0,0 and only first value from m_data property is displayed. Can anyone please tell me what am i doing wrong? Maybe I'v got wrong calling of beginInsert... methods, I'v even tried to move them it into for-cycle, but it results in calling data method more times with index 0,0. Thanks a lot for advices! -
wrote on 10 Oct 2011, 06:28 last edited by
Hello,
Could you give us the original code ?
A little bit confused with value<>values , split<>splits .... -
wrote on 10 Oct 2011, 06:33 last edited by
Sorry for confusion, i'v edited original post so it should be fine now. I was rewriting code from VM as ctr+c and ctrl+v didnt work somehow:-)
-
wrote on 10 Oct 2011, 06:55 last edited by
I'm not 100% sure. Is it allowed to cascase insertRows and insertColumns?
If you sett all data for the model in this method, I would use beginResetModel and endResetModel. -
wrote on 10 Oct 2011, 06:59 last edited by
hmm i think that you are right about beginResetModel and endResetModel. I'v updated my code with these, however data method is still called just once with index 0,0...
-
wrote on 10 Oct 2011, 07:07 last edited by
I am not sure either, but don't you have to use to QVariant for your data ?
-
wrote on 10 Oct 2011, 07:12 last edited by
Well i'v been using this "example":http://doc.qt.nokia.com/4.7-snapshot/declarative-modelviews-abstractitemmodel.html, my data method is following:
@
QVariant myTable::data(const QModelIndex &index, int role) const {
cout<<"Data called with col: "<<index.column()<<" row:"<<index.row()<<endl;
if (this->m_data.count() <= 0){
return QVariant();
}
QList<QString> tmpList = this->m_data[index.row()];
QString res = tmpList[index.column()];
return res;
}
@ -
wrote on 10 Oct 2011, 07:17 last edited by
You MUST check the role. typically, you implement it for display role and leave all the rest as is (means call base method).
How are your rowCount and columnCount methods implemented?This all must fit together, so just showing one block of the code is not enough...
-
wrote on 10 Oct 2011, 07:26 last edited by
My rowCount and columnCount methods are following:
@
int myTable::rowCount(const QModelIndex &parent) const{
return this->m_data.count();
}int myTable::columnCount(const QModelIndex &parent) const{
if (this->m_data.count() <= 0){
return 0;
}
return this->m_data[0].count();
}
@I wasn't checking role in data method because i got only one role, this is how constructor looks like:
@
myTable::myTable(QObject *parent) : QAbstractTableModel(parent){
QHash<int, QByteArray> roles;
roles[valueRole] = "value";
setRoleNames(roles);
}
@ -
wrote on 10 Oct 2011, 07:44 last edited by
At second glance I would have called beginInsertXXXX() and endInsertXXXX() the number of XXXX inserted, namely in the two for{...}
-
wrote on 10 Oct 2011, 08:06 last edited by
That ends up with runtime error:
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
The program has unexpectedly finished -
wrote on 10 Oct 2011, 08:13 last edited by
To be honnest i don't like to use QByteArray : this object is designed for network exchange, not for display.
I would first create some QString from your QByteArrays, then implement the display of your table with it.... -
wrote on 10 Oct 2011, 08:22 last edited by
Well, I am not sure if i understand you. I am using QList< QList<QString> > and i am displaying QString. The thing is, that data method gets called just once with index 0,0 and value stored in QList on this index is displayed just fine, but others are not, since data method is not called anymore...
ie:
QList< QList<QString> > my_data is filled with following
1 2
3 4
In main.qml i got following:
@
MyModel{
id: varModel
dataId: "matrix"
}GridView{
id: grid
model: varModel
delegate: {
Text{
text: value
}
}
}
@Method data gets called just once with index 0,0 and value 1 which is on this index is displayed fine, others are not...
-
wrote on 10 Oct 2011, 08:26 last edited by
Hi portoise,
could you post some minimal example so we can just debug the problem?
That would make it easier. Or did you try to debug on your own?
What is really returned in rowCount / columnCount/ flags etc.? -
wrote on 10 Oct 2011, 08:27 last edited by
I really don't like your design at all. Please considder "my docnote":http://developer.qt.nokia.com/doc/qt-4.7/model-view-programming.html#note-13 on designing your own QAbstractItemModels.
I think you first need to define a data store object that handles your blob of data, and supplies a sane API to query it. Only then, you create a QAbstractItemModel (or QAbstractTableModel, in your case) that works on top of that data store. The model is only used as an interface between the Qt item views and your internal data store. It is not suitable as a general data store, I think. The data itself should not be contained within the model.
-
wrote on 10 Oct 2011, 08:27 last edited by
Still in myTable::setValues(QByteArray &values) you use QByte Array.
-
wrote on 10 Oct 2011, 09:09 last edited by
Here is link to my project on dropbox: "Test":http://dl.dropbox.com/u/44811245/Test.tar.gz
It reads data from text file specified in constructor of DeviceControll class. Data in file should be in following format: "dataId : rowCount colCount values", values should be separated by space. ie: "myMatrix: 2 2 1 2 3 4".Andre, thanks for your reply and link to your docnote. It explained a lot to me, now i get how model should be used and next time i will stick to that!;-)
This "project" is just my playground and I'v followed example that I'v linked above.
-
wrote on 10 Oct 2011, 09:25 last edited by
hi portoist,
I found one point:@
QVariant myTable::data(const QModelIndex &index, int role) const
{
if (role != valueRole){
return QVariant();
}
...
}
@this should not be, as you return for each role other than value role.
Try this code for the model data function:@
QVariant myTable::data(const QModelIndex &index, int role) const
{
if(index.isValid())
{
cout<<"Data called with column:"<<index.column()<<" row:"<<index.row()<<endl;
if(Qt::DisplayRole == role)
{
if (this->m_data.count() <= 0){
return QVariant();
}
QList<QString> tmpLists = this->m_data[index.row()];
QString s = tmpLists[index.column()];
cout << "Data will return: "<<s.toLocal8Bit().data()<<endl;
return s;
}
}
return QAbstractTableModel::data(index, role);
}
@ -
wrote on 10 Oct 2011, 09:35 last edited by
Hmm thanks, i'v missed that! But:
@
return QAbstractTableModel::data(index, role);
@
will not work as it is virtual method. -
wrote on 10 Oct 2011, 09:41 last edited by
Indeed, it is pure virtual (normal virtual methods would work, of course). I guess this was just a matter of habbit from Gerolf to call base implementations as the default case; and a good habbit that is too! :-)
1/23