PDA

View Full Version : Map table to tree through model/view possible?



Davor
1st December 2009, 16:31
I am trying to convert a table into a tree, hereby making use of the aforementioned model and view. I can create/order the table anyhow I want. Currently, every row in the table represents a path of the tree, which means that for some row an element in the "column-1" is the parent of the element of "column".

I have tried reimplementing the index() and parent() of the QSqlTableModel. I have tried using the proxy model. I have studied numerous examples, read Qt's “view/model programming”, and various other docs. I didn't even get close to a tree. I am constantly getting errors/crashes/etc..

I know I could use the “treeitem” and “treemodel” from Qt's source snippets, but it seems a whole way round something that seems so simple.

So, how do I make an index point to some other index?

Kind regards,
Davor

wysota
1st December 2009, 19:59
You need to have a proxy model and implement mapToSource() and mapFromSource() there (apart from the rest of the required methods, of course).

Davor
2nd December 2009, 07:48
You need to have a proxy model and implement mapToSource() and mapFromSource() there (apart from the rest of the required methods, of course).

I already tried that, but I do not know how to implement mapFromSource() and mapToSource() to make the index point to the index with the same row, but with column-1,

squidge
2nd December 2009, 08:14
Have you tried index method?

Davor
2nd December 2009, 08:37
Have you tried index method?

Yes! I think I was very explicit about that in my first post...

How can I return a createIndex() pointing to some other index?

squidge
2nd December 2009, 09:06
Well, index takes row, column and parent, so it should be easy. Thats what you wanted, right?

wysota
2nd December 2009, 09:13
I don't think this would be enough. You would only gain as much that your data would be shifted one column to the left.

But just for the sake of an example:

QModelIndex Proxy::mapToSource(const QModelIndex &proxyIndex){
if(proxyIndex.column()==0) return QModelIndex();
return sourceModel()->index(proxyIndex.row(), proxyIndex.column()-1, <something has to go here>);
}

QModelIndex Proxy::mapFromSource(const QModelIndex &sourceIndex){
return createIndex(sourceIndex.row(), sourceIndex.column()+1, <something has to go here>);
}

Davor
2nd December 2009, 11:03
Well, index takes row, column and parent, so it should be easy. Thats what you wanted, right?

That is exactly what I thought... until I started coding.

I don't understand the index() method: the arguments over-determine the subject. Whats the meaning of row, column AND parent. Either row/column OR parent is more than enough to devise the index. I don't understand the logic.

The following example does nothing more than QSortFilterProxyModel does with the table. I haven't tried reimplementing AbstractProxyModel yet with this code.



#ifndef TABLETOTREEPROXYMODEL_H
#define TABLETOTREEPROXYMODEL_H

//#include <QAbstractProxyModel>
#include <QSortFilterProxyModel>
#include <QModelIndex>

class TableToTreeProxyModel : public QSortFilterProxyModel
{
public:
TableToTreeProxyModel(QObject * parent = 0);

QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex())
{
if (!parent.isValid()) return createIndex(row, 0);
else return index(parent.row(),parent.column()+1);
return createIndex(row,column);
}
QModelIndex parent(const QModelIndex &child)
{
if (!child.isValid()) return QModelIndex();
if (child.column()==0) return QModelIndex();
return index(child.row(),child.column()-1);
}
};

#endif // TABLETOTREEPROXYMODEL_H

Davor
2nd December 2009, 11:08
I don't think this would be enough. You would only gain as much that your data would be shifted one column to the left.

But just for the sake of an example:

QModelIndex Proxy::mapToSource(const QModelIndex &proxyIndex){
if(proxyIndex.column()==0) return QModelIndex();
return sourceModel()->index(proxyIndex.row(), proxyIndex.column()-1, <something has to go here>);
}

QModelIndex Proxy::mapFromSource(const QModelIndex &sourceIndex){
return createIndex(sourceIndex.row(), sourceIndex.column()+1, <something has to go here>);
}

This also has completely no effect:



#ifndef TABLETOTREEPROXYMODEL_H
#define TABLETOTREEPROXYMODEL_H

//#include <QAbstractProxyModel>
#include <QSortFilterProxyModel>
#include <QModelIndex>

class TableToTreeProxyModel : public QSortFilterProxyModel
{
public:
TableToTreeProxyModel(QObject * parent = 0) : QSortFilterProxyModel(parent){};

QModelIndex mapToSource(const QModelIndex &proxyIndex)
{
if(proxyIndex.column()==0) return QModelIndex();
return sourceModel()->index(proxyIndex.row(), proxyIndex.column()-1, parent(proxyIndex));
}

QModelIndex mapFromSource(const QModelIndex &sourceIndex)
{
return createIndex(sourceIndex.row(), sourceIndex.column()+1);
}

QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex())
{
if (!parent.isValid()) return createIndex(row, column);
else return index(parent.row(),parent.column()+1);
return createIndex(row,column);
}
QModelIndex parent(const QModelIndex &child)
{
if (!child.isValid()) return QModelIndex();
if (child.column()==0) return QModelIndex();
return index(child.row(),child.column()-1);
}
};

#endif // TABLETOTREEPROXYMODEL_H

Davor
2nd December 2009, 12:22
This seems logical, but has completely no effect:



#ifndef TABLETOTREEPROXYMODEL_H
#define TABLETOTREEPROXYMODEL_H

//#include <QAbstractProxyModel>
#include <QSortFilterProxyModel>
#include <QModelIndex>

class TableToTreeProxyModel : public QSortFilterProxyModel
{
public:
TableToTreeProxyModel(QObject * parent = 0) : QSortFilterProxyModel(parent){};

QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex())
{
if (!parent.isValid()) return sourceModel()->index(row, column);
if (parent.isValid()) return sourceModel()->index(parent.row(),parent.column()+1);
return sourceModel()->index(row,column);
}
QModelIndex parent(const QModelIndex &child)
{
if (!child.isValid()) return QModelIndex();
if (child.column()==0) return QModelIndex();
return sourceModel()->index(child.row(),child.column()-1);
}
};

#endif // TABLETOTREEPROXYMODEL_H

wysota
2nd December 2009, 12:55
If you ignore the parent, you will never receive a tree model. You also have to return indexes from the proxy model and not from the source model. The only method that can and has to return indexes from the proxy model is mapToSource().

I still fail to grasp what is the effect you want to obtain. Maybe a very simple practical example would clear things up?

Davor
2nd December 2009, 13:03
If you ignore the parent, you will never receive a tree model. You also have to return indexes from the proxy model and not from the source model. The only method that can and has to return indexes from the proxy model is mapToSource().

I still fail to grasp what is the effect you want to obtain. Maybe a very simple practical example would clear things up?

Take a table with two columns and two rows and following data:
1,2
3,4

I want to be able to click on “1” so it expands to “2”,
I want to be able to click on “3” so it expands to “4”,

In other words as already stated: each record represents the path of a tree.

squidge
2nd December 2009, 15:07
That is exactly what I thought... until I started coding.

I don't understand the index() method: the arguments over-determine the subject. Whats the meaning of row, column AND parent. Either row/column OR parent is more than enough to devise the index. I don't understand the logic.

Imagine a treeview like so:
#root
#-first tree
#---first tree subitem
#-second tree
#---second tree subitem

So 'first tree' and 'second tree' will require a parent of 'root', whilst the subitems will request the first/second tree as parents as appropriate. Once you have that, THEN you can use the row and column. Without the parent, the row and column are meaningless.

Davor
2nd December 2009, 15:30
Imagine a treeview like so:
#root
#-first tree
#---first tree subitem
#-second tree
#---second tree subitem

So 'first tree' and 'second tree' will require a parent of 'root', whilst the subitems will request the first/second tree as parents as appropriate. Once you have that, THEN you can use the row and column. Without the parent, the row and column are meaningless.

Ok, so what should I return then from index() if parent is invalid? QModelIndex()?

Davor
2nd December 2009, 15:55
Here is something interesting: from QAbstractItemModel Class Reference for “parent()”:

[…] A common convention used in models that expose tree data structures is that only items in the first column have children. For that case, when reimplementing this function in a subclass the column of the returned QModelIndex would be 0.[...]

The meaning of “conventions” is left open: which implications has it on the model? Same with “would be”: is it an option or a necessity to make it 0?


In the end, I really feel like wasting time. In the meantime I could have written 50 classic trees, so to speak. The Qt Model/View documentation doesn't say anything about Model-View interaction. It just says what you should implement for certain thing to occur, and let the magic work, and if magic fails, then one's only option is to delve into the sourcecode and try to figure out the internal workings.

Up till now, I tried many things. They outcomes/debuggings all lack logic to me. And in the end, I don't even see why one would need pointers to make trees. It seems an unnecessary complication. Why? Because the underlying Model/View structure is represented in tables in the first place, and because trees are representable as graphs (with constraints), and the two structures (one could probably argue) are completely isomorphic, given the constraints. (There are books written about this, mainly for the practical purpose of translating the (old) hierarchical databases into the relational ones.)

I'll just use the TreeItem and TreeModel for my implementation, of simply the QTreeWidget..

Thanks for your help anyway.

JD2000
2nd December 2009, 16:27
As I understand it, you have

Table(row, column) is the parent of Table(row, column+1)

It would seem to me that all you need to do is iterate through your table, adding items to the tree, in pseudo code this would be:

For row
insert parent

For column
insert child
next column

next row

the number of rows and colums can be obtained from your tableModel:

TableModel->row count();
TableModel->columncount();

the methods for inserting nodes are included in the QTreeWidgetItem class.

squidge
2nd December 2009, 17:10
Ok, so what should I return then from index() if parent is invalid? QModelIndex()?Yes, the same as you would if row or column was invalid.

wysota
2nd December 2009, 18:04
Your mapping is very complex (contrary to how it might look). I can't find a mathematical function to map between the two models. I know how the proxy can be implemented to work but it's not trivial. I'm unable to find a trivial solution to the problem and I've been trying for half an hour now. I'm close to coming to a conclusion that there is no deterministic function to map between the two models in Qt's model-view architecture - you always lack one piece of information - the row, column and parent are not enough to do the mapping.

Davor
2nd December 2009, 19:28
As I understand it, you have

Table(row, column) is the parent of Table(row, column+1)

It would seem to me that all you need to do is iterate through your table, adding items to the tree, in pseudo code this would be:

For row
insert parent

For column
insert child
next column

next row

the number of rows and colums can be obtained from your tableModel:

TableModel->row count();
TableModel->columncount();

the methods for inserting nodes are included in the QTreeWidgetItem class.

Indeed, it’s funny considering the fact that that the model/view was made for easy programming. Or maybe it’s just my bad luck having this problem...

PS: wysota, you have no idea how much I would love to share just a fraction of your insight in this model/view thing! Thanks for trying to solve it.

Coises
3rd December 2009, 07:49
The row and column of a QModelIndex are relative to the item’s parent. To create the tree structure you describe, the row and column mapping from source to target is easy: the column of every item in the tree is 0; the row of every item from column 0 of the source is the same as its row in the source, and the row of every item from any column other than 0 is 0.

So nearly all the row/column information from the table is thrown away when you map to the row/column values for the tree. Obviously, you must have some way to recover this information to make the QModelIndexes into the tree meaningful.

The QAbstractItemModel::createIndex functions do not take the parent as an argument; though there is a QModelIndex::parent() function, this just delegates the operation to QAbstractItemModel::parent. Tree structure information is not stored in a QModelIndex, but must be provided by the model.

What you do have in a QModelIndex is an “opaque” value — either a void* or a quint32 (not both) — in which a model can store information it needs to identify the item. You would need to use this field in some way that will allow you to implement QAbstractItemModel::index, QAbstractItemModel::parent, QSortFilterProxyModel::mapFromSource and QSortFilterProxyModel::mapToSource consistently.

If it happens that the ranges of source row and column numbers are limited such that you can squeeze both values into a total of 32 bits, the job is easy — just save the source row and column in the opaque field of each tree QModelIndex, and you’ll have all the information you need to implement the required functions.

If you cannot pack the source row and column into a 32-bit field, it gets messy; you might use a hash table containing row/column pairs, locating or adding each distinct pair as needed and passing the address of the pair as the third argument to createIndex.

Davor
3rd December 2009, 08:29
The row and column of a QModelIndex are relative to the item’s parent. To create the tree structure you describe, the row and column mapping from source to target is easy: the column of every item in the tree is 0; the row of every item from column 0 of the source is the same as its row in the source, and the row of every item from any column other than 0 is 0.

So nearly all the row/column information from the table is thrown away when you map to the row/column values for the tree. Obviously, you must have some way to recover this information to make the QModelIndexes into the tree meaningful.

The QAbstractItemModel::createIndex functions do not take the parent as an argument; though there is a QModelIndex::parent() function, this just delegates the operation to QAbstractItemModel::parent. Tree structure information is not stored in a QModelIndex, but must be provided by the model.

What you do have in a QModelIndex is an “opaque” value — either a void* or a quint32 (not both) — in which a model can store information it needs to identify the item. You would need to use this field in some way that will allow you to implement QAbstractItemModel::index, QAbstractItemModel::parent, QSortFilterProxyModel::mapFromSource and QSortFilterProxyModel::mapToSource consistently.

If it happens that the ranges of source row and column numbers are limited such that you can squeeze both values into a total of 32 bits, the job is easy — just save the source row and column in the opaque field of each tree QModelIndex, and you’ll have all the information you need to implement the required functions.

If you cannot pack the source row and column into a 32-bit field, it gets messy; you might use a hash table containing row/column pairs, locating or adding each distinct pair as needed and passing the address of the pair as the third argument to createIndex.

I can enumerate uniquely every index/dataitem in the table by the following enumeration method: ((row+column)^2+row-column)/2. I tried to use it as quint32, but the problem was that I didn't understood it's possibilities: quint32 is hardly documented, so I had no idea what to do with it. Can I return it with QModelIndex::internalId ()? Guess not as it is an qint64... The description says: “returns a*qint64*used by the model to associate the index with the internal data structure”... so one has to figure out what this data structure refers to, and what quint32 stands for.

JD2000
3rd December 2009, 09:34
So isn't the mapping function

Table(n,m) -> Tree(n,0) child(m)?

wysota
3rd December 2009, 10:36
The mapping is different on "0" level and different on all other levels. It is also different when mapping from the proxy to the source (and that's the problem). I managed to implement everything but the parent() method. In my opinion there is no way of backtracking indexes into the source model other than providing a structure (or two, to be exact) for mapping between indexes of the two models. That's ok as QSortFilterProxyModel does something similar but it is quite complex to implement as there are synchronization issues to consider.

Davor
3rd December 2009, 11:00
The mapping is different on "0" level and different on all other levels. It is also different when mapping from the proxy to the source (and that's the problem). I managed to implement everything but the parent() method. In my opinion there is no way of backtracking indexes into the source model other than providing a structure (or two, to be exact) for mapping between indexes of the two models. That's ok as QSortFilterProxyModel does something similar but it is quite complex to implement as there are synchronization issues to consider.

I have implemented the topicissue without problems with the TreeItem and TreeModel (from Qt's source snippets) Here is a snippet from the adjusted TreeModel::setupModelData method which works:



QList<TreeItem*> parents;
parents << parent;

QSqlTableModel table(this, db->returnConnection());
table.setTable("edg_5_lvl_ext");
table.select();

for (int row = 0; row < table.rowCount(); row++)
{
for (int column = 0; column < table.columnCount(); column++)
{
QList<QVariant> data;
data << table.data(table.index(row,column, QModelIndex()),Qt::DisplayRole);
parents.last()->appendChild(new TreeItem(data, parents.last()));
parents << parents.last()->child(parents.last()->childCount()-1);
}
parents << parent;
}


In the process I got some more insight into how the TreeModel class is made: primarily with QModelIndex::internalPointer ()

Maybe one could use the same method with a proxy for pointing to indexes from the sourceModel(). Here some brainstorming for the mapToSource method:



QModelIndex mapToSource(const QModelIndex &proxyIndex)
{
if (*(QModelIndex*) proxyIndex.internalPointer() == QModelIndex()) return QModelIndex();
if (((QModelIndex*) proxyIndex.internalPointer())->column() == 0) return QModelIndex();
return sourceModel()->index(proxyIndex.row(), proxyIndex.column());
}


Something else wysota. I don't know what you had in mind when you said that you always miss one value: colum/row or parent. But maybe if you could use the qint32 from an index, you maybe could extract the unknown row from ((row+column)^2+row-column)/2 formula, provided you know the column.

wysota
3rd December 2009, 11:08
I have implemented the topicissue without problems with the TreeItem and TreeModel (from Qt's source snippets) Here is a snippet from the adjusted TreeModel::setupModelData method which works
This is easy because you only have to map one way. It's the other way that causes problems. The other problem - the parent() method is easily avoidable with tree items because you can simply store a pointer to the parent item in the item itself. You don't have such pointers when operating on bare indices.


Something else wysota. I don't know what you had in mind when you said that you always miss one value: colum/row or parent. But maybe if you could use the qint32 from an index, you maybe could extract the unknown row from ((row+column)^2+row-column)/2 formula, provided you know the column.

If the formula is correct then probably so. I might give it a try.

Edit: Nope, it won't work. I don't need the formula - I can store the row number in the index, that's not a problem. But it's still not possible to do the mapping both ways. You need to store both column and row.

I can do that, that's not a problem but the model will only be able to hold 32k rows and 32k columns :) Here is the code:


#include <QtGui>

class MyProxy : public QAbstractProxyModel {
public:
MyProxy() : QAbstractProxyModel(){}
QModelIndex mapToSource(const QModelIndex &proxyIndex) const {
if(!proxyIndex.isValid()) return QModelIndex();
int c = columnFromIndex(proxyIndex);
int r = rowFromIndex(proxyIndex);
return sourceModel()->index(r,c);
}

QModelIndex mapFromSource(const QModelIndex &sourceIndex) const {
if(!sourceIndex.isValid()) return QModelIndex();
if(sourceIndex.column()==0)
return createIndex(sourceIndex.row(), 0, calculateId(sourceIndex));
return createIndex(0, 0, calculateId(sourceIndex));
}
int columnCount(const QModelIndex &parent = QModelIndex()) const {
return 1;
}
int rowCount(const QModelIndex &parent = QModelIndex()) const {
if(!parent.isValid())
return qMin(0x10000, sourceModel()->rowCount());
int c = mapToSource(parent).column();
if(c==sourceModel()->columnCount()-1)
return 0;
return 1;
}
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const {
if(parent.isValid()) {
// if parent is valid then in the source model
// we want to receive the same row but the next column, provided that row==0 && col==0
// otherwise the index is not valid
if(row!=0 || column!=0) return QModelIndex();
return createIndex(row, column, (int)parent.internalId()+1);
}
if(column!=0) return QModelIndex();
// parent is not valid thus we can calculate the id the same way as for the source model
return createIndex(row, 0, calculateId(row, 0));
}
QModelIndex parent(const QModelIndex &child) const {
if(!child.isValid())
return QModelIndex();
// parent of an index in the source model is the same row but previous column
int c = mapToSource(child).column();
int r = mapToSource(child).row();
if(c==0){
// if original column == 0 then there is no parent
return QModelIndex();
}
c -= 1;
if(c==0){
return createIndex(r, 0, calculateId(r, c));
}
return createIndex(0, 0, calculateId(r, c));
}
private:
int columnFromIndex(const QModelIndex &proxyIndex) const {
quint32 id = proxyIndex.internalId();
int c = (id & 0xffff);
return c;
}
int rowFromIndex(const QModelIndex &proxyIndex) const {
quint32 id = proxyIndex.internalId();
int r = (id & 0xffff0000) >> 16;
return r;
}
int calculateId(const QModelIndex &sourceIndex) const {
quint32 r = sourceIndex.row();
quint32 c = sourceIndex.column();
return calculateId(r, c);
}
int calculateId(quint32 r, quint32 c) const{
return (((r & 0xffff) << 16) | (c & 0xffff));
}
};

int main(int argc, char **argv) {
QApplication app(argc, argv);
QTreeView tv;
QStandardItemModel model(40,20);
for(int i=0; i<model.rowCount(); i++){
for(int j=0; j<model.columnCount(); j++){
model.setData(model.index(i,j), qrand() % 100, Qt::DisplayRole);
}
}
MyProxy proxy;
proxy.setSourceModel(&model);
tv.setModel(&proxy);
tv.show();
QTableView orig;
orig.setModel(&model);
orig.show();
return app.exec();
}

Davor
3rd December 2009, 12:13
Edit: Nope, it won't work. I don't need the formula - I can store the row number in the index, that's not a problem. But it's still not possible to do the mapping both ways. You need to store both column and row.

I can do that, that's not a problem but the model will only be able to hold 32k rows and 32k columns :)

You're awesome. You really deserve the “Guru” tag :)

Your enumeration is indeed the best way to solve this. I also have to say that I have learned more from your example that from all the Qt docs I have read up till now.

You post should be made to stick out someway, so the one who searches for the solution, might not get scared from the rest of the posts. I can not edit my initial post, so maybe the admin could refer from it to wysota's solution?

Thanks again wysota.

wysota
3rd December 2009, 12:23
I think I'll post it on the wiki, it's a nice example indeed although a bit hackish.

JD2000
3rd December 2009, 20:30
Seems overly complicated to me, how about this:

#include <QtGui>

int main(int argc, char **argv) {
QApplication app(argc, argv);

QStandardItemModel tableModel(4, 4);
for (int row = 0; row < 4; ++row) {
for (int column = 0; column < 4; ++column) {
QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
tableModel.setItem(row, column, item);
}
}
QTableView tablev;
tablev.setModel(&tableModel);
tablev.show();

QTreeView treev;
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel();

proxyModel->setSourceModel(&tableModel);
treev.setModel(proxyModel);
treev.show();

return app.exec();

}



I've hardcoded the table dimensions at 4x4 for the sake of brevity, but you get the idea.

wysota
3rd December 2009, 21:33
Does it cause a flat model to become a hierarchical one? Or maybe I am missing the point of this example...

JD2000
4th December 2009, 09:55
Ah, good point.

You certainly end up with a table and tree, both referencing the data held in the table and both having 4 rows (in this case) but yes, each table row becomes a tree column 0 element.

My take on this was as stated in the title - map a table to a tree - I don't know whether the parent child relationship between table row elements is actually that important. It makes a great intellectual exercise though, well solved!

Davor
4th December 2009, 14:44
Ah, good point.

You certainly end up with a table and tree, both referencing the data held in the table and both having 4 rows (in this case) but yes, each table row becomes a tree column 0 element.

My take on this was as stated in the title - map a table to a tree - I don't know whether the parent child relationship between table row elements is actually that important. It makes a great intellectual exercise though, well solved!

On the importance: currently, Qt framework doesn't allow any direct mapping between one table and a hierarchical structure. That's unfortunate, at least because both structures are isomorphic. Here a practical example: Suppose you gather data which is hierarchically representable, store it into a relational database (see my other post, here for reference: Kaufmann's Joe Celko's Trees and Hierarchies in SQL for Smarties) and which you want to reproduce/visualize. From such databases you only get tables... no trees.

The whole problem isn't solved yet: you still have redundant trees: take the following table of primary keys forming the edges between two nodes (i.e. two other tables):
11
12
23

Both 1 and 2 from the second row refer tot the same PK from the first column, so they should form one tree with two children.

Tree should look something like this:
1
|||1
|||2
2
|||3

Such tables are simple to generate with relational databases. What is needed is some way to translate them into trees. Wysota has made the first step. I will not bother him for the second, but just note this as an answer to your question.

And as for how to make it.... unfortunately it is not that easy again. The most important is the fact that the parent is uniquely identified by the preceding (primary key) fields (and not by the column and it's PK, as it would seem from the relational model).

Here is an example on how to populate the tree:

for each row
---for each column
------check column = primary
------PK = readPKfrommodel(row,column)
------append PK into PathIDVector (which is the parent's ID)
------If PathIDVector does not exist yet --> make a treeitem(PathIDVector) which is the child of treeitem(PathIDVector_less_one)

Currently I have this with TreeItem/TreeModel, and it's no pretty code. If I find time, I'll try to implement it in wysota's code, and post it here.

JD2000
4th December 2009, 21:39
Tables and trees are not isomorphic, that is the problem.

A tree holds what are essentially tables (their elements are indexed by [row,column]) in some sort of

hierarchy. If you collapse a tree, you end up with a table.

In order to impose such a hierarchy on a table, the table / table elements need to store their

row/column data as usual plus references to their parents. Then when you rebuild the tree check this

reference to find out which node to attach the table element to.

Alternatively for example in cases like yours where there is a defined structure, the parent reference

is not needed because it can be calculated by the model being used:



#include <QtGui>

int main(int argc, char **argv) {
QApplication app(argc, argv);

int maxrows = 4;
int maxcols = 4;

QStandardItemModel tableModel(maxrows, maxcols);
for (int row = 0; row < maxrows; ++row) {
for (int column = 0; column < maxcols; ++column) {
QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
tableModel.setItem(row, column, item);
}
}

QTableView tablev;
tablev.setModel(&tableModel);
tablev.show();

QStandardItemModel treeModel;
QStandardItem *parentItem = treeModel.invisibleRootItem();

for (int row = 0; row < maxrows; ++row) {
for (int col = 0; col < maxcols; ++col) {
// grab the data out of the table
QStandardItem *item = new QStandardItem(tableModel.data(tableModel.index(row , col, QModelIndex()), Qt::DisplayRole).toString());
parentItem->appendRow(item);
parentItem = item;
}
parentItem = treeModel.invisibleRootItem();
}

QTreeView treev;
treev.setModel(&treeModel);
treev.show();

return app.exec();

}

The problem here is that after creation, the tree and table are independant of each other.

I suppose that they could be kept in sync by implementing their respective data changed signals/slots.

wysota
4th December 2009, 23:24
I suppose that they could be kept in sync by implementing their respective data changed signals/slots.

Oh, I would really like to see you do that... Especially the part where items are added or removed.

JD2000
7th December 2009, 10:11
I had not given it much thought to be honest, presumably in that case you would use the rows/columns inserted or removed signals. I was actually trying to respond to the
On the importance: currently, Qt framework doesn't allow any direct mapping between one table and a hierarchical structure. That's unfortunate, at least because both structures are isomorphic. Here a practical example .........comment. I think it is a bit unfair to expect QT to provide a mapping between a 2D table and a 3D tree framework, infact it is impossible without some extra information relating to the structure of the tree.

I note that you are no longer a guru, congratulations on your enlightenment Wysota!