Viewing QSqlTableModel in QTreeView through a proxy model
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
Code:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSqlError>
class sqlTreeModelTest;
{
Q_OBJECT
public:
explicit MainWindow
(QWidget *parent
= 0);
private:
void setupModel();
};
#endif // MAINWINDOW_H
mainwindow.cpp
Code:
#include "mainwindow.h"
#include "sqltreemodeltest.h"
#include "proxytreemodeltest.h"
#include <QTreeView>
#include <QVBoxLayout>
#include <QSqlQuery>
MainWindow
::MainWindow(QWidget *parent
) :{
verticalLayout->addWidget(treeView);
setCentralWidget(centralWidget);
if(error.isValid())
qDebug() << error.text();
setupModel();
}
db.setDatabaseName(":memory:");
if(!db.open())
return db.lastError();
if(tables.contains("test", Qt::CaseInsensitive))
if(!q.exec("DROP TABLE test"))
return q.lastError();
if(!q.
exec(QLatin1String("CREATE TABLE test(id INTEGER PRIMARY KEY, title VARCHAR, type INTEGER, parentId INTEGER DEFAULT 0)"))) return q.lastError();
if(!q.
exec(QLatin1String("INSERT INTO test (title, type, parentId)" "VALUES ('row0', 0, 0),"
"('row0a', 0, 1)")))
return q.lastError();
}
void MainWindow::setupModel() {
sqlTreeModelTest *testModel = new sqlTreeModelTest(this);
testModel->setTable("test");
testModel->select();
proxyTreeModelTest *treeProxy = new proxyTreeModelTest(this);
treeProxy->setSourceModel(testModel);
treeView->setModel(treeProxy);
}
sqltreemodeltest.h
Code:
#ifndef SQLTREEMODELTEST_H
#define SQLTREEMODELTEST_H
#include <QSqlTableModel>
{
Q_OBJECT
public:
sqlTreeModelTest
(QObject *parent
=0);
virtual Qt
::ItemFlags flags
(const QModelIndex &index
) const;
};
#endif // SQLTREEMODELTEST_H
sqltreemodeltest.cpp
Code:
#include "sqltreemodeltest.h"
Qt
::ItemFlags sqlTreeModelTest
::flags(const QModelIndex &index
) const { //QSqlTableModel doesn't allow children so use these flags
if(index.isValid())
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
else
return 0;
}
proxytreemodeltest.h
Code:
#ifndef PROXYTREEMODELTEST_H
#define PROXYTREEMODELTEST_H
#include <QDebug>
#include <QAbstractProxyModel>
{
Q_OBJECT
public:
proxyTreeModelTest
(QObject *parent
=0);
virtual int columnCount
(const QModelIndex &parent
) const { return sourceModel
()->columnCount
(parent
);
}
virtual QVariant headerData
(int section, Qt
::Orientation orientation,
int role
) const { return sourceModel
()->headerData
(section,orientation,role
);
} virtual bool setHeaderData
(int section, Qt
::Orientation orientation,
const QVariant &value,
int role
) { return sourceModel
()->setHeaderData
(section,orientation,value,role
);
}
virtual bool hasChildren
(const QModelIndex &parent
) const;
private:
int getParentId(int childId) const;
};
#endif // PROXYTREEMODELTEST_H
proxytreemodeltest.cpp
Code:
#include "proxytreemodeltest.h"
#include <QSqlQuery>
#include <QSqlTableModel>
#include <QSqlError>
#include <QDebug>
if(!sourceIndex.isValid())
int id = (sourceIndex.column() == 0) ? sourceIndex.data().toInt() : sourceIndex.sibling(sourceIndex.row(),0).data().toInt();
int row = -1;
while(q->next()) {
row++;
if(q->value(0).toInt() == id)
break;
}
delete q;
return createIndex(row, sourceIndex.column(), id);
}
if(!proxyIndex.isValid())
int id = proxyIndex.internalId();
q.exec("SELECT id FROM test");
int row = -1;
while(q.next()) {
row++;
if(q.value(0).toInt() == id)
break;
}
return sourceModel()->index(row, proxyIndex.column());
}
bool proxyTreeModelTest
::hasChildren(const QModelIndex &parent
) const {
q.prepare("SELECT COUNT(*) FROM test WHERE parentId=?");
q.addBindValue(parent.internalId());
q.exec();
q.first();
return q.value(0).toInt() > 0;
}
int childId = childIndex.internalId();
int parentId = getParentId(childId);
if(parentId == 0)
int parentRow = -1;
QSqlQuery* q
= getChildren
(getParentId
(parentId
));
while(q->next()) {
parentRow++;
if(q->value(0).toInt() == parentId)
break;
}
delete q;
return createIndex(parentRow, childIndex.row(), parentId);
}
if(row < 0 || column < 0)
QSqlQuery* q
= getChildren
(parent.
internalId());
q->seek(row);
int id = q->value(0).toInt();
delete q;
return createIndex(row, column, id);
}
int proxyTreeModelTest
::rowCount(const QModelIndex &parent
) const {
QSqlQuery* q
= getChildren
(parent.
internalId());
//use last() and at() since SQLite does not support query size calls
q->last();
int size = q->at() + 1;
delete q;
return size;
}
int proxyTreeModelTest::getParentId(int childId) const {
q.prepare("SELECT parentId FROM test WHERE id=?");
q.addBindValue(childId);
q.exec();
q.first();
return q.value(0).toInt();
}
QSqlQuery* proxyTreeModelTest
::getChildren(int parentId
) const { q->prepare("SELECT id FROM test WHERE parentId=?");
q->addBindValue(parentId);
q->exec();
return q;
}
main.cpp
Code:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
MainWindow w;
w.show();
return a.exec();
}
Re: Viewing QSqlTableModel in QTreeView through a proxy model
In sqltreemodeltest.cpp.
return Qt::ItemIsEnabled | Qt::ItemIsSelectable|Qt::ItemIsEditable;
You need add a flag Qt::ItemIsEditable;
Added after 19 minutes:
Oh, sorry, I misunderstand the question.
Re: Viewing QSqlTableModel in QTreeView through a proxy model
i want to say;
change
"treeView->setSelectionBehavior(QAbstractItemView::SelectRow s);"
to
"treeView->setSelectionBehavior(QAbstractItemView::SelectIte ms);"
in mainwindow.cpp
thats all
but, i think you are not a beginner and this solution is not for you :) isn,t it?
and how we add and delete nodes, any idea?
(i am a beginner)
Re: Viewing QSqlTableModel in QTreeView through a proxy model
Quote:
and how we add and delete nodes, any idea?
The way you add or delete nodes is to change the QAbstractItemModel-based model that the QTreeView uses to build itself. The QTreeView is simply a way to display the model, and does not contain any data itself beyond what is needed to show the model on screen.
You can find information on tree views and item models in the Qt documentation, tutorials, and examples.