QskModelObjectBinder modified and moved to src/controls
This commit is contained in:
parent
6ea2126638
commit
59afa42c84
|
@ -3,6 +3,5 @@
|
|||
# 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)
|
||||
qsk_add_example(models Window.h Window.cpp main.cpp)
|
||||
target_link_libraries(models PRIVATE Qt::Sql)
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
//
|
||||
// Created by aldo on 27/01/21.
|
||||
//
|
||||
|
||||
#include "QskModelObjectBinder.h"
|
||||
#include <QMetaProperty>
|
||||
#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;
|
||||
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<QObject*,Item> items;
|
||||
QAbstractItemModel* m;
|
||||
QPersistentModelIndex currentRowIndex;
|
||||
};
|
||||
|
||||
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->currentRowIndex=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->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"
|
|
@ -1,32 +0,0 @@
|
|||
#ifndef QSKDATACONTROLMAPPER_H
|
||||
#define QSKDATACONTROLMAPPER_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
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "Window.h"
|
||||
|
||||
#include <QskDialogButtonBox.h>
|
||||
#include <QskLinearBox.h>
|
||||
#include <QskPushButton.h>
|
||||
#include <QskTextInput.h>
|
||||
|
@ -18,18 +17,18 @@
|
|||
#include <QSqlTableModel>
|
||||
#include <QSqlRecord>
|
||||
|
||||
Window::Window( )
|
||||
|
||||
Window::Window()
|
||||
{
|
||||
auto db = QSqlDatabase::addDatabase("QSQLITE");
|
||||
db.setDatabaseName(":memory:");
|
||||
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);
|
||||
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();
|
||||
|
||||
|
@ -37,46 +36,41 @@ Window::Window( )
|
|||
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
|
||||
mapper->bindModel( table );
|
||||
|
||||
mapper->bindObject( spin, 0 );
|
||||
mapper->bindObject( txt, 1 );
|
||||
|
||||
// this loads the record from the first row and updates the controls data.
|
||||
mapper->selectRow(0);
|
||||
mapper->setCurrentRow(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 ); });
|
||||
connect(prev,&QskPushButton::clicked,[ = ]() { mapper->setCurrentRow( 0 ); });
|
||||
connect(next,&QskPushButton::clicked,[ = ]() { mapper->setCurrentRow( 1 ); });
|
||||
v->addItem(h);
|
||||
|
||||
auto save = new QskPushButton("Save Data to Model",v);
|
||||
connect(save,&QskPushButton::clicked,[=]() {
|
||||
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());
|
||||
auto r = table->record(mapper->currentRow() );
|
||||
qDebug() << r;
|
||||
});
|
||||
auto set0 = new QskPushButton("Set Model field to 0",v);
|
||||
connect(set0,&QskPushButton::clicked,[=]() {
|
||||
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"
|
||||
|
|
|
@ -5,18 +5,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QskDialog.h>
|
||||
#include <QskWindow.h>
|
||||
|
||||
class QskDialogButtonBox;
|
||||
class QskLinearBox;
|
||||
|
||||
class Window : public QskWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Window();
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -226,6 +226,7 @@ list(APPEND HEADERS
|
|||
controls/QskListViewSkinlet.h
|
||||
controls/QskMenu.h
|
||||
controls/QskMenuSkinlet.h
|
||||
controls/QskModelObjectBinder.h
|
||||
controls/QskObjectTree.h
|
||||
controls/QskPageIndicator.h
|
||||
controls/QskPageIndicatorSkinlet.h
|
||||
|
@ -332,6 +333,7 @@ list(APPEND SOURCES
|
|||
controls/QskListViewSkinlet.cpp
|
||||
controls/QskMenuSkinlet.cpp
|
||||
controls/QskMenu.cpp
|
||||
controls/QskModelObjectBinder.cpp
|
||||
controls/QskObjectTree.cpp
|
||||
controls/QskPageIndicator.cpp
|
||||
controls/QskPageIndicatorSkinlet.cpp
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskModelObjectBinder.h"
|
||||
|
||||
#include <qmetaobject.h>
|
||||
#include <qabstractitemmodel.h>
|
||||
#include <qpointer.h>
|
||||
#include <qassert.h>
|
||||
|
||||
static inline QMetaProperty qskMetaProperty(
|
||||
const QMetaObject* metaObject, const QByteArray& name )
|
||||
{
|
||||
if ( name.isEmpty() )
|
||||
return metaObject->userProperty();
|
||||
|
||||
const auto idx = metaObject->indexOfProperty( name );
|
||||
return ( idx >= 0 ) ? metaObject->property( idx ) : QMetaProperty();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Binding
|
||||
{
|
||||
Binding( int column, const QMetaProperty& property )
|
||||
: property( property )
|
||||
, column( column )
|
||||
{
|
||||
}
|
||||
|
||||
QMetaProperty property;
|
||||
int column;
|
||||
};
|
||||
}
|
||||
|
||||
class QskModelObjectBinder::PrivateData
|
||||
{
|
||||
public:
|
||||
void updateProperties(const QModelIndex& topLeft,
|
||||
const QModelIndex& bottomRight, const QVector< int >& roles )
|
||||
{
|
||||
if ( !roles.contains( Qt::EditRole ) )
|
||||
return;
|
||||
|
||||
const int row = currentRowIndex.row();
|
||||
|
||||
if ( topLeft.row() <= row && row <= bottomRight.row() )
|
||||
{
|
||||
for ( auto it = bindings.begin(); it != bindings.end(); ++it )
|
||||
{
|
||||
const int col = it.value().column;
|
||||
|
||||
if( topLeft.column() <= col && col <= bottomRight.column() )
|
||||
updateObjectProperty( it.key(), it.value() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateObjectProperty( QObject* object, const Binding& binding )
|
||||
{
|
||||
const auto index = model->index( currentRowIndex.row(), binding.column );
|
||||
binding.property.write( object, index.data( Qt::EditRole ) );
|
||||
}
|
||||
|
||||
public:
|
||||
QMap< QObject*, Binding > bindings;
|
||||
QPointer< QAbstractItemModel > model;
|
||||
QPersistentModelIndex currentRowIndex;
|
||||
};
|
||||
|
||||
QskModelObjectBinder::QskModelObjectBinder( QObject* parent )
|
||||
: QObject( parent )
|
||||
, m_data( new PrivateData() )
|
||||
{
|
||||
}
|
||||
|
||||
QskModelObjectBinder::QskModelObjectBinder( QAbstractItemModel* model, QObject* parent )
|
||||
: QskModelObjectBinder( parent )
|
||||
{
|
||||
bindModel( model );
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::bindObject(
|
||||
QObject* object, int column, const QByteArray& propertyName )
|
||||
{
|
||||
if( object == nullptr )
|
||||
return;
|
||||
|
||||
// does not work with dynamic properties ...
|
||||
|
||||
const auto metaProperty = qskMetaProperty( object->metaObject(), propertyName );
|
||||
Q_ASSERT( metaProperty.isValid() );
|
||||
|
||||
if ( metaProperty.isValid() )
|
||||
{
|
||||
m_data->bindings.insert( object, { column, metaProperty } );
|
||||
connect( object, &QObject::destroyed,
|
||||
this, &QskModelObjectBinder::unbindObject );
|
||||
}
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::unbindObject( QObject* object )
|
||||
{
|
||||
auto it = m_data->bindings.find( object );
|
||||
if ( it != m_data->bindings.end() )
|
||||
{
|
||||
disconnect( object, &QObject::destroyed,
|
||||
this, &QskModelObjectBinder::unbindObject );
|
||||
|
||||
m_data->bindings.erase( it );
|
||||
}
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::bindModel( QAbstractItemModel* model )
|
||||
{
|
||||
if ( m_data->model )
|
||||
disconnect( m_data->model, nullptr, this, nullptr );
|
||||
|
||||
m_data->model = model;
|
||||
m_data->currentRowIndex = QModelIndex();
|
||||
|
||||
if( model )
|
||||
{
|
||||
auto updateProperties = [this](
|
||||
const QModelIndex& topLeft, const QModelIndex& bottomRight,
|
||||
const QVector< int >& roles )
|
||||
{
|
||||
m_data->updateProperties( topLeft, bottomRight, roles );
|
||||
};
|
||||
|
||||
connect( m_data->model, &QAbstractItemModel::dataChanged,
|
||||
this, updateProperties );
|
||||
}
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::setCurrentRow( int row )
|
||||
{
|
||||
Q_ASSERT( m_data->model != nullptr );
|
||||
|
||||
if ( m_data->model == nullptr )
|
||||
return;
|
||||
|
||||
m_data->currentRowIndex = m_data->model->index( row, 0 );
|
||||
|
||||
for ( auto it = m_data->bindings.begin(); it != m_data->bindings.end(); ++it )
|
||||
m_data->updateObjectProperty( it.key(), it.value() );
|
||||
}
|
||||
|
||||
int QskModelObjectBinder::currentRow() const
|
||||
{
|
||||
return m_data->currentRowIndex.row();
|
||||
}
|
||||
|
||||
void QskModelObjectBinder::updateModel()
|
||||
{
|
||||
if ( auto model = m_data->model )
|
||||
{
|
||||
const auto& bindings = m_data->bindings;
|
||||
|
||||
for ( auto it = bindings.begin(); it != bindings.end(); ++it )
|
||||
{
|
||||
const auto value = it.value().property.read( it.key() );
|
||||
|
||||
const auto row = m_data->currentRowIndex.row();
|
||||
const auto index = model->index( row, it.value().column );
|
||||
|
||||
model->setData( index, value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_QskModelObjectBinder.cpp"
|
|
@ -0,0 +1,42 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_MODEL_OBJECT_BINDER_H
|
||||
#define QSK_MODEL_OBJECT_BINDER_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
|
||||
#include <qobject.h>
|
||||
#include <qbytearray.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QAbstractItemModel;
|
||||
|
||||
class QSK_EXPORT QskModelObjectBinder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QskModelObjectBinder( QObject* parent = nullptr );
|
||||
QskModelObjectBinder( QAbstractItemModel*, QObject* parent = nullptr );
|
||||
|
||||
void bindObject( QObject*, int column,
|
||||
const QByteArray& propertyName = QByteArray() );
|
||||
|
||||
void unbindObject( QObject* );
|
||||
void bindModel( QAbstractItemModel* );
|
||||
|
||||
void setCurrentRow( int row );
|
||||
[[nodiscard]] int currentRow() const;
|
||||
|
||||
void updateModel();
|
||||
|
||||
private:
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue