From d0e2659c30efc626a7fb47d9187447f681aaa607 Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Tue, 24 Dec 2024 03:46:20 +0100 Subject: [PATCH] List the labels of all lodgings in QML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To query the database, i have to run the query inside the same thread where the database was created, which means that Database should be a singleton not only within QML, but also in C++, and has to be the _same_ singleton in both worlds. Although i expose an object that i have created, i followed the same section titled “Exposing an existing object as a singleton” from Qt’s documentation[0]. The only difference is that i do not have to declare the element as a foreign type, because it is a bona fide QObject. [0]: https://doc.qt.io/qt-6/qml-singleton.html#exposing-an-existing- object-as-a-singleton --- src/CMakeLists.txt | 1 + src/ReservationsPage.qml | 13 ++++++ src/calendarlistmodel.cpp | 86 +++++++++++++++++++++++++++++++++++++++ src/calendarlistmodel.h | 40 ++++++++++++++++++ src/database.cpp | 24 ++++++++++- src/database.h | 16 +++++++- src/main.cpp | 2 + 7 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 src/calendarlistmodel.cpp create mode 100644 src/calendarlistmodel.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 79097e4..bca704a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ qt_add_qml_module(${PROJECT_NAME} QtCore QtQuick SOURCES + calendarlistmodel.cpp calendarlistmodel.h database.cpp database.h mnemonicattached.cpp mnemonicattached.h QML_FILES diff --git a/src/ReservationsPage.qml b/src/ReservationsPage.qml index 86350d3..5a3bb61 100644 --- a/src/ReservationsPage.qml +++ b/src/ReservationsPage.qml @@ -1,4 +1,5 @@ pragma ComponentBehavior: Bound +import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -15,6 +16,18 @@ Page { } } + ListView { + anchors.fill: parent + + delegate: Text { + required property string name + + text: name + } + model: CalendarListModel { + } + } + MnemonicAction { id: logoutAction diff --git a/src/calendarlistmodel.cpp b/src/calendarlistmodel.cpp new file mode 100644 index 0000000..60faf1e --- /dev/null +++ b/src/calendarlistmodel.cpp @@ -0,0 +1,86 @@ +#include "calendarlistmodel.h" +#include +#include +#include "database.h" + +struct CalendarListModel::Calendar +{ + QString name; +}; + +CalendarListModel::CalendarListModel(QObject *parent) + : QAbstractListModel{parent} + , m_calendars{} +{ + fetch(); +} + +CalendarListModel::~CalendarListModel() +{ + qDeleteAll(m_calendars); +} + +QVariant CalendarListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) { + return {}; + } + return section; +} + +int CalendarListModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + + return m_calendars.count(); +} + +QHash CalendarListModel::roleNames() const +{ + QHash roles = QAbstractItemModel::roleNames(); + roles[Name] = "name"; + return roles; +} + +void CalendarListModel::fetch() +{ + Database::query([]() { + QSqlQuery query("select label from campsite order by label"); + QVector calendars; + while (query.next()) { + Calendar calendar{query.value(0).toString()}; + calendars.append(calendar); + } + return calendars; + }).then([this](const QVector &calendars) { + if (calendars.empty()) { + return; + } + beginInsertRows(QModelIndex(), 0, calendars.count() - 1); + for (const Calendar &calendar : calendars) { + m_calendars.append(new Calendar(calendar)); + } + endInsertRows(); + }); +} + +QVariant CalendarListModel::data(const QModelIndex &index, int role) const +{ + if (!checkIndex(index, + QAbstractItemModel::CheckIndexOption::IndexIsValid + | QAbstractItemModel::CheckIndexOption::ParentIsInvalid)) { + return {}; + } + + Calendar *calendar = m_calendars.at(index.row()); + switch (role) { + case Name: + return calendar->name; + } + + return {}; +} + +#include "moc_calendarlistmodel.cpp" diff --git a/src/calendarlistmodel.h b/src/calendarlistmodel.h new file mode 100644 index 0000000..7b32c51 --- /dev/null +++ b/src/calendarlistmodel.h @@ -0,0 +1,40 @@ +#ifndef CALENDARLISTMODEL_H +#define CALENDARLISTMODEL_H + +#include +#include + +class CalendarListModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + +public: + enum Roles { + Name = Qt::UserRole, + }; + + explicit CalendarListModel(QObject *parent = nullptr); + ~CalendarListModel() override; + + QVariant headerData(int section, + Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QHash roleNames() const override; + +private: + struct Calendar; + + Q_DISABLE_COPY_MOVE(CalendarListModel) + + void fetch(); + + QVector m_calendars; +}; + +#endif // CALENDARLISTMODEL_H diff --git a/src/database.cpp b/src/database.cpp index 7a0b1db..91fdd87 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -2,7 +2,6 @@ #include #include #include -#include Database::Database(QObject *parent) : QObject{parent} @@ -12,6 +11,29 @@ Database::Database(QObject *parent) m_pool.setExpiryTimeout(-1); } +Database &Database::getInstance() +{ + static Database instance; + return instance; +} + +Database *Database::create(QQmlEngine *qmlEngine, QJSEngine *) +{ + Database &instance = getInstance(); + Q_ASSERT(qmlEngine->thread() == instance.thread()); + + static QQmlEngine *engine = nullptr; + if (engine) { + Q_ASSERT(qmlEngine == engine); + } else { + engine = qmlEngine; + } + + QJSEngine::setObjectOwnership(&instance, QJSEngine::CppOwnership); + + return &instance; +} + QFuture Database::open(const QString &user, const QString &password, const QString &hostName, diff --git a/src/database.h b/src/database.h index 59d4289..620f3f8 100644 --- a/src/database.h +++ b/src/database.h @@ -3,8 +3,9 @@ #include #include +#include #include -#include +#include class Database : public QObject { @@ -13,7 +14,9 @@ class Database : public QObject QML_ELEMENT public: - explicit Database(QObject *parent = nullptr); + static Database &getInstance(); + + static Database *create(QQmlEngine *qmlEngine, QJSEngine *); Q_INVOKABLE QFuture open(const QString &user, const QString &password, @@ -24,12 +27,21 @@ public: const QString &connectOptions); Q_INVOKABLE QFuture close(); + template + static auto query(Function &&f) + { + return QtConcurrent::run(&getInstance().m_pool, f); + } + signals: void closed(); void errorOcurred(const QString &errorMessage); void opened(); private: + explicit Database(QObject *parent = nullptr); + Q_DISABLE_COPY_MOVE(Database) + QThreadPool m_pool; }; diff --git a/src/main.cpp b/src/main.cpp index abd3739..af18428 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ #include #include +#include "database.h" int main(int argc, char *argv[]) { @@ -8,6 +9,7 @@ int main(int argc, char *argv[]) QGuiApplication app(argc, argv); + Database::getInstance(); // return ignored; create instance before QML engine QQmlApplicationEngine engine; QObject::connect( &engine,