28#include <QStandardItemModel>
33#include "moc_qgsprocessingaggregatewidgets.cpp"
35using namespace Qt::StringLiterals;
42 : QAbstractTableModel( parent )
50 if ( role == Qt::DisplayRole )
52 switch ( orientation )
60 return tr(
"Source Expression" );
64 return tr(
"Aggregate Function" );
68 return tr(
"Delimiter" );
80 return tr(
"Length" );
84 return tr(
"Precision" );
100 return mSourceFields;
105 if ( parent.isValid() )
107 return mMapping.
count();
112 if ( parent.isValid() )
119 if ( index.isValid() )
122 const Aggregate &agg { mMapping.at( index.row() ) };
126 case Qt::DisplayRole:
169 if ( index.isValid() )
171 return Qt::ItemFlags( Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled );
173 return Qt::ItemFlags();
178 if ( index.isValid() )
180 if ( role == Qt::EditRole )
208 setFieldTypeFromName( f.
field, value.toString() );
214 const int length { value.toInt( &ok ) };
222 const int precision { value.toInt( &ok ) };
228 emit dataChanged( index, index );
239bool QgsAggregateMappingModel::moveUpOrDown(
const QModelIndex &index,
bool up )
241 if ( !index.isValid() && index.model() ==
this )
245 const int row { up ? index.row() - 1 : index.row() };
247 if ( row < 0 || row + 1 >=
rowCount( QModelIndex() ) )
251 beginMoveRows( QModelIndex(), row, row, QModelIndex(), row + 2 );
252 mMapping.swapItemsAt( row, row + 1 );
257QString QgsAggregateMappingModel::qgsFieldToTypeName(
const QgsField &field )
260 for (
const QgsVectorDataProvider::NativeType &type : types )
262 if ( type.mType == field.
type() && type.mSubType == field.
subType() )
264 return type.mTypeName;
270void QgsAggregateMappingModel::setFieldTypeFromName(
QgsField &field,
const QString &name )
273 for (
const QgsVectorDataProvider::NativeType &type : types )
275 if ( type.mTypeName == name )
288 if ( mExpressionContextGenerator )
289 mExpressionContextGenerator->setSourceFields( mSourceFields );
303 else if ( f.type() == QMetaType::Type::QString || ( f.type() == QMetaType::Type::QVariantList && f.subType() == QMetaType::Type::QString ) )
308 mMapping.push_back( aggregate );
315 return mExpressionContextGenerator.get();
320 mExpressionContextGenerator->setBaseExpressionContextGenerator( generator );
332 for (
auto &agg : mMapping )
334 agg.field.setTypeName( qgsFieldToTypeName( agg.field ) );
341 const int lastRow {
rowCount( QModelIndex() ) };
342 beginInsertRows( QModelIndex(), lastRow, lastRow );
349 mMapping.push_back( agg );
355 if ( index.isValid() && index.model() ==
this && index.row() <
rowCount( QModelIndex() ) )
357 beginRemoveRows( QModelIndex(), index.row(), index.row() );
358 mMapping.removeAt( index.row() );
370 return moveUpOrDown( index );
375 return moveUpOrDown( index,
false );
386 QVBoxLayout *verticalLayout =
new QVBoxLayout();
387 verticalLayout->setContentsMargins( 0, 0, 0, 0 );
388 mTableView =
new QTableView();
389 verticalLayout->addWidget( mTableView );
390 setLayout( verticalLayout );
393 mTableView->setModel( mModel );
399 connect( mModel, &QgsAggregateMappingModel::rowsInserted,
this, [
this] { updateColumns(); } );
400 connect( mModel, &QgsAggregateMappingModel::modelReset,
this, [
this] { updateColumns(); } );
409 return qobject_cast<QgsAggregateMappingModel *>( mModel );
424 return mTableView->selectionModel();
434 mSourceLayer = layer;
444 mTableView->scrollTo( index );
459 if ( !mTableView->selectionModel()->hasSelection() )
462 std::list<int> rowsToRemove { selectedRows() };
463 rowsToRemove.reverse();
464 for (
const int row : rowsToRemove )
466 if ( !
model()->removeField(
model()->index( row, 0, QModelIndex() ) ) )
476 if ( !mTableView->selectionModel()->hasSelection() )
479 const std::list<int> rowsToMoveUp { selectedRows() };
480 for (
const int row : rowsToMoveUp )
482 if ( !
model()->moveUp(
model()->index( row, 0, QModelIndex() ) ) )
492 if ( !mTableView->selectionModel()->hasSelection() )
495 std::list<int> rowsToMoveDown { selectedRows() };
496 rowsToMoveDown.reverse();
497 for (
const int row : rowsToMoveDown )
499 if ( !
model()->moveDown(
model()->index( row, 0, QModelIndex() ) ) )
507void QgsAggregateMappingWidget::updateColumns()
509 for (
int i = 0; i < mModel->rowCount(); ++i )
516 for (
int i = 0; i < mModel->columnCount(); ++i )
518 mTableView->resizeColumnToContents( i );
522std::list<int> QgsAggregateMappingWidget::selectedRows()
525 if ( mTableView->selectionModel()->hasSelection() )
527 const QModelIndexList constSelection { mTableView->selectionModel()->selectedIndexes() };
528 for (
const QModelIndex &index : constSelection )
530 rows.push_back( index.row() );
545QgsAggregateMappingDelegate::QgsAggregateMappingDelegate( QObject *parent )
546 : QStyledItemDelegate( parent )
550QWidget *QgsAggregateMappingDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex & )
const
553 QComboBox *editor =
new QComboBox( parent );
555 const QStringList aggregateList { aggregates() };
557 for (
const QString &aggregate : aggregateList )
559 editor->addItem( aggregate );
560 editor->setItemData( i, aggregate, Qt::UserRole );
564 connect( editor, qOverload<int>( &QComboBox::currentIndexChanged ),
this, [
this, editor](
int currentIndex ) {
565 Q_UNUSED( currentIndex )
566 const_cast<QgsAggregateMappingDelegate *
>( this )->emit commitData( editor );
572void QgsAggregateMappingDelegate::setEditorData( QWidget *editor,
const QModelIndex &index )
const
574 QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
578 const QVariant value = index.model()->data( index, Qt::EditRole );
579 editorWidget->setCurrentIndex( editorWidget->findData( value ) );
582void QgsAggregateMappingDelegate::setModelData( QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index )
const
584 QComboBox *editorWidget { qobject_cast<QComboBox *>( editor ) };
588 const QVariant currentValue = editorWidget->currentData();
589 model->setData( index, currentValue, Qt::EditRole );
592const QStringList QgsAggregateMappingDelegate::aggregates()
594 static QStringList sAggregates;
595 static std::once_flag initialized;
596 std::call_once( initialized, []() {
597 sAggregates << u
"first_value"_s
603 if ( !function || function->isDeprecated() || function->name().isEmpty() || function->name().at( 0 ) ==
'_' )
606 if ( function->groups().contains(
"Aggregates"_L1 ) )
608 if ( function->name() ==
"aggregate"_L1
609 || function->name() ==
"relation_aggregate"_L1 )
612 sAggregates.append( function->name() );
615 std::sort( sAggregates.begin(), sAggregates.end() );
Holds mapping information for defining sets of aggregates of fields from a QgsFields object.
QgsFields sourceFields() const
Returns a list of source fields.
void appendField(const QgsField &field, const QString &source=QString(), const QString &aggregate=QString())
Appends a new field to the model, with an optional source and aggregate.
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
ColumnDataIndex
The ColumnDataIndex enum represents the column index for the view.
@ Aggregate
Aggregate name.
@ SourceExpression
Expression.
@ DestinationPrecision
Destination field precision.
@ DestinationName
Destination field name.
@ DestinationType
Destination field type string.
@ DestinationLength
Destination field length.
Qt::ItemFlags flags(const QModelIndex &index) const override
bool removeField(const QModelIndex &index)
Removes the field at index from the model, returns true on success.
bool moveUp(const QModelIndex &index)
Moves down the field at index.
QVariant data(const QModelIndex &index, int role) const override
QList< QgsAggregateMappingModel::Aggregate > mapping() const
Returns a list of Aggregate objects representing the current status of the model.
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QgsExpressionContextGenerator * contextGenerator() const
Returns the context generator with the source fields.
bool moveDown(const QModelIndex &index)
Moves up the field at index.
void setSourceFields(const QgsFields &sourceFields)
Set source fields to sourceFields.
void setMapping(const QList< QgsAggregateMappingModel::Aggregate > &mapping)
Sets the mapping to show in the model.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void setBaseExpressionContextGenerator(const QgsExpressionContextGenerator *generator)
Sets the base expression context generator, which will generate the expression contexts for expressio...
QgsAggregateMappingModel(const QgsFields &sourceFields=QgsFields(), QObject *parent=nullptr)
Constructs a QgsAggregateMappingModel from a set of sourceFields.
Abstract interface for generating an expression context.
An abstract base class for defining QgsExpression functions.
Handles parsing and evaluation of expressions (formerly called "search strings").
static const QList< QgsExpressionFunction * > & Functions()
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes).
Holds mapping information for mapping from one set of QgsFields to another.
static const QList< QgsVectorDataProvider::NativeType > supportedDataTypes()
Returns a static list of supported data types.
Encapsulate a field in an attribute table or data source.
QString typeName() const
Gets the field type.
void setPrecision(int precision)
Set the field precision.
void setSubType(QMetaType::Type subType)
If the field is a collection, set its element's type.
void setName(const QString &name)
Set the field name.
void setType(QMetaType::Type type)
Set variant type.
void setLength(int len)
Set the field length.
QString displayName() const
Returns the name to use when displaying this field.
QMetaType::Type subType() const
If the field is a collection, gets its element's type.
void setTypeName(const QString &typeName)
Set the field type.
Container of fields for a vector layer.
Represents a vector layer which manages a vector based dataset.
The Aggregate struct holds information about an aggregate column.
QString source
The source expression used as the input for the aggregate calculation.
QString delimiter
Delimiter string.
QString aggregate
Aggregate name.
QgsField field
The field in its current status (it might have been renamed).