PDA

View Full Version : QMap to keep the model statistcs



gyre
10th December 2007, 01:06
Hi I managed to implement storing of "message" statistics...
I made a QMap <id, count> where I store tha stats for particular id
The only problem is that although they get updated upon new arriving messages they dont change actually displayed value...
I will explain:
I get 10 messages in a row of id=4.
But in view there is only displayed count=1 despite the fact that count contains 10.
The correct value is displayed when a message with the different id arrives...
Does anyone know what is wrong with that ?

My model:


class MsgStatsModel : public QAbstractTableModel
{
Q_OBJECT

typedef QMap<int, int> IdStatsMap;


public:
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;

IdStatsMap idStats;
public slots:
void updateStats(const QVcaCanMsg &canmsg);
public:
MsgStatsModel(QObject *parent = 0);
~MsgStatsModel();
};

MsgStatsModel::MsgStatsModel(QObject *parent)
: QAbstractTableModel(parent)
{
// std::cout <<"Model Created!"<<"\n";
}

MsgStatsModel::~MsgStatsModel()
{

// std::cout <<"Model Destroyed!"<<"\n";

}

int MsgStatsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return idStats.size();

}

int MsgStatsModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 3;

}

QVariant MsgStatsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QVariant ret = QAbstractTableModel::headerData(section, orientation, role);
if(orientation == Qt::Horizontal){
if(role == Qt::DisplayRole){
switch(section){
case 0 :
return tr("ID");
break;
case 1:
return tr("Count");
break;
case 2 :
return tr("Comment");
break;
default :
break;
}
}
}
return ret;
}

QVariant MsgStatsModel::data(const QModelIndex &index, int role) const
{

QVariant ret;

if(!index.isValid()){
std::cout <<"Invalid Index of data provided!"<< std::endl;
return ret;
}
QList<int> keys = idStats.keys();

int row = index.row();
int col = index.column();

if(row >= 0 && row < rowCount()) {
if(role == Qt::DisplayRole) {
int key = keys[row];
int count = idStats.value(key);
switch(col) {
case 0 :
ret = key;
break;
case 1:
ret = count;
break;
}
}
}
return ret;
}

void MsgStatsModel::updateStats(const QVcaCanMsg &canmsg)
{
qDebug()<< "WE GET UPDATE!";
int key = canmsg.id();
int old_row_count = idStats.size();
if(idStats.contains(key)){
int count = idStats.value(key);
qDebug() <<"KEY IS: " << key<<"COUNT IS: " <<count;
idStats[key] = count+1;
} else {
beginInsertRows(QModelIndex(), old_row_count, old_row_count);
idStats.insert(key, 1);
endInsertRows();
}
}

wysota
10th December 2007, 01:09
You don't emit proper signals where appropriate. If you change the value in an existing index, you need to emit dataChanged() signal with proper parameters.

BTW. You have three columns in your model yet you return data only for two of them...

gyre
10th December 2007, 01:19
You don't emit proper signals where appropriate. If you change the value in an existing index, you need to emit dataChanged() signal with proper parameters.

AAh....so when I update a count I have to emit a dataCHanged() signal ? the problem is that it takes indexes as the argument...and those I dont know in that method :(


BTW. You have three columns in your model yet you return data only for two of them...
yes I know...the last one will be editable...I wanted to make work first the statistics :)

wysota
10th December 2007, 01:30
AAh....so when I update a count I have to emit a dataCHanged() signal ? the problem is that it takes indexes as the argument...and those I dont know in that method :(

That's why it's probably better to use a QList instead of QMap and store both the id and the count in the structure kept in the list. You might also use QHash for a fast location of an id within the list. Otherwise you'll need a way to map an id to index and that might be difficult without a container that preserves order of items.

gyre
10th December 2007, 01:33
That's why it's probably better to use a QList instead of QMap and store both the id and the count in the structure kept in the list. You might also use QHash for a fast location of an id within the list. Otherwise you'll need a way to map an id to index and that might be difficult without a container that preserves order of items.
But even if I used QList I wouldnt know the number of the indexes....or would I ?
So you are saying that tehere is now way to make it work this way ?

wysota
10th December 2007, 01:41
But even if I used QList I wouldnt know the number of the indexes....or would I ?
You would.

So you are saying that tehere is now way to make it work this way ?
Yes, of course. For instance like so:


QHash<int, int> m_lookupIndex; // maps id to index in the list
struct Item{
int id;
int count;
Item(){id=-1;count=0;}
};
QList<Item> m_data;

void Model::insert(...){
int id = getId();
Item item;
item.id = id;
item.count = 1;
int index = m_data.count();
m_list.append(item);
m_lookupIndex[id] = index;
//...
}

QModelIndex Model::idToIndex(int id){
if(!m_lookupIndex.contains(id))
return QModelIndex();
int ind = m_lookupIndex[id];
// it'd be 2 times faster using iterators, but it's just an example, so...
return index(ind, 0);
}

gyre
10th December 2007, 01:49
You would.

Yes, of course. For instance like so:


QHash<int, int> m_lookupIndex; // maps id to index in the list
struct Item{
int id;
int count;
Item(){id=-1;count=0;}
};
QList<Item> m_data;

void Model::insert(...){
int id = getId();
Item item;
item.id = id;
item.count = 1;
int index = m_data.count();
m_list.append(item);
m_lookupIndex[id] = index;
//...
}

QModelIndex Model::idToIndex(int id){
if(!m_lookupIndex.contains(id))
return QModelIndex();
int ind = m_lookupIndex[id];
// it'd be 2 times faster using iterators, but it's just an example, so...
return index(ind, 0);
}

fuh you got me confused I must say :)...thanks anyway

wysota
10th December 2007, 09:39
fuh you got me confused I must say :)

Why is that?

gyre
10th December 2007, 15:55
I have implemented as you suggested it to me...
But after I compiled...I tried to run the program...and I got a big memory leake of Qt libraries...dunno why :(...



MsgStatsModel::MsgStatsModel(QObject *parent)
: QAbstractTableModel(parent)
{
// std::cout <<"Model Created!"<<"\n";
}

MsgStatsModel::~MsgStatsModel()
{

// std::cout <<"Model Destroyed!"<<"\n";

}

int MsgStatsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return idStatsList.count();

}

int MsgStatsModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 2;

}

QVariant MsgStatsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QVariant ret = QAbstractTableModel::headerData(section, orientation, role);
if(orientation == Qt::Horizontal){
if(role == Qt::DisplayRole){
switch(section){
case 0 :
return tr("ID");
break;
case 1 :
return tr("Count");
break;
case 2 :
return tr("Comment");
break;
default :
break;
}
}
}
return ret;
}

QVariant MsgStatsModel::data(const QModelIndex &index, int role) const
{

QVariant ret;

if(!index.isValid()){
std::cout <<"Invalid Index of data provided!"<< std::endl;
return ret;
}

int row = index.row();
int col = index.column();

if(row >= 0 && row < rowCount()) {
if(role == Qt::DisplayRole) {
CountStats item = idStatsList[row];
switch(col) {
case 0 :
ret = item.id;
break;
case 1 :
ret = item.count;
break;
case 2 :
ret = item.comments;
break;
default :
break;
}
}
}
return ret;
}

//SLOT catching new arriving messages
void MsgStatsModel::updateStats(const QVcaCanMsg &canmsg)
{
int id = canmsg.id();
int old_row_count = idStatsList.count();

if(!idStatsList.isEmpty()){
if(indexHash.contains(id)){
int row = indexHash.value(id);
int idCount = idStatsList[row].count;
idStatsList[row].count = idCount+1;
qDebug() << "UPDATED COUNT IS: " << idStatsList[row].count;
emit dataChanged(index(row, 0), index(row, 0));
} else {
CountStats item;
item.id=id;
item.count=1;
int row = old_row_count;

beginInsertRows(QModelIndex(), row, row);
idStatsList.append(item);
indexHash.insert(id, row);
endInsertRows();
}
} else {
CountStats item;
item.id=id;
item.count=1;
int row = old_row_count;

beginInsertRows(QModelIndex(), row, row);
idStatsList.append(item);
indexHash.insert(id, row);
endInsertRows();
}
}


header:


typedef struct countstats{
int id;
int count;
QString comments;
} CountStats;

class MsgStatsModel : public QAbstractTableModel
{
Q_OBJECT

typedef QList<CountStats> StatList;
QHash<int, int> indexHash;

public:
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;

StatList idStatsList;
public slots:
void updateStats(const QVcaCanMsg &canmsg);
public:
MsgStatsModel(QObject *parent = 0);
~MsgStatsModel();
};


so no success :(

wysota
10th December 2007, 15:59
Memory leak? How do you know you have a memory leak?

How does the application behave?

gyre
10th December 2007, 16:06
Memory leak? How do you know you have a memory leak?

How does the application behave?

it exits with the following error log...
*** glibc detected *** /home/gyre/temp/06_filter_widget_added/bin/qcanalyzer: realloc(): invalid old size: 0x080dbf38 ***
======= Backtrace: =========
/lib/libc.so.6[0xb734dbdd]
/lib/libc.so.6(realloc+0xfe)[0xb734fb4e]
/usr/lib/libQtCore.so.4(_Z8qReallocPvj+0x24)[0xb7597bf8]
/usr/lib/libQtCore.so.4(_ZN7QString7reallocEi+0x24c)[0xb75df3b4]
/usr/lib/libQtCore.so.4(_ZN7QString6appendE5QChar+0x78)[0xb75e099a]
/usr/lib/libQtCore.so.4[0xb759abba]
/usr/lib/libQtCore.so.4[0xb7613e14]
/usr/lib/libQtCore.so.4[0xb7610a3d]
/usr/lib/libQtCore.so.4[0xb761104c]
/usr/lib/libQtCore.so.4[0xb7611498]
/usr/lib/libQtCore.so.4(_ZNK9QResource12isCompressedEv+0x1f )[0xb7611717]
/usr/lib/libQtCore.so.4[0xb7611929]
/usr/lib/libQtCore.so.4[0xb7611a4a]
/usr/lib/libQtCore.so.4(_ZN19QAbstractFileEngine6createERK7 QString+0x5b)[0xb75ea4eb]
/usr/lib/libQtCore.so.4[0xb75fe9d9]
/usr/lib/libQtCore.so.4(_ZN9QFileInfoC1ERK7QString+0x4a)[0xb75fed9a]
/usr/lib/libQtGui.so.4(_ZN5QIconC1ERK7QString+0x2e)[0xb793ba60]
/home/gyre/temp/06_filter_widget_added/bin/qcanalyzer[0x805ba16]
/home/gyre/temp/06_filter_widget_added/bin/qcanalyzer[0x8057a63]
/home/gyre/temp/06_filter_widget_added/bin/qcanalyzer(_ZNK18QAbstractItemModel5matchERK11QMod elIndexiRK8QVarianti6QFlagsIN2Qt9MatchFlagEE+0x4f3 )[0x80535c7]
/lib/libc.so.6(__libc_start_main+0xe0)[0xb72f8050]
/home/gyre/temp/06_filter_widget_added/bin/qcanalyzer(_ZNK6QFrame8sizeHintEv+0x8d)[0x8053421]
======= Memory map: ========
08048000-080b2000 r-xp 00000000 08:03 1934881 /home/gyre/temp/06_filter_widget_added/bin/qcanalyzer
080b2000-080b3000 rw-p 0006a000 08:03 1934881 /home/gyre/temp/0f54000 rw-p 00001000 08:03 3883263 /lib/libdl-2.6.1.so
b6f54000-b703c000 r-xp 00000000 08:03 5051900 /usr/lib/libX11.so.6.2.0
b703c000-b7040000 rw-p 000e8000 08:03 5051900 /usr/lib/libX11.so.6.2.0
b7040000-b704d000 r-xp 00000000 08:03 5049986 /usr/lib/libXext.so.6.4.0
b704d000-b704e000 rw-p 0000c000 08:03 5049986 /usr/lib/libXext.so.6.4.0
b704e000-b7071000 r-xp 00000000 08:03 5051365 /usr/lib/libfontconfig.so.1.2.0
b7071000-b7079000 rw-p 00023000 08:03 5051365 /usr/lib/libfontconfig.so.1.2.0
b7079000-b707a000 rw-p b7079000 00:00 0
b707a000-b70e5000 r-xp 00000000 08:03 1032250 /usr/lib/libfreetype.so.6.3.16
b70e5000-b70e9000 rw-p 0006a000 08:03 1032250 /usr/lib/libfreetype.so.6.3.16
b70e9000-b70eb000 r-xp 00000000 08:03 1032259 /usr/lib/libXinerama.so.1.0.0
b70eb000-b70ec000 rw-p 00001000 08:03 1032259 /usr/lib/libXinerama.so.1.0.0
b70ec000-b70f4000 r-xp 00000000 08:03 1032243 /usr/lib/libXcursor.so.1.0.2
b70f4000-b70f5000 rw-p 00007000 08:03 1032243 /usr/lib/libXcursor.so.1.0.2
b70f5000-b70f9000 r-xp 00000000 08:03 5051904 /usr/lib/libXfixes.so.3.1.0
b70f9000-b70fa000 rw-p 00003000 08:03 5051904 /usr/lib/libXfixes.so.3.1.0
[stack]

and so on

wysota
10th December 2007, 16:14
It's not a memory leak. You must have made a mistake in your code...

Here is something that works for me:

#include <QApplication>
#include <QAbstractListModel>
#include <QListView>
#include <QList>
#include <QHash>
#include <QTimerEvent>

class Model : public QAbstractListModel {
public:
Model() : QAbstractListModel() {m_tid = -1;}
void add(int id){
if(m_hash.contains(id)){
int row = m_hash[id];
m_data[row].count++;
emit dataChanged(index(row), index(row));
} else {
int row = m_data.count();
beginInsertRows(QModelIndex(), row, row);
S s;
s.id = id;
s.count = 1;
m_data.append(s);
m_hash[id] = row;
endInsertRows();
}
}
int columnCount(const QModelIndex&) const { return 1; }
int rowCount(const QModelIndex &) const { return m_data.count(); }
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const{
if(role!=Qt::DisplayRole || !hasIndex(index.row(), index.column(), index.parent()))
return QVariant();
int row = index.row();
return QString("%1: %2").arg(m_data[row].id).arg(m_data[row].count);
}
QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
void start(){ m_tid = startTimer(100); }
protected:
void timerEvent(QTimerEvent *e){
if(e->timerId()==m_tid){
int id = qrand() % 20;
add(id);
} else
QAbstractListModel::timerEvent(e);
}
private:
struct S {
int id;
int count;
S(){id=-1;count=1;}
};
QList<S> m_data;
QHash<int, int> m_hash;
int m_tid;
};

int main(int argc, char **argv){
QApplication app(argc, argv);
Model m;
QListView lv;
lv.setModel(&m);
m.start();
lv.show();
return app.exec();
}

gyre
10th December 2007, 16:27
I went through my code milion times :(...
Thanks buddy...Ill take a look at yours and try to find a mistake :(...

gyre
10th December 2007, 17:40
so...I remade my codes...now they compile OK...and my app doesnt exit with the code as above...but the statistics dont get actualized ...even when I emit dataChanged() signal when the data get updated...but they still dont :(...
I slowly but surely give up :(...



int MsgStatsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return idStatsList.count();

}

int MsgStatsModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 2;

}

QVariant MsgStatsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QVariant ret = QAbstractTableModel::headerData(section, orientation, role);
if(orientation == Qt::Horizontal){
if(role == Qt::DisplayRole){
switch(section){
case 0 :
return tr("ID");
break;
case 1 :
return tr("Count");
break;
case 2 :
return tr("Comment");
break;
default :
break;
}
}
}
return ret;
}

QVariant MsgStatsModel::data(const QModelIndex &index, int role) const
{

QVariant ret;

if(!index.isValid()){
std::cout <<"Invalid Index of data provided!"<< std::endl;
return ret;
}

int row = index.row();
int col = index.column();

if(row >= 0 && row < rowCount()) {
if(role == Qt::DisplayRole) {
switch(col) {
case 0 :
ret = idStatsList[row].id;
break;
case 1 :
ret = idStatsList[row].count;
break;
case 2 :
//ret = item.comments;
ret=QVariant();
break;
default :
break;
}
}
}
return ret;
}

//SLOT catching new arriving messages
void MsgStatsModel::updateStats(const QVcaCanMsg &canmsg)
{
int id = canmsg.id();

if(indexHash.contains(id)){
int row = indexHash[id];
idStatsList[row].count++;
emit dataChanged(index(row, 0), index(row, 0));
} else {
int row = idStatsList.count();
beginInsertRows(QModelIndex(), row, row);
StatsItem item;
item.id = id;
item.count = 1;
idStatsList.append(item);
indexHash[id] = row;
endInsertRows();
}
}


something is wrong...but cant figure out what...statistics get stored CORRECTLY...count is incremented nicely even for different ids...the problem is display in TableView...:(

wysota
10th December 2007, 18:18
emit dataChanged(index(row, 0), index(row, 0));
If you have three columns, I suggest you emit it like:

emit dataChanged(index(row, 0), index(row, 2));

If you emit changes only in the first column, nothing gets updated, because your count is in the second column, so the correct version is:

emit dataChanged(index(row, 1), index(row, 1));