PDA

View Full Version : data, model and tree view



larry104
24th June 2006, 03:29
Hi,
I have a maybe stupid question but, can anyone help me here:

I have a data class which gets manipulated through a tableWidget. Then I have a model and a treeview based on that data. The data can only be viewed in the tree view. How do I tell the model that the data was changed through the table widget?

The table widget looks like that:
a1 1 2 3
b1 4 5

The tree view displays the full factorial combinations of a1 b1


a1
1
b1
4
5
2
b1
4
5
3
b1
4
5

So, adding a value to b1 would change the tree. I guess (at least I don't see how) I can't have a single model for the two different views since the setData is different for both views.
I appreciate any comments to help me solve my problem.
Thanks.

wysota
24th June 2006, 23:52
Why do you say that you need two different models? If you insist on that (I don't say you shouldn't), use a proxy model (subclass either the abstract proxy model class or its sort/filter subclass) and implement those "versions" there using a common base. Thanks to that all three or two models (core+two proxies or core+one proxy) will always be in sync on their own.

larry104
27th June 2006, 04:05
I was thinking to use two models since the two data representations have not nuch in comon.
The
a1 1 2 3
b1 4 5
is just the summary of the factors used in the full factorial representation so, it would be natural to keep them seperate. If I choose your way of having a single model and two proxies I'm struggling to find a nice interface to serve both views. Also it's not clear to me how to use the proxies since the documentation is not very good. Any suggestion where to finde more detailed info's on proxy and mybe some examples.
Thanks a lot.

wysota
27th June 2006, 08:45
IMO the model is the same in both cases, just the hierarchy differs, that's why I suggest to use a proxy -- to change the hierarchy. You might want to take a look at the source of QSortFilterProxyModel. In general you need to reimplement methods responsible for mapping indexes from the source model to the proxy model and vice versa.

larry104
28th June 2006, 21:08
I mananged to create a proxy model which is currently just doing the same as the model itself.

treeview
|
proxy tableview
| |
model
|
data

Playing with it I noticed that if I change data in the tablview the treeview is not updated. Do I miss something here?
Thanks.

wysota
28th June 2006, 21:39
Can we see some code? Hard to say anything without it.

larry104
28th June 2006, 22:11
Sure, I attached the proxyFilter code. What's also interessting that if I click into the treeview I get a bunch of cout's from the setData instead just a single one.



snip main.cpp

TestModel model;
QSplitter *splitter = new QSplitter;
QTreeView *tree = new QTreeView(splitter);
QTableView *table = new QTableView(splitter);

table->setModel(&model);
table->setRootIndex(QModelIndex());

ProxyFilter filterModel;
filterModel.setSourceModel(&model);
tree->setModel(&filterModel);

splitter->setWindowTitle("Test views");
splitter->show();

return app.exec();
}


proxyFilter.cpp:


#include "proxyFilter.h"
#include <iostream>

ProxyFilter::ProxyFilter() {
}

ProxyFilter::~ProxyFilter() {
}

QModelIndex ProxyFilter::mapFromSource (const QModelIndex & sourceIndex) const {

std::cout << "mapFromSource" << std::endl;

if(sourceIndex.isValid())
return createIndex(sourceIndex.row(), sourceIndex.column());
else
return QModelIndex();
}

QModelIndex ProxyFilter::mapToSource (const QModelIndex & proxyIndex) const {

std::cout << "mapToSource" << std::endl;
return sourceModel()->index(proxyIndex.row(), proxyIndex.column());
}


int ProxyFilter::columnCount(const QModelIndex & parent) const {

// return sourceModel()->columnCount(parent);

// filter out all cols - keep only col = 0
return 1;

}

int ProxyFilter::rowCount(const QModelIndex & parent) const {
return sourceModel()->rowCount(parent);
}


QModelIndex ProxyFilter::index(int row, int column, const QModelIndex& parent) const {

return sourceModel()->index(row, column, parent);

}

QModelIndex ProxyFilter::parent(const QModelIndex&) const {

return QModelIndex();
}

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

std::cout << "data: row =" << index.row() << " col=" << index.column() << std::endl;
return sourceModel()->data(index, role);

}

bool ProxyFilter::setData(const QModelIndex &index, const QVariant &value, int role) {

std::cout << "setData: row =" << index.row() << " col=" << index.column() << std::endl;
return sourceModel()->setData(index, value, role);

}

Qt::ItemFlags ProxyFilter::flags(const QModelIndex &index) const {

return sourceModel()->flags(index);

}

void ProxyFilter::setSourceModel(QAbstractItemModel *sourceModel) {

connect(sourceModel, SIGNAL(modelReset()), this, SIGNAL(modelReset()));
QAbstractProxyModel::setSourceModel(sourceModel);

}


proxyFilter.h:


#ifndef PROXYFILTER_H
#define PROXYFILTER_H

#include <QAbstractProxyModel>
#include <QAbstractItemModel>

class ProxyFilter : public QAbstractProxyModel {

Q_OBJECT

public:

ProxyFilter();
~ProxyFilter();

int columnCount(const QModelIndex & parent = QModelIndex()) const;
int rowCount(const QModelIndex & parent = QModelIndex()) const;

QModelIndex mapFromSource (const QModelIndex & sourceIndex) const;
QModelIndex mapToSource (const QModelIndex & proxyIndex) const;

QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex&) const ;

QVariant data(const QModelIndex & index, int role) const;
bool setData (const QModelIndex &index, const QVariant &value, int role);

Qt::ItemFlags flags(const QModelIndex &index) const;

void setSourceModel(QAbstractItemModel *sourceModel);

};

#endif

larry104
28th June 2006, 22:16
Well, it's actually data cout's I get when just moving the mouse over the field - strange?!?

wysota
28th June 2006, 22:43
No, it's not strange :) Anyway, everytime you use indexes with the source model, you have to map them to the source model space using mapToSource(). And everytime where you get an index from the source model, you should map it to the proxy model space using mapFromSource.

As for multple occurences of data() calls -- don't worry about it, it's perfectly normal. The view is asking the model for things like a tooltip for the item under the pointer. If you extend your cout to also show the roles in question, you'll see that for yourself.

larry104
28th June 2006, 22:54
You mean instead of using (e.g. in data)
return sourceModel()->data(index, role);

I should better use
return sourceModel()->data(mapToSource(index), role);

That make the code cleaner ;)

wysota
28th June 2006, 23:00
You mean instead of using (e.g. in data)
return sourceModel()->data(index, role);

I should better use
return sourceModel()->data(mapToSource(index), role);

Yes. And the same in all other places. Remember that indexes are only valid in context of a current model. You can't "share" or "move" indexes between models. And you can't store them too (you have QPersistentModelIndex for that).

larry104
28th June 2006, 23:08
Ok. I still have the problem that the data changed in the table is not updated in the tree view. If I click into the tree it will get of cause updated since it get's it from the model. Any idea why the views are not in sync? I should mention that if the data is changed in the treeview is updated in the tableview.

larry104
29th June 2006, 01:27
I found a solution to the problem I have. I needed to connect the SIGNAL form the model to a custom slot of my PROXY which then emits a dataChanged. So, the modified setSourceModel looks like:



void ProxyFilter::setSourceModel(QAbstractItemModel *sourceModel) {

connect(sourceModel, SIGNAL(modelReset()), this, SIGNAL(modelReset()));
QAbstractProxyModel::setSourceModel(sourceModel);

if (sourceModel) {
connect(sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(_my_sourceDataChanged(QModelIndex,QModelIndex )));
}

}


and the implementation of the SLOT _my_sourceDataChanged



QModelIndex ProxyFilter::_my_sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right) {

QModelIndex proxy_top_left = mapFromSource(source_top_left);
QModelIndex proxy_bottom_right = mapFromSource(source_bottom_right);
emit dataChanged(proxy_top_left, proxy_bottom_right);
}


Was this really necesarry to do? Shouldn't it be the idea of a proxy to exactly do this automatically? Any comments - did I miss something?
Thanks.

wysota
29th June 2006, 10:34
Yes, it should handle it on its own. It might be a bug. Are you using an up to date release of Qt?

larry104
29th June 2006, 19:12
Yep, I'm using 4.1.3. Has anyone else noticed that problem?

wysota
29th June 2006, 23:07
4.1.4 is the newest one. Maybe the bug has been reported? Have you checked the task-tracker on the Trolltech site?

larry104
30th June 2006, 21:01
I installed 4.1.4 - same problem.

blukske
3rd July 2006, 15:43
I noticed the same problem as well. No signals are propagated when deriving from a QAbstractProxyModel. QSortFilterProxyModel propagates signals just fine.