I'm trying to use a proxy model to take a 'parentId' column in a QSqlTableModel (using SQLITE) and view the hierarchy with a QTreeView. To make this work without needed a treeItem-type class I'm using internalId() from QModelIndex to store the 'parentId'. It works 99% of the time and I can't nail down where I'm messing up. I can view the parents/children and even expand/collapse them but I cannot select the children of a parent item. The selectionModel's selectionChanged() signal and the proxy models index() return the expected QModelIndex and so I don't know what is making the view upset. Here is the code that produces the issue. Thanks for any insight or suggestions

mainwindow.h
Qt Code:
  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3.  
  4. #include <QMainWindow>
  5. #include <QSqlError>
  6.  
  7. class QTreeView;
  8. class sqlTreeModelTest;
  9.  
  10. class MainWindow : public QMainWindow
  11. {
  12. Q_OBJECT
  13.  
  14. public:
  15. explicit MainWindow(QWidget *parent = 0);
  16. QSqlError initdb();
  17. private:
  18. QTreeView *treeView;
  19. void setupModel();
  20. };
  21.  
  22. #endif // MAINWINDOW_H
To copy to clipboard, switch view to plain text mode 

mainwindow.cpp
Qt Code:
  1. #include "mainwindow.h"
  2. #include "sqltreemodeltest.h"
  3. #include "proxytreemodeltest.h"
  4.  
  5. #include <QTreeView>
  6. #include <QVBoxLayout>
  7. #include <QSqlQuery>
  8.  
  9. MainWindow::MainWindow(QWidget *parent) :
  10. QMainWindow(parent)
  11. {
  12. QWidget *centralWidget = new QWidget(this);
  13. QVBoxLayout *verticalLayout = new QVBoxLayout(centralWidget);
  14. treeView = new QTreeView(centralWidget);
  15. verticalLayout->addWidget(treeView);
  16. setCentralWidget(centralWidget);
  17.  
  18. QSqlError error = initdb();
  19. if(error.isValid())
  20. qDebug() << error.text();
  21. setupModel();
  22. }
  23.  
  24. QSqlError MainWindow::initdb() {
  25.  
  26. QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
  27.  
  28. db.setDatabaseName(":memory:");
  29. if(!db.open())
  30. return db.lastError();
  31.  
  32. QStringList tables = db.tables();
  33. if(tables.contains("test", Qt::CaseInsensitive))
  34. if(!q.exec("DROP TABLE test"))
  35. return q.lastError();
  36.  
  37. if(!q.exec(QLatin1String("CREATE TABLE test(id INTEGER PRIMARY KEY, title VARCHAR, type INTEGER, parentId INTEGER DEFAULT 0)")))
  38. return q.lastError();
  39. if(!q.exec(QLatin1String("INSERT INTO test (title, type, parentId)"
  40. "VALUES ('row0', 0, 0),"
  41. "('row0a', 0, 1)")))
  42. return q.lastError();
  43. return QSqlError();
  44. }
  45.  
  46. void MainWindow::setupModel() {
  47.  
  48. sqlTreeModelTest *testModel = new sqlTreeModelTest(this);
  49. testModel->setTable("test");
  50. testModel->select();
  51.  
  52. proxyTreeModelTest *treeProxy = new proxyTreeModelTest(this);
  53. treeProxy->setSourceModel(testModel);
  54.  
  55. treeView->setModel(treeProxy);
  56. treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
  57. treeView->setSelectionMode(QAbstractItemView::SingleSelection);
  58. }
To copy to clipboard, switch view to plain text mode 

sqltreemodeltest.h
Qt Code:
  1. #ifndef SQLTREEMODELTEST_H
  2. #define SQLTREEMODELTEST_H
  3.  
  4. #include <QSqlTableModel>
  5.  
  6. class sqlTreeModelTest : public QSqlTableModel
  7. {
  8. Q_OBJECT
  9.  
  10. public:
  11. sqlTreeModelTest(QObject *parent=0);
  12. virtual Qt::ItemFlags flags(const QModelIndex &index) const;
  13. };
  14.  
  15. #endif // SQLTREEMODELTEST_H
To copy to clipboard, switch view to plain text mode 

sqltreemodeltest.cpp
Qt Code:
  1. #include "sqltreemodeltest.h"
  2.  
  3. sqlTreeModelTest::sqlTreeModelTest(QObject *parent) : QSqlTableModel(parent) {}
  4.  
  5. Qt::ItemFlags sqlTreeModelTest::flags(const QModelIndex &index) const {
  6. //QSqlTableModel doesn't allow children so use these flags
  7. if(index.isValid())
  8. return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
  9. else
  10. return 0;
  11. }
To copy to clipboard, switch view to plain text mode 

proxytreemodeltest.h
Qt Code:
  1. #ifndef PROXYTREEMODELTEST_H
  2. #define PROXYTREEMODELTEST_H
  3.  
  4. #include <QDebug>
  5. #include <QAbstractProxyModel>
  6.  
  7. class QSqlQuery;
  8.  
  9. class proxyTreeModelTest : public QAbstractProxyModel
  10. {
  11. Q_OBJECT
  12.  
  13. public:
  14. proxyTreeModelTest(QObject *parent=0);
  15. virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
  16. virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
  17. virtual QModelIndex parent(const QModelIndex &child) const;
  18.  
  19. virtual QModelIndex index(int row, int column, const QModelIndex &parent) const;
  20. virtual int rowCount(const QModelIndex &parent) const;
  21. virtual int columnCount(const QModelIndex &parent) const { return sourceModel()->columnCount(parent); }
  22.  
  23. virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const { return sourceModel()->headerData(section,orientation,role); }
  24. virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { return sourceModel()->setHeaderData(section,orientation,value,role); }
  25.  
  26. virtual bool hasChildren(const QModelIndex &parent) const;
  27.  
  28. private:
  29.  
  30. int getParentId(int childId) const;
  31. QSqlQuery* getChildren(int parentId) const;
  32. };
  33.  
  34. #endif // PROXYTREEMODELTEST_H
To copy to clipboard, switch view to plain text mode 

proxytreemodeltest.cpp
Qt Code:
  1. #include "proxytreemodeltest.h"
  2.  
  3. #include <QSqlQuery>
  4. #include <QSqlTableModel>
  5. #include <QSqlError>
  6.  
  7. #include <QDebug>
  8.  
  9. proxyTreeModelTest::proxyTreeModelTest(QObject *parent) : QAbstractProxyModel(parent) {}
  10.  
  11. QModelIndex proxyTreeModelTest::mapFromSource(const QModelIndex &sourceIndex) const {
  12. if(!sourceIndex.isValid())
  13. return QModelIndex();
  14.  
  15. int id = (sourceIndex.column() == 0) ? sourceIndex.data().toInt() : sourceIndex.sibling(sourceIndex.row(),0).data().toInt();
  16.  
  17. int row = -1;
  18. QSqlQuery* q = getChildren(getParentId(id));
  19. while(q->next()) {
  20. row++;
  21. if(q->value(0).toInt() == id)
  22. break;
  23. }
  24. delete q;
  25. return createIndex(row, sourceIndex.column(), id);
  26. }
  27.  
  28. QModelIndex proxyTreeModelTest::mapToSource(const QModelIndex &proxyIndex) const {
  29. if(!proxyIndex.isValid())
  30. return QModelIndex();
  31.  
  32. int id = proxyIndex.internalId();
  33.  
  34. q.exec("SELECT id FROM test");
  35. int row = -1;
  36. while(q.next()) {
  37. row++;
  38. if(q.value(0).toInt() == id)
  39. break;
  40. }
  41. return sourceModel()->index(row, proxyIndex.column());
  42. }
  43.  
  44. bool proxyTreeModelTest::hasChildren(const QModelIndex &parent) const {
  45.  
  46. q.prepare("SELECT COUNT(*) FROM test WHERE parentId=?");
  47. q.addBindValue(parent.internalId());
  48. q.exec();
  49. q.first();
  50. return q.value(0).toInt() > 0;
  51. }
  52.  
  53. QModelIndex proxyTreeModelTest::parent(const QModelIndex &childIndex) const {
  54.  
  55. int childId = childIndex.internalId();
  56. int parentId = getParentId(childId);
  57. if(parentId == 0)
  58. return QModelIndex();
  59.  
  60. int parentRow = -1;
  61. QSqlQuery* q = getChildren(getParentId(parentId));
  62. while(q->next()) {
  63. parentRow++;
  64. if(q->value(0).toInt() == parentId)
  65. break;
  66. }
  67. delete q;
  68. return createIndex(parentRow, childIndex.row(), parentId);
  69. }
  70.  
  71. QModelIndex proxyTreeModelTest::index(int row, int column, const QModelIndex &parent) const {
  72.  
  73. if(row < 0 || column < 0)
  74. return QModelIndex();
  75.  
  76. QSqlQuery* q = getChildren(parent.internalId());
  77. q->seek(row);
  78. int id = q->value(0).toInt();
  79. delete q;
  80. return createIndex(row, column, id);
  81. }
  82.  
  83. int proxyTreeModelTest::rowCount(const QModelIndex &parent) const {
  84.  
  85. QSqlQuery* q = getChildren(parent.internalId());
  86.  
  87. //use last() and at() since SQLite does not support query size calls
  88. q->last();
  89. int size = q->at() + 1;
  90. delete q;
  91. return size;
  92. }
  93.  
  94. int proxyTreeModelTest::getParentId(int childId) const {
  95. q.prepare("SELECT parentId FROM test WHERE id=?");
  96. q.addBindValue(childId);
  97. q.exec();
  98. q.first();
  99. return q.value(0).toInt();
  100. }
  101.  
  102. QSqlQuery* proxyTreeModelTest::getChildren(int parentId) const {
  103. QSqlQuery* q = new QSqlQuery;
  104. q->prepare("SELECT id FROM test WHERE parentId=?");
  105. q->addBindValue(parentId);
  106. q->exec();
  107. return q;
  108. }
To copy to clipboard, switch view to plain text mode 

main.cpp
Qt Code:
  1. #include "mainwindow.h"
  2. #include <QApplication>
  3.  
  4. int main(int argc, char *argv[])
  5. {
  6. QApplication a(argc, argv);
  7. MainWindow w;
  8. w.show();
  9.  
  10. return a.exec();
  11. }
To copy to clipboard, switch view to plain text mode