Results 1 to 20 of 34

Thread: Map table to tree through model/view possible?

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Nov 2009
    Posts
    129
    Thanks
    4
    Thanked 29 Times in 29 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default 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.

  2. #2
    Join Date
    Dec 2009
    Posts
    24
    Thanks
    3
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: Map table to tree through model/view possible?

    Quote Originally Posted by Coises View Post
    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.

  3. #3
    Join Date
    Oct 2009
    Posts
    151
    Thanks
    6
    Thanked 13 Times in 11 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Map table to tree through model/view possible?

    So isn't the mapping function

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

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Map table to tree through model/view possible?

    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.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  5. #5
    Join Date
    Dec 2009
    Posts
    24
    Thanks
    3
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: Map table to tree through model/view possible?

    Quote Originally Posted by wysota View Post
    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:

    Qt Code:
    1. QList<TreeItem*> parents;
    2. parents << parent;
    3.  
    4. QSqlTableModel table(this, db->returnConnection());
    5. table.setTable("edg_5_lvl_ext");
    6. table.select();
    7.  
    8. for (int row = 0; row < table.rowCount(); row++)
    9. {
    10. for (int column = 0; column < table.columnCount(); column++)
    11. {
    12. QList<QVariant> data;
    13. data << table.data(table.index(row,column, QModelIndex()),Qt::DisplayRole);
    14. parents.last()->appendChild(new TreeItem(data, parents.last()));
    15. parents << parents.last()->child(parents.last()->childCount()-1);
    16. }
    17. parents << parent;
    18. }
    To copy to clipboard, switch view to plain text mode 

    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:

    Qt Code:
    1. QModelIndex mapToSource(const QModelIndex &proxyIndex)
    2. {
    3. if (*(QModelIndex*) proxyIndex.internalPointer() == QModelIndex()) return QModelIndex();
    4. if (((QModelIndex*) proxyIndex.internalPointer())->column() == 0) return QModelIndex();
    5. return sourceModel()->index(proxyIndex.row(), proxyIndex.column());
    6. }
    To copy to clipboard, switch view to plain text mode 

    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.

  6. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Map table to tree through model/view possible?

    Quote Originally Posted by Davor View Post
    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:

    Qt Code:
    1. #include <QtGui>
    2.  
    3. class MyProxy : public QAbstractProxyModel {
    4. public:
    5. MyProxy() : QAbstractProxyModel(){}
    6. QModelIndex mapToSource(const QModelIndex &proxyIndex) const {
    7. if(!proxyIndex.isValid()) return QModelIndex();
    8. int c = columnFromIndex(proxyIndex);
    9. int r = rowFromIndex(proxyIndex);
    10. return sourceModel()->index(r,c);
    11. }
    12.  
    13. QModelIndex mapFromSource(const QModelIndex &sourceIndex) const {
    14. if(!sourceIndex.isValid()) return QModelIndex();
    15. if(sourceIndex.column()==0)
    16. return createIndex(sourceIndex.row(), 0, calculateId(sourceIndex));
    17. return createIndex(0, 0, calculateId(sourceIndex));
    18. }
    19. int columnCount(const QModelIndex &parent = QModelIndex()) const {
    20. return 1;
    21. }
    22. int rowCount(const QModelIndex &parent = QModelIndex()) const {
    23. if(!parent.isValid())
    24. return qMin(0x10000, sourceModel()->rowCount());
    25. int c = mapToSource(parent).column();
    26. if(c==sourceModel()->columnCount()-1)
    27. return 0;
    28. return 1;
    29. }
    30. QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const {
    31. if(parent.isValid()) {
    32. // if parent is valid then in the source model
    33. // we want to receive the same row but the next column, provided that row==0 && col==0
    34. // otherwise the index is not valid
    35. if(row!=0 || column!=0) return QModelIndex();
    36. return createIndex(row, column, (int)parent.internalId()+1);
    37. }
    38. if(column!=0) return QModelIndex();
    39. // parent is not valid thus we can calculate the id the same way as for the source model
    40. return createIndex(row, 0, calculateId(row, 0));
    41. }
    42. QModelIndex parent(const QModelIndex &child) const {
    43. if(!child.isValid())
    44. return QModelIndex();
    45. // parent of an index in the source model is the same row but previous column
    46. int c = mapToSource(child).column();
    47. int r = mapToSource(child).row();
    48. if(c==0){
    49. // if original column == 0 then there is no parent
    50. return QModelIndex();
    51. }
    52. c -= 1;
    53. if(c==0){
    54. return createIndex(r, 0, calculateId(r, c));
    55. }
    56. return createIndex(0, 0, calculateId(r, c));
    57. }
    58. private:
    59. int columnFromIndex(const QModelIndex &proxyIndex) const {
    60. quint32 id = proxyIndex.internalId();
    61. int c = (id & 0xffff);
    62. return c;
    63. }
    64. int rowFromIndex(const QModelIndex &proxyIndex) const {
    65. quint32 id = proxyIndex.internalId();
    66. int r = (id & 0xffff0000) >> 16;
    67. return r;
    68. }
    69. int calculateId(const QModelIndex &sourceIndex) const {
    70. quint32 r = sourceIndex.row();
    71. quint32 c = sourceIndex.column();
    72. return calculateId(r, c);
    73. }
    74. int calculateId(quint32 r, quint32 c) const{
    75. return (((r & 0xffff) << 16) | (c & 0xffff));
    76. }
    77. };
    78.  
    79. int main(int argc, char **argv) {
    80. QApplication app(argc, argv);
    81. QStandardItemModel model(40,20);
    82. for(int i=0; i<model.rowCount(); i++){
    83. for(int j=0; j<model.columnCount(); j++){
    84. model.setData(model.index(i,j), qrand() % 100, Qt::DisplayRole);
    85. }
    86. }
    87. MyProxy proxy;
    88. proxy.setSourceModel(&model);
    89. tv.setModel(&proxy);
    90. tv.show();
    91. QTableView orig;
    92. orig.setModel(&model);
    93. orig.show();
    94. return app.exec();
    95. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by wysota; 3rd December 2009 at 12:28. Reason: Corrected the code
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  7. The following user says thank you to wysota for this useful post:

    Davor (3rd December 2009)

  8. #7
    Join Date
    Dec 2009
    Posts
    24
    Thanks
    3
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: Map table to tree through model/view possible?

    Quote Originally Posted by wysota View Post
    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.
    Last edited by Davor; 3rd December 2009 at 12:21.

  9. #8
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Map table to tree through model/view possible?

    I think I'll post it on the wiki, it's a nice example indeed although a bit hackish.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  10. #9
    Join Date
    Oct 2009
    Posts
    151
    Thanks
    6
    Thanked 13 Times in 11 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Map table to tree through model/view possible?

    Seems overly complicated to me, how about this:
    Qt Code:
    1. #include <QtGui>
    2.  
    3. int main(int argc, char **argv) {
    4. QApplication app(argc, argv);
    5.  
    6. QStandardItemModel tableModel(4, 4);
    7. for (int row = 0; row < 4; ++row) {
    8. for (int column = 0; column < 4; ++column) {
    9. QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
    10. tableModel.setItem(row, column, item);
    11. }
    12. }
    13. QTableView tablev;
    14. tablev.setModel(&tableModel);
    15. tablev.show();
    16.  
    17. QTreeView treev;
    18.  
    19. proxyModel->setSourceModel(&tableModel);
    20. treev.setModel(proxyModel);
    21. treev.show();
    22.  
    23. return app.exec();
    24.  
    25. }
    To copy to clipboard, switch view to plain text mode 


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

  11. #10
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Map table to tree through model/view possible?

    Does it cause a flat model to become a hierarchical one? Or maybe I am missing the point of this example...
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  12. #11
    Join Date
    Oct 2009
    Posts
    151
    Thanks
    6
    Thanked 13 Times in 11 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Map table to tree through model/view possible?

    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!
    Last edited by JD2000; 4th December 2009 at 10:18.

  13. #12
    Join Date
    Dec 2009
    Posts
    24
    Thanks
    3
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: Map table to tree through model/view possible?

    Quote Originally Posted by JD2000 View Post
    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.

  14. #13
    Join Date
    Oct 2009
    Posts
    151
    Thanks
    6
    Thanked 13 Times in 11 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Map table to tree through model/view possible?

    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:


    Qt Code:
    1. #include <QtGui>
    2.  
    3. int main(int argc, char **argv) {
    4. QApplication app(argc, argv);
    5.  
    6. int maxrows = 4;
    7. int maxcols = 4;
    8.  
    9. QStandardItemModel tableModel(maxrows, maxcols);
    10. for (int row = 0; row < maxrows; ++row) {
    11. for (int column = 0; column < maxcols; ++column) {
    12. QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
    13. tableModel.setItem(row, column, item);
    14. }
    15. }
    16.  
    17. QTableView tablev;
    18. tablev.setModel(&tableModel);
    19. tablev.show();
    20.  
    21. QStandardItemModel treeModel;
    22. QStandardItem *parentItem = treeModel.invisibleRootItem();
    23.  
    24. for (int row = 0; row < maxrows; ++row) {
    25. for (int col = 0; col < maxcols; ++col) {
    26. // grab the data out of the table
    27. QStandardItem *item = new QStandardItem(tableModel.data(tableModel.index(row, col, QModelIndex()), Qt::DisplayRole).toString());
    28. parentItem->appendRow(item);
    29. parentItem = item;
    30. }
    31. parentItem = treeModel.invisibleRootItem();
    32. }
    33.  
    34. QTreeView treev;
    35. treev.setModel(&treeModel);
    36. treev.show();
    37.  
    38. return app.exec();
    39.  
    40. }
    To copy to clipboard, switch view to plain text mode 

    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.

Similar Threads

  1. Postgresql QSqlRelationalTableModel empty table
    By RolandHughes in forum Qt Programming
    Replies: 0
    Last Post: 12th November 2008, 17:18
  2. Replies: 3
    Last Post: 5th October 2008, 23:41
  3. Questions regarding setting up a Qt SQL Model/View
    By Methedrine in forum Qt Programming
    Replies: 3
    Last Post: 26th November 2007, 09:26
  4. Replies: 4
    Last Post: 11th September 2006, 14:13
  5. creating table plugin
    By mgurbuz in forum Qt Programming
    Replies: 3
    Last Post: 28th April 2006, 13:50

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.