From 13dacbeca5c1ad873fa88e4214b227fa703d5894 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 <0xFEEDC0DE64@gmail.com> Date: Sat, 15 Sep 2018 20:37:17 +0200 Subject: [PATCH] Imported existing sources --- DbDatabaseTree.pro | 23 ++++++ install.sql | 96 +++++++++++++++++++++++++ main.cpp | 29 ++++++++ mymodel.cpp | 171 +++++++++++++++++++++++++++++++++++++++++++++ mymodel.h | 70 +++++++++++++++++++ 5 files changed, 389 insertions(+) create mode 100644 DbDatabaseTree.pro create mode 100644 install.sql create mode 100644 main.cpp create mode 100644 mymodel.cpp create mode 100644 mymodel.h diff --git a/DbDatabaseTree.pro b/DbDatabaseTree.pro new file mode 100644 index 0000000..d9905cb --- /dev/null +++ b/DbDatabaseTree.pro @@ -0,0 +1,23 @@ +QT += core sql gui widgets + +DBLIBS += + +TARGET = databasetree + +PROJECT_ROOT = .. + +SOURCES += main.cpp \ + mymodel.cpp + +HEADERS += \ + mymodel.h + +FORMS += + +RESOURCES += + +TRANSLATIONS += + +DISTFILES += install.sql + +include($${PROJECT_ROOT}/app.pri) diff --git a/install.sql b/install.sql new file mode 100644 index 0000000..31ecf7e --- /dev/null +++ b/install.sql @@ -0,0 +1,96 @@ +DROP TABLE IF EXISTS `Tree`; + +CREATE TABLE `Tree` ( + `ID` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + `ParentID` INT UNSIGNED CHECK (`ID` <> `ParentID`), + `Name` VARCHAR(255) NOT NULL, + `Description` TEXT NOT NULL, + UNIQUE(`ParentID`, `Name`), + FOREIGN KEY (`ParentID`) REFERENCES `Tree`(`ID`) +); + +INSERT INTO `test`.`tree` (`ParentID`, `Name`, `Description`) VALUES +(NULL, "root0", "Eine Testbeschreibung"), +(NULL, "root1", "Eine Testbeschreibung"), +(NULL, "root2", "Eine Testbeschreibung"), +(NULL, "root3", "Eine Testbeschreibung"), +(1, "root0sub0", "Eine Testbeschreibung"), +(1, "root0sub1", "Eine Testbeschreibung"), +(1, "root0sub2", "Eine Testbeschreibung"), +(1, "root0sub3", "Eine Testbeschreibung"), +(2, "root1sub0", "Eine Testbeschreibung"), +(2, "root1sub1", "Eine Testbeschreibung"), +(2, "root1sub2", "Eine Testbeschreibung"), +(2, "root1sub3", "Eine Testbeschreibung"), +(3, "root2sub0", "Eine Testbeschreibung"), +(3, "root2sub1", "Eine Testbeschreibung"), +(3, "root2sub2", "Eine Testbeschreibung"), +(3, "root2sub3", "Eine Testbeschreibung"), +(4, "root3sub0", "Eine Testbeschreibung"), +(4, "root3sub1", "Eine Testbeschreibung"), +(4, "root3sub2", "Eine Testbeschreibung"), +(4, "root3sub3", "Eine Testbeschreibung"), +(5, "root0sub0sub0", "Eine Testbeschreibung"), +(5, "root0sub0sub1", "Eine Testbeschreibung"), +(5, "root0sub0sub2", "Eine Testbeschreibung"), +(5, "root0sub0sub3", "Eine Testbeschreibung"), +(6, "root0sub1sub0", "Eine Testbeschreibung"), +(6, "root0sub1sub1", "Eine Testbeschreibung"), +(6, "root0sub1sub2", "Eine Testbeschreibung"), +(6, "root0sub1sub3", "Eine Testbeschreibung"), +(6, "root0sub2sub0", "Eine Testbeschreibung"), +(7, "root0sub2sub1", "Eine Testbeschreibung"), +(7, "root0sub2sub2", "Eine Testbeschreibung"), +(7, "root0sub2sub3", "Eine Testbeschreibung"), +(8, "root0sub3sub0", "Eine Testbeschreibung"), +(8, "root0sub3sub1", "Eine Testbeschreibung"), +(8, "root0sub3sub2", "Eine Testbeschreibung"), +(8, "root0sub3sub3", "Eine Testbeschreibung"), +(9, "root1sub0sub0", "Eine Testbeschreibung"), +(9, "root1sub0sub1", "Eine Testbeschreibung"), +(9, "root1sub0sub2", "Eine Testbeschreibung"), +(9, "root1sub0sub3", "Eine Testbeschreibung"), +(10, "root1sub1sub0", "Eine Testbeschreibung"), +(10, "root1sub1sub1", "Eine Testbeschreibung"), +(10, "root1sub1sub2", "Eine Testbeschreibung"), +(10, "root1sub1sub3", "Eine Testbeschreibung"), +(11, "root1sub2sub0", "Eine Testbeschreibung"), +(11, "root1sub2sub1", "Eine Testbeschreibung"), +(11, "root1sub2sub2", "Eine Testbeschreibung"), +(11, "root1sub2sub3", "Eine Testbeschreibung"), +(12, "root1sub3sub0", "Eine Testbeschreibung"), +(12, "root1sub3sub1", "Eine Testbeschreibung"), +(12, "root1sub3sub2", "Eine Testbeschreibung"), +(12, "root1sub3sub3", "Eine Testbeschreibung"), +(13, "root2sub0sub0", "Eine Testbeschreibung"), +(13, "root2sub0sub1", "Eine Testbeschreibung"), +(13, "root2sub0sub2", "Eine Testbeschreibung"), +(13, "root2sub0sub3", "Eine Testbeschreibung"), +(14, "root2sub1sub0", "Eine Testbeschreibung"), +(14, "root2sub1sub1", "Eine Testbeschreibung"), +(14, "root2sub1sub2", "Eine Testbeschreibung"), +(14, "root2sub1sub3", "Eine Testbeschreibung"), +(15, "root2sub2sub0", "Eine Testbeschreibung"), +(15, "root2sub2sub1", "Eine Testbeschreibung"), +(15, "root2sub2sub2", "Eine Testbeschreibung"), +(15, "root2sub2sub3", "Eine Testbeschreibung"), +(16, "root2sub3sub0", "Eine Testbeschreibung"), +(16, "root2sub3sub1", "Eine Testbeschreibung"), +(16, "root2sub3sub2", "Eine Testbeschreibung"), +(16, "root2sub3sub3", "Eine Testbeschreibung"), +(17, "root3sub0sub0", "Eine Testbeschreibung"), +(17, "root3sub0sub1", "Eine Testbeschreibung"), +(17, "root3sub0sub2", "Eine Testbeschreibung"), +(17, "root3sub0sub3", "Eine Testbeschreibung"), +(18, "root3sub1sub0", "Eine Testbeschreibung"), +(18, "root3sub1sub1", "Eine Testbeschreibung"), +(18, "root3sub1sub2", "Eine Testbeschreibung"), +(18, "root3sub1sub3", "Eine Testbeschreibung"), +(19, "root3sub2sub0", "Eine Testbeschreibung"), +(19, "root3sub2sub1", "Eine Testbeschreibung"), +(19, "root3sub2sub2", "Eine Testbeschreibung"), +(19, "root3sub2sub3", "Eine Testbeschreibung"), +(20, "root3sub3sub0", "Eine Testbeschreibung"), +(20, "root3sub3sub1", "Eine Testbeschreibung"), +(20, "root3sub3sub2", "Eine Testbeschreibung"), +(20, "root3sub3sub3", "Eine Testbeschreibung"); diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..d75fb88 --- /dev/null +++ b/main.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +#include "mymodel.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "myConnection"); + db.setHostName("localhost"); + db.setUserName("test"); + db.setPassword("test"); + db.setDatabaseName("test"); + if(!db.open()) + { + qWarning() << db.lastError().text(); + return -1; + } + + QTreeView view; + view.setModel(new MyModel(db, &app)); + view.show(); + + return app.exec(); +} diff --git a/mymodel.cpp b/mymodel.cpp new file mode 100644 index 0000000..447377e --- /dev/null +++ b/mymodel.cpp @@ -0,0 +1,171 @@ +#include "mymodel.h" + +#include +#include + +void ItemData::fetch(QSqlQuery &query) +{ + if(m_fetched) + { + qWarning() << m_id << "already fetched"; + return; + } + + qDebug() << "loadChildrens" << m_id; + + query.bindValue(":ParentID", m_id); + if(!query.exec()) + { + qWarning() << query.lastError().text(); + qFatal("could not execute query"); + return; + } + + while(query.next()) + m_childrens.append(new ItemData(this, query.value("ID").toUInt(), query.value("Name").toString(), query.value("Description").toString())); + + m_fetched = true; +} + +MyModel::MyModel(QSqlDatabase &db, QObject *parent) : + QAbstractItemModel(parent), + m_db(db), + m_query(m_db) +{ + if(!m_query.prepare("SELECT `ID`, `Name`, `Description` FROM `Tree` WHERE (:ParentID = 0 AND `ParentID` IS NULL) OR (`ParentID` = :ParentID);")) + { + qWarning() << m_query.lastError().text(); + qFatal("could not prepare query"); + return; + } + + m_root = new ItemData(Q_NULLPTR, 0); + //m_root->fetch(m_query); +} + +QModelIndex MyModel::index(int row, int column, const QModelIndex &parent) const +{ + if(!hasIndex(row, column, parent)) + return QModelIndex(); + + auto parentData = parent.isValid() ? static_cast(parent.internalPointer()) : m_root; + + auto childData = parentData->child(row); + if (childData) + return createIndex(row, column, childData); + else + return QModelIndex(); +} + +QModelIndex MyModel::parent(const QModelIndex &child) const +{ + if(!child.isValid()) + return QModelIndex(); + + auto childData = static_cast(child.internalPointer()); + auto parentData = childData->parent(); + + if(parentData == m_root) + return QModelIndex(); + + return createIndex(parentData->row(), 0, parentData); +} + +int MyModel::rowCount(const QModelIndex &parent) const +{ + if(parent.column() > 0) + return 0; + + auto parentData = parent.isValid() ? static_cast(parent.internalPointer()) : m_root; + + return parentData->childCount(); +} + +int MyModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return 2; +} + +QVariant MyModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + switch(role) + { + case Qt::DisplayRole: + case Qt::EditRole: + auto itemData = static_cast(index.internalPointer()); + switch(index.column()) + { + case 0: return itemData->name(); + case 1: return itemData->description(); + } + } + + return QVariant(); +} + +QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch(orientation) + { + case Qt::Horizontal: + switch(section) + { + case 0: + switch(role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return tr("Name"); + } + case 1: + switch(role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return tr("Beschreibung"); + } + } + break; + case Qt::Vertical: + break; + } + + return QVariant(); +} + +bool MyModel::hasChildren(const QModelIndex &parent) const +{ + if(parent.column() > 0) + return false; + + auto parentData = parent.isValid() ? static_cast(parent.internalPointer()) : m_root; + + if(!parentData->fetched()) + return true; + + return parentData->childCount() > 0; +} + +void MyModel::fetchMore(const QModelIndex &parent) +{ + if(parent.column() > 0) + return; + + auto parentData = parent.isValid() ? static_cast(parent.internalPointer()) : m_root; + + parentData->fetch(m_query); +} + +bool MyModel::canFetchMore(const QModelIndex &parent) const +{ + if(parent.column() > 0) + return false; + + auto parentData = parent.isValid() ? static_cast(parent.internalPointer()) : m_root; + + return !parentData->fetched(); +} diff --git a/mymodel.h b/mymodel.h new file mode 100644 index 0000000..b7df2ea --- /dev/null +++ b/mymodel.h @@ -0,0 +1,70 @@ +#ifndef MYMODEL_H +#define MYMODEL_H + +#include +#include +#include +#include +#include + +class ItemData +{ +public: + inline explicit ItemData(ItemData *parent, quint32 id, const QString &name = QString(), const QString &description = QString()) : + m_parent(parent), + m_id(id), + m_name(name), + m_description(description), + m_fetched(false) + {} + + inline ~ItemData() { qDeleteAll(m_childrens); } + + inline ItemData *parent() const { return m_parent; } + inline const QList &childrens() const { return m_childrens; } + inline quint32 id() const { return m_id; } + inline const QString &name() const { return m_name; } + inline const QString &description() const { return m_description; } + inline bool fetched() const { return m_fetched; } + + inline int row() { if(m_parent) return m_parent->childrens().indexOf(this); return 0; } + inline ItemData *child(int row) const { return m_childrens.at(row); } + inline int childCount() const { return m_childrens.count(); } + + void fetch(QSqlQuery &query); + +private: + ItemData *m_parent; + QList m_childrens; + quint32 m_id; + QString m_name; + QString m_description; + bool m_fetched; +}; + +class MyModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit MyModel(QSqlDatabase &db, QObject *parent = 0); + + QModelIndex index(int row, int column, const QModelIndex &parent) const Q_DECL_OVERRIDE; + QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &parent) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex &parent) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; + QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE; + bool hasChildren(const QModelIndex &parent) const Q_DECL_OVERRIDE; + void fetchMore(const QModelIndex &parent) Q_DECL_OVERRIDE; + bool canFetchMore(const QModelIndex &parent) const Q_DECL_OVERRIDE; + +private: + void loadChildrens(ItemData *itemData); + + QSqlDatabase &m_db; + QSqlQuery m_query; + ItemData *m_root; +}; + +#endif // MYMODEL_H