Expose layers field names to expression widget

Fixes #37544
This commit is contained in:
Alessandro Pasotti 2021-12-15 12:22:19 +01:00
parent 68a1470ea1
commit 9a0c54c5c4
5 changed files with 85 additions and 23 deletions

View File

@ -68,6 +68,7 @@ Gets the type of expression item, e.g., header, field, ExpressionNode.
static const int ITEM_TYPE_ROLE;
static const int SEARCH_TAGS_ROLE;
static const int ITEM_NAME_ROLE;
static const int LAYER_ID_ROLE;
};

View File

@ -507,16 +507,16 @@ void QgsExpressionBuilderWidget::loadFieldsAndValues( const QMap<QString, QStrin
// This is not maintained and setLayer() should be used instead.
}
void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, int countLimit, bool forceUsedValues )
void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, QgsVectorLayer *layer, int countLimit, bool forceUsedValues )
{
// TODO We should really return a error the user of the widget that
// the there is no layer set.
if ( !mLayer )
if ( !layer )
return;
// TODO We should thread this so that we don't hold the user up if the layer is massive.
const QgsFields fields = mLayer->fields();
const QgsFields fields = layer->fields();
int fieldIndex = fields.lookupField( fieldName );
if ( fieldIndex < 0 )
@ -534,7 +534,7 @@ void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, int
}
else
{
values = qgis::setToList( mLayer->uniqueValues( fieldIndex, countLimit ) );
values = qgis::setToList( layer->uniqueValues( fieldIndex, countLimit ) );
}
std::sort( values.begin(), values.end() );
@ -564,7 +564,7 @@ void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, int
else
strValue = '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'';
QString representedValue = formatter->representValue( mLayer, fieldIndex, setup.config(), QVariant(), value );
QString representedValue = formatter->representValue( layer, fieldIndex, setup.config(), QVariant(), value );
if ( forceRepresentedValue || representedValue != value.toString() )
representedValue = representedValue + QStringLiteral( " [" ) + strValue + ']';
@ -852,11 +852,20 @@ void QgsExpressionBuilderWidget::loadSampleValues()
QgsExpressionItem *item = mExpressionTreeView->currentItem();
// TODO We should really return a error the user of the widget that
// the there is no layer set.
if ( !mLayer || !item )
QgsVectorLayer *layer = nullptr;
if ( ! item->data( QgsExpressionItem::LAYER_ID_ROLE ).isNull() )
{
layer = qobject_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( item->data( QgsExpressionItem::LAYER_ID_ROLE ).toString() ) );
}
else
{
layer = mLayer;
}
if ( !layer || !item )
return;
mValueGroupBox->show();
fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), 10 );
fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, 10 );
}
void QgsExpressionBuilderWidget::loadAllValues()
@ -864,11 +873,20 @@ void QgsExpressionBuilderWidget::loadAllValues()
QgsExpressionItem *item = mExpressionTreeView->currentItem();
// TODO We should really return a error the user of the widget that
// the there is no layer set.
if ( !mLayer || !item )
QgsVectorLayer *layer = nullptr;
if ( ! item->data( QgsExpressionItem::LAYER_ID_ROLE ).isNull() )
{
layer = qobject_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( item->data( QgsExpressionItem::LAYER_ID_ROLE ).toString() ) );
}
else
{
layer = mLayer;
}
if ( !layer || !item )
return;
mValueGroupBox->show();
fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), -1 );
fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, -1 );
}
void QgsExpressionBuilderWidget::loadSampleUsedValues()
@ -876,11 +894,20 @@ void QgsExpressionBuilderWidget::loadSampleUsedValues()
QgsExpressionItem *item = mExpressionTreeView->currentItem();
// TODO We should really return a error the user of the widget that
// the there is no layer set.
if ( !mLayer || !item )
QgsVectorLayer *layer = nullptr;
if ( ! item->data( QgsExpressionItem::LAYER_ID_ROLE ).isNull() )
{
layer = qobject_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( item->data( QgsExpressionItem::LAYER_ID_ROLE ).toString() ) );
}
else
{
layer = mLayer;
}
if ( !layer || !item )
return;
mValueGroupBox->show();
fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), 10, true );
fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, 10, true );
}
void QgsExpressionBuilderWidget::loadAllUsedValues()
@ -888,11 +915,20 @@ void QgsExpressionBuilderWidget::loadAllUsedValues()
QgsExpressionItem *item = mExpressionTreeView->currentItem();
// TODO We should really return a error the user of the widget that
// the there is no layer set.
if ( !mLayer || !item )
QgsVectorLayer *layer = nullptr;
if ( ! item->data( QgsExpressionItem::LAYER_ID_ROLE ).isNull() )
{
layer = qobject_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( item->data( QgsExpressionItem::LAYER_ID_ROLE ).toString() ) );
}
else
{
layer = mLayer;
}
if ( !layer || !item )
return;
mValueGroupBox->show();
fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), -1, true );
fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, -1, true );
}
void QgsExpressionBuilderWidget::txtPython_textChanged()

View File

@ -419,7 +419,7 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
void clearFunctionMarkers();
void clearErrors();
void runPythonCode( const QString &code );
void fillFieldValues( const QString &fieldName, int countLimit, bool forceUsedValues = false );
void fillFieldValues( const QString &fieldName, QgsVectorLayer *layer, int countLimit, bool forceUsedValues = false );
QString getFunctionHelp( QgsExpressionFunction *function );
QString loadFunctionHelp( QgsExpressionItem *functionName );
QString helpStylesheet() const;

View File

@ -323,7 +323,7 @@ void QgsExpressionTreeView::updateFunctionTree()
loadExpressionContext();
}
void QgsExpressionTreeView::registerItem( const QString &group,
QgsExpressionItem *QgsExpressionTreeView::registerItem( const QString &group,
const QString &label,
const QString &expressionText,
const QString &helpText,
@ -366,6 +366,7 @@ void QgsExpressionTreeView::registerItem( const QString &group,
topLevelItem->setFont( font );
mModel->appendRow( topLevelItem );
}
return item;
}
void QgsExpressionTreeView::registerItemForAllGroups( const QStringList &groups, const QString &label, const QString &expressionText, const QString &helpText, QgsExpressionItem::ItemType type, bool highlightedItem, int sortOrder, const QStringList &tags )
@ -414,7 +415,28 @@ void QgsExpressionTreeView::loadLayers()
for ( ; layerIt != layers.constEnd(); ++layerIt )
{
QIcon icon = QgsIconUtils::iconForLayer( layerIt.value() );
registerItem( QStringLiteral( "Map Layers" ), layerIt.value()->name(), QStringLiteral( "'%1'" ).arg( layerIt.key() ), formatLayerHelp( layerIt.value() ), QgsExpressionItem::ExpressionNode, false, 99, icon );
QgsExpressionItem *parentItem = registerItem( QStringLiteral( "Map Layers" ), layerIt.value()->name(), QStringLiteral( "'%1'" ).arg( layerIt.key() ), formatLayerHelp( layerIt.value() ), QgsExpressionItem::ExpressionNode, false, 99, icon );
loadLayerFields( qobject_cast<QgsVectorLayer *>( layerIt.value() ), parentItem );
}
}
void QgsExpressionTreeView::loadLayerFields( QgsVectorLayer *layer, QgsExpressionItem *parentItem )
{
const QgsFields fields { layer->fields() };
for ( int fieldIdx = 0; fieldIdx < layer->fields().count(); ++fieldIdx )
{
const QgsField field = fields.at( fieldIdx );
QIcon icon = fields.iconForField( fieldIdx );
const QString label { field.displayNameWithAlias() };
QgsExpressionItem *item = new QgsExpressionItem( label, " '" + field.name() + "' ", QString(), QgsExpressionItem::Field );
item->setData( label, Qt::UserRole );
item->setData( 99, QgsExpressionItem::CUSTOM_SORT_ROLE );
item->setData( QStringList(), QgsExpressionItem::SEARCH_TAGS_ROLE );
item->setData( field.name(), QgsExpressionItem::ITEM_NAME_ROLE );
item->setData( layer->id(), QgsExpressionItem::LAYER_ID_ROLE );
item->setIcon( icon );
parentItem->appendRow( item );
}
}

View File

@ -98,6 +98,8 @@ class GUI_EXPORT QgsExpressionItem : public QStandardItem
static const int SEARCH_TAGS_ROLE = Qt::UserRole + 3;
//! Item name role
static const int ITEM_NAME_ROLE = Qt::UserRole + 4;
//! Layer ID role \since QGIS 3.24
static const int LAYER_ID_ROLE = Qt::UserRole + 5;
private:
QString mExpressionText;
@ -318,13 +320,13 @@ class GUI_EXPORT QgsExpressionTreeView : public QTreeView
* \param tags tags to find function
* \param name name of the item
*/
void registerItem( const QString &group, const QString &label, const QString &expressionText,
const QString &helpText = QString(),
QgsExpressionItem::ItemType type = QgsExpressionItem::ExpressionNode,
bool highlightedItem = false, int sortOrder = 1,
const QIcon &icon = QIcon(),
const QStringList &tags = QStringList(),
const QString &name = QString() );
QgsExpressionItem *registerItem( const QString &group, const QString &label, const QString &expressionText,
const QString &helpText = QString(),
QgsExpressionItem::ItemType type = QgsExpressionItem::ExpressionNode,
bool highlightedItem = false, int sortOrder = 1,
const QIcon &icon = QIcon(),
const QStringList &tags = QStringList(),
const QString &name = QString() );
/**
* Registers a node item for the expression builder, adding multiple items when the function exists in multiple groups
@ -345,6 +347,7 @@ class GUI_EXPORT QgsExpressionTreeView : public QTreeView
void loadExpressionContext();
void loadRelations();
void loadLayers();
void loadLayerFields( QgsVectorLayer *layer, QgsExpressionItem *parentItem );
void loadFieldNames();
/**