QskDataObjectBinder
This commit is contained in:
parent
3902d051ca
commit
0f825eebb4
|
@ -3,6 +3,6 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
############################################################################
|
||||
|
||||
qsk_add_example(models Window.h Window.cpp QskDataControlMapper.cpp QskDataControlMapper.h main.cpp)
|
||||
qsk_add_example(models QskModelObjectBinder.cpp QskModelObjectBinder.h Window.h Window.cpp main.cpp)
|
||||
|
||||
target_link_libraries(models PRIVATE Qt::Sql)
|
|
@ -1,78 +0,0 @@
|
|||
//
|
||||
// Created by aldo on 27/01/21.
|
||||
//
|
||||
|
||||
#include "QskDataControlMapper.h"
|
||||
|
||||
QskDataControlMapper::QskDataControlMapper() : m(nullptr){
|
||||
|
||||
}
|
||||
|
||||
void QskDataControlMapper::addMapping(QskControl *widget, int section) {
|
||||
if(widget)
|
||||
addMapping(widget,section,widget->metaObject()->userProperty().name());
|
||||
}
|
||||
|
||||
void QskDataControlMapper::addMapping(QskControl *widget, int section, const QByteArray &propertyName) {
|
||||
Item item;
|
||||
item.control=widget;
|
||||
item.section=section;
|
||||
item.property=propertyName;
|
||||
item.index=m->index(currentIndex.row(),section);
|
||||
items.append(item);
|
||||
}
|
||||
|
||||
void QskDataControlMapper::setModel(QAbstractItemModel *model) {
|
||||
m=model;
|
||||
currentIndex=QModelIndex();
|
||||
if(m) {
|
||||
connect(m,&QAbstractItemModel::dataChanged,this,
|
||||
&QskDataControlMapper::_dataChanged);
|
||||
}
|
||||
}
|
||||
|
||||
bool QskDataControlMapper::qContainsIndex(const QModelIndex & idx, const QModelIndex & topLeft, const QModelIndex & bottomRight)
|
||||
{
|
||||
return idx.row() >= topLeft.row() && idx.row() <= bottomRight.row()
|
||||
&& idx.column() >= topLeft.column() && idx.column() <= bottomRight.column();
|
||||
}
|
||||
|
||||
void QskDataControlMapper::_dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> &)
|
||||
{
|
||||
for (auto& x : items) {
|
||||
if (qContainsIndex(x.index, topLeft, bottomRight)) {
|
||||
populate(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QskDataControlMapper::populate(QskDataControlMapper::Item & x)
|
||||
{
|
||||
x.control->setProperty(x.property,
|
||||
m->index(currentIndex.row(),x.section).data(Qt::EditRole));
|
||||
}
|
||||
|
||||
void QskDataControlMapper::populate()
|
||||
{
|
||||
for (auto& x : items) {
|
||||
populate(x);
|
||||
}
|
||||
}
|
||||
|
||||
void QskDataControlMapper::submit() {
|
||||
if(!m)
|
||||
return;
|
||||
for(auto & x : items) {
|
||||
m->setData(m->index(currentIndex.row(), x.section),
|
||||
x.control->property(x.property));
|
||||
}
|
||||
}
|
||||
|
||||
void QskDataControlMapper::setCurrentIndex(int index) {
|
||||
if(!m)
|
||||
return;
|
||||
currentIndex=m->index(index,0);
|
||||
populate();
|
||||
}
|
||||
|
||||
#include "moc_QskDataControlMapper.cpp"
|
|
@ -1,54 +0,0 @@
|
|||
#ifndef QSKDATACONTROLMAPPER_H
|
||||
#define QSKDATACONTROLMAPPER_H
|
||||
|
||||
#include <QskControl.h>
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
class QskDataControlMapper : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
struct Item;
|
||||
|
||||
public:
|
||||
|
||||
QskDataControlMapper();
|
||||
|
||||
void addMapping(QskControl* widget, int section);
|
||||
void addMapping(QskControl* widget, int section, const QByteArray &propertyName);
|
||||
|
||||
void setModel(QAbstractItemModel* model);
|
||||
|
||||
static bool qContainsIndex(const QModelIndex &idx, const QModelIndex &topLeft,
|
||||
const QModelIndex &bottomRight);
|
||||
|
||||
void _dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &);
|
||||
|
||||
void populate(QskDataControlMapper::Item & x);
|
||||
|
||||
void populate ();
|
||||
|
||||
void submit();
|
||||
|
||||
void setCurrentIndex(int index);
|
||||
|
||||
private:
|
||||
|
||||
QList<Item> items;
|
||||
|
||||
QAbstractItemModel* m;
|
||||
QPersistentModelIndex currentIndex;
|
||||
|
||||
struct Item {
|
||||
QPointer<QskControl> control;
|
||||
int section;
|
||||
QByteArray property;
|
||||
QPersistentModelIndex index;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //QSKDATACONTROLMAPPER_H
|
|
@ -0,0 +1,160 @@
|
|||
//
|
||||
// Created by aldo on 27/01/21.
|
||||
//
|
||||
|
||||
#include "QskModelObjectBinder.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QPointer>
|
||||
#include <qassert.h>
|
||||
|
||||
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;
|
||||
QPointer< 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(modelIndex.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(modelIndex.row(),x.column),prop);
|
||||
}
|
||||
|
||||
void updateModelFields()
|
||||
{
|
||||
for (auto it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
updateModelField( it.value() );
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
QMap<QObject*,Item> items;
|
||||
QAbstractItemModel* m;
|
||||
QPersistentModelIndex modelIndex;
|
||||
};
|
||||
|
||||
static bool modelContainsIndex(const QModelIndex & idx, const QModelIndex & topLeft, const QModelIndex & bottomRight)
|
||||
{
|
||||
return idx.row() >= topLeft.row() && idx.row() <= bottomRight.row()
|
||||
&& idx.column() >= topLeft.column() && idx.column() <= bottomRight.column();
|
||||
}
|
||||
|
||||
QskModelObjectBinder::QskModelObjectBinder(QObject* parent)
|
||||
: QObject( parent )
|
||||
{
|
||||
m_data=std::make_unique<PrivateData>( );
|
||||
}
|
||||
|
||||
QskModelObjectBinder::QskModelObjectBinder(QAbstractItemModel *model, QObject* parent)
|
||||
: QObject( parent )
|
||||
{
|
||||
m_data=std::make_unique<PrivateData>();
|
||||
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->modelIndex=QModelIndex();
|
||||
if(model) {
|
||||
connect(m_data->m,&QAbstractItemModel::dataChanged,this,
|
||||
&QskModelObjectBinder::modelDataChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::modelDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> &)
|
||||
{
|
||||
const int row = m_data->modelIndex.row();
|
||||
for (auto it = m_data->items.begin(); it != m_data->items.end(); ++it) {
|
||||
if (modelContainsIndex(m_data->m->index(row,it.value().column), topLeft, bottomRight)) {
|
||||
m_data->updateObjectProperty(it.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::selectRow( int row )
|
||||
{
|
||||
Q_ASSERT( m_data->m != nullptr );
|
||||
|
||||
if ( !m_data->m )
|
||||
return;
|
||||
|
||||
m_data->modelIndex = m_data->m->index( row, 0 );
|
||||
m_data->updateAllObjects();
|
||||
}
|
||||
|
||||
int QskModelObjectBinder::currentRow() const
|
||||
{
|
||||
return m_data->modelIndex.row();
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::updateModel()
|
||||
{
|
||||
m_data->updateModelFields();
|
||||
}
|
||||
|
||||
#include "moc_QskModelObjectBinder.cpp"
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef QSKDATACONTROLMAPPER_H
|
||||
#define QSKDATACONTROLMAPPER_H
|
||||
|
||||
#include <QskControl.h>
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
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<int> &);
|
||||
|
||||
private:
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //QSKDATACONTROLMAPPER_H
|
|
@ -12,11 +12,13 @@
|
|||
#include <QskPushButton.h>
|
||||
#include <QskTextInput.h>
|
||||
|
||||
#include <QskDataControlMapper.h>
|
||||
#include <QskModelObjectBinder.h>
|
||||
|
||||
#include <QSqlTableModel>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlTableModel>
|
||||
#include <QSqlRecord>
|
||||
#include <iostream>
|
||||
|
||||
Window::Window( )
|
||||
|
||||
|
@ -36,18 +38,18 @@ Window::Window( )
|
|||
auto txt = new QskTextInput();
|
||||
auto spin = new QskSpinBox();
|
||||
|
||||
auto mapper = new QskDataControlMapper();
|
||||
mapper->setModel(table);
|
||||
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->addMapping(spin, 0,"value");
|
||||
mapper->addMapping(txt, 1, "text");
|
||||
mapper->bindControl(spin, 0,"value");
|
||||
mapper->bindControl(txt, 1, "text");
|
||||
*/
|
||||
mapper->addMapping(spin, 0); // needs USER=true for value in QskBoundedInput
|
||||
mapper->addMapping(txt, 1); // needs USER=true for text in QskTextInput
|
||||
mapper->bindObject(spin, 0); // needs USER=true for value in QskBoundedInput
|
||||
mapper->bindObject(txt, 1); // needs USER=true for text in QskTextInput
|
||||
|
||||
mapper->setCurrentIndex(0);
|
||||
mapper->selectRow(0);
|
||||
|
||||
auto v = new QskLinearBox(Qt::Vertical);
|
||||
v->addSpacer( 0,100 );
|
||||
|
@ -57,9 +59,16 @@ Window::Window( )
|
|||
auto h = new QskLinearBox(Qt::Horizontal);
|
||||
auto prev = new QskPushButton("<",h);
|
||||
auto next = new QskPushButton(">",h);
|
||||
connect(prev,&QskPushButton::clicked,[=]() { mapper->setCurrentIndex( 0 ); });
|
||||
connect(next,&QskPushButton::clicked,[=]() { mapper->setCurrentIndex( 1 ); });
|
||||
connect(prev,&QskPushButton::clicked,[=]() { mapper->selectRow( 0 ); });
|
||||
connect(next,&QskPushButton::clicked,[=]() { mapper->selectRow( 1 ); });
|
||||
v->addItem(h);
|
||||
|
||||
auto save = new QskPushButton("Save",v);
|
||||
connect(save,&QskPushButton::clicked,[=]() {
|
||||
mapper->updateModel();
|
||||
auto r = table->record(mapper->currentRow());
|
||||
qDebug() << r;
|
||||
});
|
||||
v->addSpacer( 0,100 );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue