Map table to tree through model/view possible?
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
Re: Map table to tree through model/view possible?
You need to have a proxy model and implement mapToSource() and mapFromSource() there (apart from the rest of the required methods, of course).
Re: Map table to tree through model/view possible?
Quote:
Originally Posted by
wysota
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,
Re: Map table to tree through model/view possible?
Have you tried index method?
Re: Map table to tree through model/view possible?
Quote:
Originally Posted by
fatjuicymole
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?
Re: Map table to tree through model/view possible?
Well, index takes row, column and parent, so it should be easy. Thats what you wanted, right?
Re: Map table to tree through model/view possible?
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:
Code:
return sourceModel()->index(proxyIndex.row(), proxyIndex.column()-1, <something has to go here>);
}
return createIndex(sourceIndex.row(), sourceIndex.column()+1, <something has to go here>);
}
Re: Map table to tree through model/view possible?
Quote:
Originally Posted by
fatjuicymole
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.
Code:
#ifndef TABLETOTREEPROXYMODEL_H
#define TABLETOTREEPROXYMODEL_H
//#include <QAbstractProxyModel>
#include <QSortFilterProxyModel>
#include <QModelIndex>
{
public:
TableToTreeProxyModel
(QObject * parent
= 0);
{
if (!parent.isValid()) return createIndex(row, 0);
else return index(parent.row(),parent.column()+1);
return createIndex(row,column);
}
{
return index(child.row(),child.column()-1);
}
};
#endif // TABLETOTREEPROXYMODEL_H
Re: Map table to tree through model/view possible?
Quote:
Originally Posted by
wysota
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:
Code:
return sourceModel()->index(proxyIndex.row(), proxyIndex.column()-1, <something has to go here>);
}
return createIndex(sourceIndex.row(), sourceIndex.column()+1, <something has to go here>);
}
This also has completely no effect:
Code:
#ifndef TABLETOTREEPROXYMODEL_H
#define TABLETOTREEPROXYMODEL_H
//#include <QAbstractProxyModel>
#include <QSortFilterProxyModel>
#include <QModelIndex>
{
public:
{
return sourceModel()->index(proxyIndex.row(), proxyIndex.column()-1, parent(proxyIndex));
}
{
return createIndex(sourceIndex.row(), sourceIndex.column()+1);
}
{
if (!parent.isValid()) return createIndex(row, column);
else return index(parent.row(),parent.column()+1);
return createIndex(row,column);
}
{
return index(child.row(),child.column()-1);
}
};
#endif // TABLETOTREEPROXYMODEL_H
Re: Map table to tree through model/view possible?
This seems logical, but has completely no effect:
Code:
#ifndef TABLETOTREEPROXYMODEL_H
#define TABLETOTREEPROXYMODEL_H
//#include <QAbstractProxyModel>
#include <QSortFilterProxyModel>
#include <QModelIndex>
{
public:
{
if (!parent.isValid()) return sourceModel()->index(row, column);
if (parent.isValid()) return sourceModel()->index(parent.row(),parent.column()+1);
return sourceModel()->index(row,column);
}
{
return sourceModel()->index(child.row(),child.column()-1);
}
};
#endif // TABLETOTREEPROXYMODEL_H
Re: Map table to tree through model/view possible?
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?
Re: Map table to tree through model/view possible?
Quote:
Originally Posted by
wysota
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.
Re: Map table to tree through model/view possible?
Quote:
Originally Posted by
Davor
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.
Re: Map table to tree through model/view possible?
Quote:
Originally Posted by
fatjuicymole
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()?
Re: Map table to tree through model/view possible?
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.
Re: Map table to tree through model/view possible?
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.
Re: Map table to tree through model/view possible?
Quote:
Originally Posted by
Davor
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.
Re: Map table to tree through model/view possible?
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.
Re: Map table to tree through model/view possible?
Quote:
Originally Posted by
JD2000
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.
Re: Map table to tree through model/view possible?
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.