bugs fixed, QskModelObjectBinder improved

This commit is contained in:
Uwe Rathmann 2024-02-26 10:52:52 +01:00
parent 1a5dc6d358
commit 8faa6a8cad
4 changed files with 178 additions and 77 deletions

View File

@ -17,60 +17,77 @@
#include <QSqlTableModel>
#include <QSqlRecord>
Window::Window()
namespace
{
auto db = QSqlDatabase::addDatabase( "QSQLITE" );
class Model : public QSqlTableModel
{
public:
Model( QObject *parent = nullptr )
: QSqlTableModel( parent, QSqlDatabase::addDatabase( "QSQLITE" ) )
{
auto db = database();
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');" );
QSqlQuery query( db );
query.exec( "create table test(id integer,value text);" );
query.exec( "insert into test (id,value) values (1,'HELLO');" );
query.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 );
mapper->bindObject( spin, 0 );
mapper->bindObject( txt, 1 );
// this loads the record from the first row and updates the controls data.
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->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,[ = ]() {
// 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 );
setTable( "test" );
select();
}
#include "moc_Window.cpp"
void setValue( int row, const QVariant &value )
{
setData( index( row, 0 ), value );
}
};
}
Window::Window()
{
auto model = new Model( this );
auto textInput = new QskTextInput();
auto spinBox = new QskSpinBox();
auto mapper = new QskModelObjectBinder( model, this );
mapper->bindObject( spinBox, 0 );
mapper->bindObject( textInput, 1 );
auto vBox = new QskLinearBox( Qt::Vertical );
vBox->addSpacer( 0, 100 );
vBox->addItem( spinBox );
vBox->addItem( textInput );
addItem( vBox );
auto hBox = new QskLinearBox( Qt::Horizontal );
{
auto prev = new QskPushButton( "<", hBox );
auto next = new QskPushButton( ">", hBox);
connect(prev, &QskPushButton::clicked,
[ mapper ]() { mapper->setCurrentRow( 0 ); });
connect( next, &QskPushButton::clicked,
[ mapper ]() { mapper->setCurrentRow( 1 ); });
vBox->addItem( hBox );
}
// update the current record with the data from the SpinBox and TextInput
auto save = new QskPushButton( "Save Data to Model", vBox );
connect( save, &QskPushButton::clicked,
[=]() { mapper->submit(); qDebug() << model->record( mapper->currentRow() ); });
// trigger the binder and update the spinbox
auto set0 = new QskPushButton( "Set Model field to 0", vBox );
connect( set0, &QskPushButton::clicked,
[=]() { model->setValue( mapper->currentRow(), 0 ); } );
vBox->addSpacer( 0,100 );
}

View File

@ -9,8 +9,6 @@
class Window : public QskWindow
{
Q_OBJECT
public:
Window();
};

View File

@ -19,6 +19,21 @@ static inline QMetaProperty qskMetaProperty(
return ( idx >= 0 ) ? metaObject->property( idx ) : QMetaProperty();
}
static void qskEnableConnections( QObject* object,
QskModelObjectBinder* binder, bool on )
{
if ( on )
{
QObject::connect( object, &QObject::destroyed,
binder, &QskModelObjectBinder::unbindObject );
}
else
{
QObject::disconnect( object, &QObject::destroyed,
binder, &QskModelObjectBinder::unbindObject );
}
}
namespace
{
struct Binding
@ -37,17 +52,29 @@ namespace
class QskModelObjectBinder::PrivateData
{
public:
void updateProperties()
{
if ( model && currentRowIndex.isValid() )
{
for ( auto it = bindings.constBegin(); it != bindings.constEnd(); ++it )
updateObjectProperty( it.key(), it.value() );
}
}
void updateProperties( const QModelIndex& topLeft,
const QModelIndex& bottomRight, const QVector< int >& roles )
{
if ( !roles.contains( Qt::EditRole ) )
if ( !( model && currentRowIndex.isValid() ) )
return;
if ( !roles.isEmpty() && 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 )
for ( auto it = bindings.constBegin(); it != bindings.constEnd(); ++it )
{
const int col = it.value().column;
@ -78,7 +105,7 @@ QskModelObjectBinder::QskModelObjectBinder( QObject* parent )
QskModelObjectBinder::QskModelObjectBinder( QAbstractItemModel* model, QObject* parent )
: QskModelObjectBinder( parent )
{
bindModel( model );
setModel( model );
}
void QskModelObjectBinder::bindObject(
@ -94,9 +121,13 @@ void QskModelObjectBinder::bindObject(
if ( metaProperty.isValid() )
{
m_data->bindings.insert( object, { column, metaProperty } );
connect( object, &QObject::destroyed,
this, &QskModelObjectBinder::unbindObject );
const Binding binding = { column, metaProperty };
m_data->bindings.insert( object, binding );
qskEnableConnections( object, this, true );
if ( m_data->model && m_data->currentRowIndex.isValid() )
m_data->updateObjectProperty( object, binding );
}
}
@ -105,20 +136,37 @@ 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 );
qskEnableConnections( object, this, false );
m_data->bindings.erase( it );
}
}
void QskModelObjectBinder::bindModel( QAbstractItemModel* model )
void QskModelObjectBinder::clearBindings()
{
auto& bindings = m_data->bindings;
for ( auto it = bindings.constBegin(); it != bindings.constEnd(); ++it )
qskEnableConnections( it.key(), this, false );
bindings.clear();
}
void QskModelObjectBinder::setModel( QAbstractItemModel* model )
{
if ( model == m_data->model )
return;
if ( m_data->model )
disconnect( m_data->model, nullptr, this, nullptr );
{
disconnect( m_data->model, &QAbstractItemModel::dataChanged, this, nullptr );
m_data->model = nullptr;
m_data->currentRowIndex = QModelIndex();
clearBindings();
}
m_data->model = model;
m_data->currentRowIndex = QModelIndex();
if( model )
{
@ -131,20 +179,40 @@ void QskModelObjectBinder::bindModel( QAbstractItemModel* model )
connect( m_data->model, &QAbstractItemModel::dataChanged,
this, updateProperties );
connect( m_data->model, &QObject::destroyed,
this, &QskModelObjectBinder::clearBindings );
setCurrentRow( 0 );
}
}
const QAbstractItemModel* QskModelObjectBinder::model() const
{
return m_data->model;
}
QAbstractItemModel* QskModelObjectBinder::model()
{
return m_data->model;
}
void QskModelObjectBinder::setCurrentRow( int row )
{
Q_ASSERT( m_data->model != nullptr );
auto model = m_data->model.data();
auto& bindings = m_data->bindings;
if ( m_data->model == nullptr )
return;
Q_ASSERT( model != nullptr );
m_data->currentRowIndex = m_data->model->index( row, 0 );
if ( model && row >= 0 && row < model->rowCount() )
{
m_data->currentRowIndex = model->index( row, 0 );
for ( auto it = m_data->bindings.begin(); it != m_data->bindings.end(); ++it )
for ( auto it = bindings.constBegin(); it != bindings.constEnd(); ++it )
m_data->updateObjectProperty( it.key(), it.value() );
Q_EMIT currentRowChanged( row );
}
}
int QskModelObjectBinder::currentRow() const
@ -152,7 +220,7 @@ int QskModelObjectBinder::currentRow() const
return m_data->currentRowIndex.row();
}
void QskModelObjectBinder::updateModel()
void QskModelObjectBinder::submit()
{
if ( auto model = m_data->model )
{
@ -170,4 +238,9 @@ void QskModelObjectBinder::updateModel()
}
}
void QskModelObjectBinder::revert()
{
m_data->updateProperties();
}
#include "moc_QskModelObjectBinder.cpp"

View File

@ -19,20 +19,33 @@ class QSK_EXPORT QskModelObjectBinder : public QObject
{
Q_OBJECT
Q_PROPERTY( int currentRow READ currentRow
WRITE setCurrentRow NOTIFY currentRowChanged )
public:
QskModelObjectBinder( QObject* parent = nullptr );
QskModelObjectBinder( QAbstractItemModel*, QObject* parent = nullptr );
void setModel( QAbstractItemModel* );
const QAbstractItemModel* model() const;
QAbstractItemModel* model();
void setCurrentRow( int row );
int currentRow() const;
void bindObject( QObject*, int column,
const QByteArray& propertyName = QByteArray() );
void unbindObject( QObject* );
void bindModel( QAbstractItemModel* );
void setCurrentRow( int row );
[[nodiscard]] int currentRow() const;
Q_SIGNALS:
void currentRowChanged( int );
void updateModel();
public Q_SLOTS:
void submit();
void revert();
void clearBindings();
private:
class PrivateData;