Adress PR review + model test + QgsExpression -> QString

This commit is contained in:
Alessandro Pasotti 2020-03-19 10:47:36 +01:00 committed by Nyall Dawson
parent 4a33c75bc9
commit a21800cf3e
7 changed files with 138 additions and 111 deletions

View File

@ -41,12 +41,12 @@ the mapping expression is editable.
{
QString originalName;
QgsField field;
QgsExpression expression;
QString expression;
};
QgsFieldMappingModel( const QgsFields &sourceFields = QgsFields(),
const QgsFields &destinationFields = QgsFields(),
const QMap<QString, QgsExpression> &expressions = QMap<QString, QgsExpression>(),
const QMap<QString, QString> &expressions = QMap<QString, QString>(),
QObject *parent = 0 );
%Docstring
Constructs a QgsFieldMappingModel from a set of ``sourceFields``
@ -54,11 +54,6 @@ and ``destinationFields``, initial values for the expressions can be
optionally specified through ``expressions`` which is a map from the original
field name to the corresponding expression. A ``parent`` object
can be also specified.
%End
QgsExpressionContextGenerator *contextGenerator() const;
%Docstring
Returns the context generator with the source fields
%End
bool destinationEditable() const;
@ -86,7 +81,7 @@ Returns a list of source fields
Returns a list of Field objects representing the current status of the model
%End
void appendField( const QgsField &field, const QgsExpression &expression = QgsExpression() );
void appendField( const QgsField &field, const QString &expression = QString() );
%Docstring
Appends a new ``field`` to the model, with an optional ``expression``
%End
@ -109,10 +104,15 @@ Moves up the field at ``index``
void setSourceFields( const QgsFields &sourceFields );
%Docstring
Set source fields to ``sourceFields``
%End
QgsExpressionContextGenerator *contextGenerator() const;
%Docstring
Returns the context generator with the source fields
%End
void setDestinationFields( const QgsFields &destinationFields,
const QMap<QString, QgsExpression> &expressions = QMap<QString, QgsExpression>() );
const QMap<QString, QString> &expressions = QMap<QString, QString>() );
%Docstring
Set destination fields to ``destinationFields``, initial values for the expressions can be
optionally specified through ``expressions`` which is a map from the original
@ -135,6 +135,8 @@ field name to the corresponding expression.
public:
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -26,7 +26,7 @@ for each set of "destination" fields an expression defines how to obtain the val
explicit QgsFieldMappingWidget( QWidget *parent = 0,
const QgsFields &sourceFields = QgsFields(),
const QgsFields &destinationFields = QgsFields(),
const QMap<QString, QgsExpression> &expressions = QMap<QString, QgsExpression>() );
const QMap<QString, QString> &expressions = QMap<QString, QString>() );
%Docstring
Constructs a QgsFieldMappingWidget from a set of ``sourceFields``
and ``destinationFields``, initial values for the expressions can be
@ -66,7 +66,7 @@ Set source fields of the underlying mapping model to ``sourceFields``
%End
void setDestinationFields( const QgsFields &destinationFields,
const QMap<QString, QgsExpression> &expressions = QMap<QString, QgsExpression>() );
const QMap<QString, QString> &expressions = QMap<QString, QString>() );
%Docstring
Set destination fields to ``destinationFields`` in the underlying model,
initial values for the expressions can be optionally specified through
@ -81,7 +81,7 @@ Scroll the fields view to ``index``
public slots:
void appendField( const QgsField &field, const QgsExpression &expression = QgsExpression() );
void appendField( const QgsField &field, const QString &expression = QString() );
%Docstring
Appends a new ``field`` to the model, with an optional ``expression``
%End

View File

@ -48,7 +48,6 @@ from qgis.core import (
QgsVectorLayer,
QgsField,
QgsFields,
QgsExpression,
)
from processing.gui.wrappers import WidgetWrapper, DIALOG_STANDARD, DIALOG_MODELER
@ -110,7 +109,7 @@ class FieldsMappingPanel(BASE, WIDGET):
'type': f.field.type(),
'length': f.field.length(),
'precision': f.field.precision(),
'expression': f.expression.expression(),
'expression': f.expression,
})
return results
@ -126,7 +125,7 @@ class FieldsMappingPanel(BASE, WIDGET):
field_def.get('length', 0),
field_def.get('precision', 0))
try:
expressions[f.name()] = QgsExpression(field_def['expressions'])
expressions[f.name()] = field_def['expressions']
except AttributeError:
pass
destinationFields.append(f)

View File

@ -20,7 +20,7 @@
QgsFieldMappingModel::QgsFieldMappingModel( const QgsFields &sourceFields,
const QgsFields &destinationFields,
const QMap<QString, QgsExpression> &expressions,
const QMap<QString, QString> &expressions,
QObject *parent )
: QAbstractTableModel( parent )
, mSourceFields( sourceFields )
@ -35,29 +35,29 @@ QVariant QgsFieldMappingModel::headerData( int section, Qt::Orientation orientat
{
if ( orientation == Qt::Horizontal )
{
switch ( section )
switch ( static_cast<ColumnDataIndex>( section ) )
{
case static_cast<int>( ColumnDataIndex::SourceExpression ):
case ColumnDataIndex::SourceExpression:
{
return tr( "Source expression" );
}
case static_cast<int>( ColumnDataIndex::DestinationName ):
case ColumnDataIndex::DestinationName:
{
return tr( "Name" );
}
case static_cast<int>( ColumnDataIndex::DestinationType ):
case ColumnDataIndex::DestinationType:
{
return tr( "Type" );
}
case static_cast<int>( ColumnDataIndex::DestinationLength ):
case ColumnDataIndex::DestinationLength:
{
return tr( "Length" );
}
case static_cast<int>( ColumnDataIndex::DestinationPrecision ):
case ColumnDataIndex::DestinationPrecision:
{
return tr( "Precision" );
}
case static_cast<int>( ColumnDataIndex::DestinationConstraints ):
case ColumnDataIndex::DestinationConstraints:
{
return tr( "Constraints" );
}
@ -78,13 +78,15 @@ QgsFields QgsFieldMappingModel::sourceFields() const
int QgsFieldMappingModel::rowCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent );
if ( parent.isValid() )
return 0;
return mMapping.count();
}
int QgsFieldMappingModel::columnCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent );
if ( parent.isValid() )
return 0;
return 6;
}
@ -99,33 +101,31 @@ QVariant QgsFieldMappingModel::data( const QModelIndex &index, int role ) const
if ( role == Qt::DisplayRole || role == Qt::EditRole )
{
switch ( col )
switch ( static_cast<ColumnDataIndex>( col ) )
{
{
case static_cast<int>( ColumnDataIndex::SourceExpression ):
{
return f.expression.expression();
}
case static_cast<int>( ColumnDataIndex::DestinationName ):
{
return f.field.displayName();
}
case static_cast<int>( ColumnDataIndex::DestinationType ):
{
return static_cast<int>( f.field.type() );
}
case static_cast<int>( ColumnDataIndex::DestinationLength ):
{
return f.field.length();
}
case static_cast<int>( ColumnDataIndex::DestinationPrecision ):
{
return f.field.precision();
}
case static_cast<int>( ColumnDataIndex::DestinationConstraints ):
{
return constraints != 0 ? tr( "Constraints active" ) : QString();
}
case ColumnDataIndex::SourceExpression:
{
return f.expression;
}
case ColumnDataIndex::DestinationName:
{
return f.field.displayName();
}
case ColumnDataIndex::DestinationType:
{
return static_cast<int>( f.field.type() );
}
case ColumnDataIndex::DestinationLength:
{
return f.field.length();
}
case ColumnDataIndex::DestinationPrecision:
{
return f.field.precision();
}
case ColumnDataIndex::DestinationConstraints:
{
return constraints != 0 ? tr( "Constraints active" ) : QString();
}
}
}
@ -157,28 +157,6 @@ QVariant QgsFieldMappingModel::data( const QModelIndex &index, int role ) const
return QVariant();
}
QgsExpressionContextGenerator *QgsFieldMappingModel::contextGenerator() const
{
return mExpressionContextGenerator.get();
}
QgsFieldMappingModel::ExpressionContextGenerator::ExpressionContextGenerator( const QgsFields *sourceFields )
{
mSourceFields = sourceFields;
}
QgsExpressionContext QgsFieldMappingModel::ExpressionContextGenerator::createExpressionContext() const
{
QgsExpressionContext ctx;
ctx.appendScope( QgsExpressionContextUtils::globalScope() );
ctx.setFields( *mSourceFields );
QgsFeature feature { *mSourceFields };
feature.setValid( true );
ctx.setFeature( feature );
return ctx;
}
Qt::ItemFlags QgsFieldMappingModel::flags( const QModelIndex &index ) const
{
if ( index.isValid() &&
@ -199,25 +177,25 @@ bool QgsFieldMappingModel::setData( const QModelIndex &index, const QVariant &va
if ( role == Qt::EditRole )
{
Field &f = mMapping[index.row()];
switch ( index.column() )
switch ( static_cast<ColumnDataIndex>( index.column() ) )
{
case static_cast<int>( ColumnDataIndex::SourceExpression ):
case ColumnDataIndex::SourceExpression:
{
const QgsExpression exp { value.toString() };
f.expression = exp;
break;
}
case static_cast<int>( ColumnDataIndex::DestinationName ):
case ColumnDataIndex::DestinationName:
{
f.field.setName( value.toString() );
break;
}
case static_cast<int>( ColumnDataIndex::DestinationType ):
case ColumnDataIndex::DestinationType:
{
f.field.setType( static_cast<QVariant::Type>( value.toInt( ) ) );
break;
}
case static_cast<int>( ColumnDataIndex::DestinationLength ):
case ColumnDataIndex::DestinationLength:
{
bool ok;
const int length { value.toInt( &ok ) };
@ -225,7 +203,7 @@ bool QgsFieldMappingModel::setData( const QModelIndex &index, const QVariant &va
f.field.setLength( length );
break;
}
case static_cast<int>( ColumnDataIndex::DestinationPrecision ):
case ColumnDataIndex::DestinationPrecision:
{
bool ok;
const int precision { value.toInt( &ok ) };
@ -233,11 +211,19 @@ bool QgsFieldMappingModel::setData( const QModelIndex &index, const QVariant &va
f.field.setPrecision( precision );
break;
}
case ColumnDataIndex::DestinationConstraints:
{
// Not editable: do nothing
}
}
emit dataChanged( index, index );
}
return true;
}
else
{
return false;
}
return true;
}
QgsFieldConstraints::Constraints QgsFieldMappingModel::fieldConstraints( const QgsField &field ) const
@ -283,11 +269,11 @@ bool QgsFieldMappingModel::moveUpOrDown( const QModelIndex &index, bool up )
return true;
}
QString QgsFieldMappingModel::bestMatchforField( const QgsFieldMappingModel::Field &f, QStringList &excludedFieldNames )
QString QgsFieldMappingModel::findExpressionForDestinationField( const QgsFieldMappingModel::Field &f, QStringList &excludedFieldNames )
{
// Search for fields in the source
// 1. match by name
for ( const auto &sf : qgis::as_const( mSourceFields ) )
for ( const QgsField &sf : qgis::as_const( mSourceFields ) )
{
if ( sf.name() == f.field.name() )
{
@ -295,7 +281,7 @@ QString QgsFieldMappingModel::bestMatchforField( const QgsFieldMappingModel::Fie
}
}
// 2. match by type
for ( const auto &sf : qgis::as_const( mSourceFields ) )
for ( const QgsField &sf : qgis::as_const( mSourceFields ) )
{
if ( excludedFieldNames.contains( sf.name() ) || sf.type() != f.field.type() )
continue;
@ -312,26 +298,30 @@ void QgsFieldMappingModel::setSourceFields( const QgsFields &sourceFields )
beginResetModel();
for ( const Field &f : qgis::as_const( mMapping ) )
{
if ( f.expression.isField() )
if ( QgsExpression( f.expression ).isField() )
{
usedFields.push_back( f.expression.expression().mid( 1, f.expression.expression().length() - 2 ) );
usedFields.push_back( f.expression.mid( 1, f.expression.length() - 2 ) );
}
}
// not const on purpose
for ( Field &f : mMapping )
for ( auto it = mMapping.begin(); it != mMapping.end(); ++it )
{
if ( f.expression.expression().isEmpty() )
if ( it->expression.isEmpty() )
{
const QString expression { bestMatchforField( f, usedFields ) };
const QString expression { findExpressionForDestinationField( *it, usedFields ) };
if ( ! expression.isEmpty() )
f.expression = expression;
it->expression = expression;
}
}
endResetModel();
}
QgsExpressionContextGenerator *QgsFieldMappingModel::contextGenerator() const
{
return mExpressionContextGenerator.get();
}
void QgsFieldMappingModel::setDestinationFields( const QgsFields &destinationFields,
const QMap<QString, QgsExpression> &expressions )
const QMap<QString, QString> &expressions )
{
beginResetModel();
mMapping.clear();
@ -345,16 +335,17 @@ void QgsFieldMappingModel::setDestinationFields( const QgsFields &destinationFie
if ( expressions.contains( f.field.name() ) )
{
f.expression = expressions.value( f.field.name() );
const QgsExpression exp { f.expression };
// if it's source field
if ( f.expression.isField() &&
mSourceFields.names().contains( f.expression.referencedColumns().toList().first() ) )
if ( exp.isField() &&
mSourceFields.names().contains( exp.referencedColumns().toList().first() ) )
{
usedFields.push_back( f.expression.referencedColumns().toList().first() );
usedFields.push_back( exp.referencedColumns().toList().first() );
}
}
else
{
const QString expression { bestMatchforField( f, usedFields ) };
const QString expression { findExpressionForDestinationField( f, usedFields ) };
if ( ! expression.isEmpty() )
f.expression = expression;
}
@ -395,7 +386,7 @@ QList<QgsFieldMappingModel::Field> QgsFieldMappingModel::mapping() const
return mMapping;
}
void QgsFieldMappingModel::appendField( const QgsField &field, const QgsExpression &expression )
void QgsFieldMappingModel::appendField( const QgsField &field, const QString &expression )
{
const int lastRow { rowCount( QModelIndex( ) ) };
beginInsertRows( QModelIndex(), lastRow, lastRow );
@ -432,3 +423,18 @@ bool QgsFieldMappingModel::moveDown( const QModelIndex &index )
return moveUpOrDown( index, false );
}
QgsFieldMappingModel::ExpressionContextGenerator::ExpressionContextGenerator( const QgsFields *sourceFields )
: mSourceFields( sourceFields )
{
}
QgsExpressionContext QgsFieldMappingModel::ExpressionContextGenerator::createExpressionContext() const
{
QgsExpressionContext ctx;
ctx.appendScope( QgsExpressionContextUtils::globalScope() );
ctx.setFields( *mSourceFields );
QgsFeature feature { *mSourceFields };
feature.setValid( true );
ctx.setFeature( feature );
return ctx;
}

View File

@ -20,7 +20,6 @@
#include <QStyledItemDelegate>
#include "qgsfields.h"
#include "qgsexpression.h"
#include "qgsexpressioncontextgenerator.h"
#include "qgsfieldconstraints.h"
#include "qgis_gui.h"
@ -68,7 +67,7 @@ class GUI_EXPORT QgsFieldMappingModel: public QAbstractTableModel
//! The field in its current status (it might have been renamed)
QgsField field;
//! The expression for the mapped field from the source fields
QgsExpression expression;
QString expression;
};
/**
@ -80,12 +79,9 @@ class GUI_EXPORT QgsFieldMappingModel: public QAbstractTableModel
*/
QgsFieldMappingModel( const QgsFields &sourceFields = QgsFields(),
const QgsFields &destinationFields = QgsFields(),
const QMap<QString, QgsExpression> &expressions = QMap<QString, QgsExpression>(),
const QMap<QString, QString> &expressions = QMap<QString, QString>(),
QObject *parent = nullptr );
//! Returns the context generator with the source fields
QgsExpressionContextGenerator *contextGenerator() const;
//! Returns TRUE if the destination fields are editable
bool destinationEditable() const;
@ -102,7 +98,7 @@ class GUI_EXPORT QgsFieldMappingModel: public QAbstractTableModel
QList<QgsFieldMappingModel::Field> mapping() const;
//! Appends a new \a field to the model, with an optional \a expression
void appendField( const QgsField &field, const QgsExpression &expression = QgsExpression() );
void appendField( const QgsField &field, const QString &expression = QString() );
//! Removes the field at \a index from the model, returns TRUE on success
bool removeField( const QModelIndex &index );
@ -116,13 +112,16 @@ class GUI_EXPORT QgsFieldMappingModel: public QAbstractTableModel
//! Set source fields to \a sourceFields
void setSourceFields( const QgsFields &sourceFields );
//! Returns the context generator with the source fields
QgsExpressionContextGenerator *contextGenerator() const;
/**
* Set destination fields to \a destinationFields, initial values for the expressions can be
* optionally specified through \a expressions which is a map from the original
* field name to the corresponding expression.
*/
void setDestinationFields( const QgsFields &destinationFields,
const QMap<QString, QgsExpression> &expressions = QMap<QString, QgsExpression>() );
const QMap<QString, QString> &expressions = QMap<QString, QString>() );
// QAbstractItemModel interface
int rowCount( const QModelIndex &parent = QModelIndex() ) const override;
@ -150,16 +149,27 @@ class GUI_EXPORT QgsFieldMappingModel: public QAbstractTableModel
};
QgsFieldConstraints::Constraints fieldConstraints( const QgsField &field ) const;
bool moveUpOrDown( const QModelIndex &index, bool up = true );
QString bestMatchforField( const QgsFieldMappingModel::Field &field, QStringList &excludedFieldNames );
/**
* Try to find the best expression for a destination \a field by searching in the
* source fields for fields with:
* - the same name
* - the same type
* Returns an expression containing a reference to the field that matches first.
*/
QString findExpressionForDestinationField( const QgsFieldMappingModel::Field &field, QStringList &excludedFieldNames );
QList<Field> mMapping;
bool mDestinationEditable = false;
QgsFields mSourceFields;
std::unique_ptr<ExpressionContextGenerator> mExpressionContextGenerator;
};
#endif // QGSFIELDMAPPINGMODEL_H

View File

@ -16,17 +16,27 @@
#include "qgsfieldmappingwidget.h"
#include "qgsfieldexpressionwidget.h"
#include "qgsexpression.h"
#ifdef ENABLE_MODELTEST
#include "modeltest.h"
#endif
QgsFieldMappingWidget::QgsFieldMappingWidget( QWidget *parent,
const QgsFields &sourceFields,
const QgsFields &destinationFields,
const QMap<QString, QgsExpression> &expressions )
const QMap<QString, QString> &expressions )
: QWidget( parent )
{
setupUi( this );
mModel = new QgsFieldMappingModel( sourceFields, destinationFields, expressions, this );
#ifdef ENABLE_MODELTEST
new ModelTest( mModel, this );
#endif
mTableView->setModel( mModel );
mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::SourceExpression ), new ExpressionDelegate( mTableView ) );
mTableView->setItemDelegateForColumn( static_cast<int>( QgsFieldMappingModel::ColumnDataIndex::DestinationType ), new TypeDelegate( mTableView ) );
@ -67,7 +77,7 @@ void QgsFieldMappingWidget::setSourceFields( const QgsFields &sourceFields )
model()->setSourceFields( sourceFields );
}
void QgsFieldMappingWidget::setDestinationFields( const QgsFields &destinationFields, const QMap<QString, QgsExpression> &expressions )
void QgsFieldMappingWidget::setDestinationFields( const QgsFields &destinationFields, const QMap<QString, QString> &expressions )
{
model()->setDestinationFields( destinationFields, expressions );
}
@ -77,7 +87,7 @@ void QgsFieldMappingWidget::scrollTo( const QModelIndex &index ) const
mTableView->scrollTo( index );
}
void QgsFieldMappingWidget::appendField( const QgsField &field, const QgsExpression &expression )
void QgsFieldMappingWidget::appendField( const QgsField &field, const QString &expression )
{
model()->appendField( field, expression );
}

View File

@ -47,7 +47,7 @@ class GUI_EXPORT QgsFieldMappingWidget : public QWidget, private Ui::QgsFieldMap
explicit QgsFieldMappingWidget( QWidget *parent = nullptr,
const QgsFields &sourceFields = QgsFields(),
const QgsFields &destinationFields = QgsFields(),
const QMap<QString, QgsExpression> &expressions = QMap<QString, QgsExpression>() );
const QMap<QString, QString> &expressions = QMap<QString, QString>() );
//! Sets the destination fields editable state to \a editable
void setDestinationEditable( bool editable );
@ -74,7 +74,7 @@ class GUI_EXPORT QgsFieldMappingWidget : public QWidget, private Ui::QgsFieldMap
* corresponding expression.
*/
void setDestinationFields( const QgsFields &destinationFields,
const QMap<QString, QgsExpression> &expressions = QMap<QString, QgsExpression>() );
const QMap<QString, QString> &expressions = QMap<QString, QString>() );
/**
* Scroll the fields view to \a index
@ -84,7 +84,7 @@ class GUI_EXPORT QgsFieldMappingWidget : public QWidget, private Ui::QgsFieldMap
public slots:
//! Appends a new \a field to the model, with an optional \a expression
void appendField( const QgsField &field, const QgsExpression &expression = QgsExpression() );
void appendField( const QgsField &field, const QString &expression = QString() );
//! Removes the currently selected field from the model
bool removeSelectedFields( );