From 6ea21266388c17ce2832a6d24c97a4df71ad8251 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 25 Feb 2024 13:21:59 +0100 Subject: [PATCH] Squashed commit of the following: Merge: f614117d f0d49d9e Author: Uwe Rathmann Date: Sun Feb 25 09:11:12 2024 +0100 Merge branch 'master' of https://github.com/ovenpasta/qskinny into models ( Author: Aldo Nicolas Bruno ) --- cmake/QskFindMacros.cmake | 4 +- playground/CMakeLists.txt | 4 + playground/models/CMakeLists.txt | 8 ++ playground/models/QskModelObjectBinder.cpp | 157 +++++++++++++++++++++ playground/models/QskModelObjectBinder.h | 32 +++++ playground/models/Window.cpp | 82 +++++++++++ playground/models/Window.h | 22 +++ playground/models/main.cpp | 35 +++++ 8 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 playground/models/CMakeLists.txt create mode 100644 playground/models/QskModelObjectBinder.cpp create mode 100644 playground/models/QskModelObjectBinder.h create mode 100644 playground/models/Window.cpp create mode 100644 playground/models/Window.h create mode 100644 playground/models/main.cpp diff --git a/cmake/QskFindMacros.cmake b/cmake/QskFindMacros.cmake index f2791254..e2b01e7f 100644 --- a/cmake/QskFindMacros.cmake +++ b/cmake/QskFindMacros.cmake @@ -54,11 +54,11 @@ macro(qsk_setup_Qt) # C++, but QSkinny itself does not need the WebEngine at all. if (QT_VERSION_MAJOR VERSION_LESS 6) - find_package(Qt${QT_VERSION_MAJOR} QUIET OPTIONAL_COMPONENTS WebEngine) + find_package(Qt${QT_VERSION_MAJOR} QUIET OPTIONAL_COMPONENTS WebEngine Sql) set( Qt5WebEngineQuick_FOUND ${Qt5WebEngine_FOUND} ) else() find_package(Qt${QT_VERSION_MAJOR} QUIET - OPTIONAL_COMPONENTS WebEngineCore WebEngineQuick) + OPTIONAL_COMPONENTS WebEngineCore WebEngineQuick Sql) endif() if( NOT Qt${QT_VERSION_MAJOR}WebEngineQuick_FOUND) diff --git a/playground/CMakeLists.txt b/playground/CMakeLists.txt index 2bc146fa..7ba9e0b5 100644 --- a/playground/CMakeLists.txt +++ b/playground/CMakeLists.txt @@ -32,3 +32,7 @@ endif() if(TARGET Qt::QuickWidgets) add_subdirectory(grids) endif() + +if(TARGET Qt::Sql) + add_subdirectory(models) +endif() diff --git a/playground/models/CMakeLists.txt b/playground/models/CMakeLists.txt new file mode 100644 index 00000000..541f5992 --- /dev/null +++ b/playground/models/CMakeLists.txt @@ -0,0 +1,8 @@ +############################################################################ +# QSkinny - Copyright (C) The authors +# SPDX-License-Identifier: BSD-3-Clause +############################################################################ + +qsk_add_example(models QskModelObjectBinder.cpp QskModelObjectBinder.h Window.h Window.cpp main.cpp) + +target_link_libraries(models PRIVATE Qt::Sql) \ No newline at end of file diff --git a/playground/models/QskModelObjectBinder.cpp b/playground/models/QskModelObjectBinder.cpp new file mode 100644 index 00000000..13beeb28 --- /dev/null +++ b/playground/models/QskModelObjectBinder.cpp @@ -0,0 +1,157 @@ +// +// Created by aldo on 27/01/21. +// + +#include "QskModelObjectBinder.h" +#include +#include +#include + +class QskModelObjectBinder::PrivateData +{ +public: + PrivateData( ) + { + } + + struct Item { + Item() :valid(false), column(-1) {}; + Item(QObject *obj, int column, const QByteArray& property) + : valid (true), object( obj ), column( column ), property( property ) + { + }; + bool valid; + QObject* object; + int column; + QByteArray property; + }; + + void updateObjectProperty(Item & x) + { + Q_ASSERT( x.object != nullptr ); + Q_ASSERT( ! x.property.isEmpty() ); + x.object->setProperty(x.property, + m->index(currentRowIndex.row(),x.column).data(Qt::EditRole)); + } + + void updateAllObjects() + { + for (auto it = items.begin(); it != items.end(); ++it) { + updateObjectProperty(it.value()); + } + } + + void updateModelField(Item& x) + { + Q_ASSERT( x.valid ); + Q_ASSERT( x.object != nullptr ); + auto prop =x.object->property( x.property ); + Q_ASSERT( prop.isValid() ); + + m->setData(m->index(currentRowIndex.row(),x.column),prop); + } + + void updateModelFields() + { + for (auto it = items.begin(); it != items.end(); ++it) + { + updateModelField( it.value() ); + } + } + +public: + QMap items; + QAbstractItemModel* m; + QPersistentModelIndex currentRowIndex; +}; + +QskModelObjectBinder::QskModelObjectBinder(QObject* parent) +: QObject( parent ) +{ + m_data=std::make_unique( ); +} + +QskModelObjectBinder::QskModelObjectBinder(QAbstractItemModel *model, QObject* parent) +: QObject( parent ) +{ + m_data=std::make_unique(); + bindModel(model); +} + + +void QskModelObjectBinder::bindObject(QObject *obj, int column, const QByteArray &propertyName) +{ + Q_ASSERT( obj != nullptr ); + + if(obj == nullptr) + return; + + QMetaProperty prop; + auto meta = obj->metaObject(); + if (propertyName.isEmpty()) + { + prop = meta->userProperty(); + }else + { + auto idx = meta->indexOfProperty( propertyName ); + if(idx>=0) + { + prop=meta->property(idx); + } + } + Q_ASSERT(! prop.isValid()); + + PrivateData::Item item(obj,column,prop.name()); + m_data->items[obj]=item; +} + +void QskModelObjectBinder::unbindObject(QObject *obj) +{ + m_data->items.remove(obj); +} + +void QskModelObjectBinder::bindModel(QAbstractItemModel *model) +{ + m_data->m=model; + m_data->currentRowIndex=QModelIndex(); + if(model) { + connect(m_data->m,&QAbstractItemModel::dataChanged,this, + &QskModelObjectBinder::modelDataChanged); + } +} + +void QskModelObjectBinder::modelDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector &) +{ + const int row = m_data->currentRowIndex.row(); + for (auto it = m_data->items.begin(); it != m_data->items.end(); ++it) { + auto & x = it.value(); + const int col = x.column; + if( qBound( topLeft.row(),row,bottomRight.row()) == row + && qBound( topLeft.column(), col, bottomRight.column()) == col) { + m_data->updateObjectProperty(x); + } + } +} + +void QskModelObjectBinder::selectRow( int row ) +{ + Q_ASSERT( m_data->m != nullptr ); + + if ( !m_data->m ) + return; + + m_data->currentRowIndex = m_data->m->index( row, 0 ); + m_data->updateAllObjects(); +} + +int QskModelObjectBinder::currentRow() const +{ + return m_data->currentRowIndex.row(); +} + +void QskModelObjectBinder::updateModel() +{ + m_data->updateModelFields(); +} + +#include "moc_QskModelObjectBinder.cpp" diff --git a/playground/models/QskModelObjectBinder.h b/playground/models/QskModelObjectBinder.h new file mode 100644 index 00000000..2f94a984 --- /dev/null +++ b/playground/models/QskModelObjectBinder.h @@ -0,0 +1,32 @@ +#ifndef QSKDATACONTROLMAPPER_H +#define QSKDATACONTROLMAPPER_H + +#include + +class QskModelObjectBinder : public QObject { + Q_OBJECT + +public: + QskModelObjectBinder(QObject *parent=nullptr); + QskModelObjectBinder(QAbstractItemModel* model, QObject *parent=nullptr); + + void bindObject(QObject*, int column, const QByteArray &propertyName=""); + void unbindObject(QObject*o); + + void bindModel(QAbstractItemModel* model); + + void selectRow(int row); + [[nodiscard]] int currentRow() const; + + void updateModel(); + +private Q_SLOTS: + void modelDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector &); + +private: + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + + +#endif //QSKDATACONTROLMAPPER_H \ No newline at end of file diff --git a/playground/models/Window.cpp b/playground/models/Window.cpp new file mode 100644 index 00000000..23d3b665 --- /dev/null +++ b/playground/models/Window.cpp @@ -0,0 +1,82 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "Window.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +Window::Window( ) + +{ + auto db = QSqlDatabase::addDatabase("QSQLITE"); + db.setDatabaseName(":memory:"); + db.open(); + QSqlQuery q(db); + q.exec("create table test(id integer,value text);"); + q.exec("insert into test (id,value) values (1,'HELLO');"); + q.exec("insert into test (id,value) values (2,'WORLD');"); + + auto table = new QSqlTableModel (nullptr,db); + table->setTable( "test" ); + table->select(); + + auto txt = new QskTextInput(); + auto spin = new QskSpinBox(); + + auto mapper = new QskModelObjectBinder(); + mapper->bindModel(table); + /* + Not needed if we set USER true for the corresponding Q_PROPERTY in QskBoundedInput and QskTextInput + maybe could be done also for other controls + mapper->bindControl(spin, 0,"value"); + mapper->bindControl(txt, 1, "text"); + */ + mapper->bindObject(spin, 0); // needs USER=true for value in QskBoundedInput + mapper->bindObject(txt, 1); // needs USER=true for text in QskTextInput + + // this loads the record from the first row and updates the controls data. + mapper->selectRow(0); + + auto v = new QskLinearBox(Qt::Vertical); + v->addSpacer( 0,100 ); + v->addItem( spin ); + v->addItem( txt ); + addItem(v); + auto h = new QskLinearBox(Qt::Horizontal); + auto prev = new QskPushButton("<",h); + auto next = new QskPushButton(">",h); + connect(prev,&QskPushButton::clicked,[=]() { mapper->selectRow( 0 ); }); + connect(next,&QskPushButton::clicked,[=]() { mapper->selectRow( 1 ); }); + v->addItem(h); + + auto save = new QskPushButton("Save Data to Model",v); + connect(save,&QskPushButton::clicked,[=]() { + // this will update the current record with the data from the SpinBox and TextInput + mapper->updateModel(); + // just for illustration we print out the record + auto r = table->record(mapper->currentRow()); + qDebug() << r; + }); + auto set0 = new QskPushButton("Set Model field to 0",v); + connect(set0,&QskPushButton::clicked,[=]() { + // this should trigger the binder and update the spinbox + table->setData( table->index(mapper->currentRow(),0),0 ); + }); + v->addSpacer( 0,100 ); +} + + +#include "moc_Window.cpp" diff --git a/playground/models/Window.h b/playground/models/Window.h new file mode 100644 index 00000000..2ededbbc --- /dev/null +++ b/playground/models/Window.h @@ -0,0 +1,22 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include +#include + +class QskDialogButtonBox; +class QskLinearBox; + +class Window : public QskWindow +{ + Q_OBJECT + + public: + Window(); + + +}; diff --git a/playground/models/main.cpp b/playground/models/main.cpp new file mode 100644 index 00000000..5b0476bb --- /dev/null +++ b/playground/models/main.cpp @@ -0,0 +1,35 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "Window.h" + +#include + +#include +#include + +#include + +int main( int argc, char* argv[] ) +{ +#ifdef ITEM_STATISTICS + QskObjectCounter counter( true ); +#endif + + QGuiApplication app( argc, argv ); + + SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); + + Window window; + window.resize( 600, 400 ); + + auto focusIndicator = new QskFocusIndicator(); + focusIndicator->setObjectName( "FocusIndicator" ); + + window.addItem( focusIndicator ); + window.show(); + + return app.exec(); +}