Skip to content

Commit

Permalink
Forms: expression controlled aliases (labels)
Browse files Browse the repository at this point in the history
  • Loading branch information
elpaso committed Apr 13, 2020
1 parent a84ec12 commit 7334ad0
Show file tree
Hide file tree
Showing 14 changed files with 247 additions and 32 deletions.
14 changes: 14 additions & 0 deletions python/core/auto_generated/qgsattributeeditorelement.sip.in
Expand Up @@ -112,6 +112,20 @@ Controls if this element should be labeled with a title (field, relation or grou
Controls if this element should be labeled with a title (field, relation or groupname).

.. versionadded:: 2.18
%End

QString labelExpression() const;
%Docstring
Returns the (possibly empty) label expression

.. versionadded:: 3.14
%End

void setLabelExpression( const QString &labelExpression );
%Docstring
Sets the label expression to ``labelExpression``

.. versionadded:: 3.14
%End

protected:
Expand Down
13 changes: 13 additions & 0 deletions python/core/auto_generated/qgseditformconfig.sip.in
Expand Up @@ -191,6 +191,19 @@ on the left hand side.
Labeling on top leaves more horizontal space for the widget itself.
%End

QString labelExpression( int idx ) const;
%Docstring
Returns the (possibly empty) expression for the label, to be evaluated in the form context.

.. versionadded:: 3.14
%End

void setLabelExpression( int idx, const QString &labelExpression );
%Docstring
Set the label expression to ``labelExpression``, to be evaluated in the form context.

.. versionadded:: 3.14
%End


QString initFunction() const;
Expand Down
10 changes: 10 additions & 0 deletions src/core/qgsattributeeditorelement.cpp
Expand Up @@ -130,6 +130,16 @@ void QgsAttributeEditorElement::setShowLabel( bool showLabel )
mShowLabel = showLabel;
}

QString QgsAttributeEditorElement::labelExpression() const
{
return mLabelExpression;
}

void QgsAttributeEditorElement::setLabelExpression( const QString &labelExpression )
{
mLabelExpression = labelExpression;
}

void QgsAttributeEditorRelation::saveConfiguration( QDomElement &elem ) const
{
elem.setAttribute( QStringLiteral( "relation" ), mRelation.id() );
Expand Down
13 changes: 13 additions & 0 deletions src/core/qgsattributeeditorelement.h
Expand Up @@ -134,10 +134,23 @@ class CORE_EXPORT QgsAttributeEditorElement SIP_ABSTRACT
*/
void setShowLabel( bool showLabel );

/**
* Returns the (possibly empty) label expression
* \since QGIS 3.14
*/
QString labelExpression() const;

/**
* Sets the label expression to \a labelExpression
* \since QGIS 3.14
*/
void setLabelExpression( const QString &labelExpression );

protected:
#ifndef SIP_RUN
AttributeEditorType mType;
QString mName;
QString mLabelExpression;
QgsAttributeEditorElement *mParent = nullptr;
bool mShowLabel;
#endif
Expand Down
37 changes: 37 additions & 0 deletions src/core/qgseditformconfig.cpp
Expand Up @@ -211,6 +211,23 @@ void QgsEditFormConfig::setLabelOnTop( int idx, bool onTop )
}
}

QString QgsEditFormConfig::labelExpression( int idx ) const
{
if ( idx >= 0 && idx < d->mFields.count() )
return d->mLabelExpressions.value( d->mFields.at( idx ).name(), QString() );
else
return QString();
}

void QgsEditFormConfig::setLabelExpression( int idx, const QString &labelExpression )
{
if ( idx >= 0 && idx < d->mFields.count() )
{
d.detach();
d->mLabelExpressions[ d->mFields.at( idx ).name() ] = labelExpression;
}
}

QString QgsEditFormConfig::initFunction() const
{
return d->mInitFunction;
Expand Down Expand Up @@ -382,6 +399,14 @@ void QgsEditFormConfig::readXml( const QDomNode &node, QgsReadWriteContext &cont
d->mLabelOnTop.insert( labelOnTopElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( labelOnTopElement.attribute( QStringLiteral( "labelOnTop" ) ).toInt() ) );
}

d->mLabelExpressions.clear();
QDomNodeList labelExpressionsNodeList = node.namedItem( QStringLiteral( "labelExpression" ) ).toElement().childNodes();
for ( int i = 0; i < labelExpressionsNodeList.size(); ++i )
{
QDomElement labelExpressionElement = labelExpressionsNodeList.at( i ).toElement();
d->mLabelExpressions.insert( labelExpressionElement.attribute( QStringLiteral( "name" ) ), labelExpressionElement.attribute( QStringLiteral( "labelExpression" ) ) );
}

QDomNodeList widgetsNodeList = node.namedItem( QStringLiteral( "widgets" ) ).toElement().childNodes();

for ( int i = 0; i < widgetsNodeList.size(); ++i )
Expand Down Expand Up @@ -505,6 +530,16 @@ void QgsEditFormConfig::writeXml( QDomNode &node, const QgsReadWriteContext &con
}
node.appendChild( labelOnTopElem );

QDomElement labelExpressionElem = doc.createElement( QStringLiteral( "labelExpression" ) );
for ( auto labelExpressionsIt = d->mLabelExpressions.constBegin(); labelExpressionsIt != d->mLabelExpressions.constEnd(); ++labelExpressionsIt )
{
QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
fieldElem.setAttribute( QStringLiteral( "name" ), labelExpressionsIt.key() );
fieldElem.setAttribute( QStringLiteral( "labelExpression" ), labelExpressionsIt.value() );
labelExpressionElem.appendChild( fieldElem );
}
node.appendChild( labelExpressionElem );

QDomElement widgetsElem = doc.createElement( QStringLiteral( "widgets" ) );

QMap<QString, QVariantMap >::ConstIterator configIt( d->mWidgetConfigs.constBegin() );
Expand Down Expand Up @@ -604,6 +639,8 @@ QgsAttributeEditorElement *QgsEditFormConfig::attributeEditorElementFromDomEleme
newElement->setShowLabel( elem.attribute( QStringLiteral( "showLabel" ) ).toInt() );
else
newElement->setShowLabel( true );
if ( elem.hasAttribute( QStringLiteral( "labelExpression" ) ) )
newElement->setLabelExpression( elem.attribute( QStringLiteral( "labelExpression" ) ) );
}

return newElement;
Expand Down
11 changes: 11 additions & 0 deletions src/core/qgseditformconfig.h
Expand Up @@ -219,6 +219,17 @@ class CORE_EXPORT QgsEditFormConfig
**/
void setLabelOnTop( int idx, bool onTop );

/**
* Returns the (possibly empty) expression for the label, to be evaluated in the form context.
* \since QGIS 3.14
**/
QString labelExpression( int idx ) const;

/**
* Set the label expression to \a labelExpression, to be evaluated in the form context.
* \since QGIS 3.14
**/
void setLabelExpression( int idx, const QString &labelExpression );

// Python form init function stuff

Expand Down
2 changes: 2 additions & 0 deletions src/core/qgseditformconfig_p.h
Expand Up @@ -36,6 +36,7 @@ class QgsEditFormConfigPrivate : public QSharedData
, mConfiguredRootContainer( o.mConfiguredRootContainer )
, mFieldEditables( o.mFieldEditables )
, mLabelOnTop( o.mLabelOnTop )
, mLabelExpressions( o.mLabelExpressions )
, mWidgetConfigs( o.mWidgetConfigs )
, mEditorLayout( o.mEditorLayout )
, mUiFormPath( o.mUiFormPath )
Expand All @@ -59,6 +60,7 @@ class QgsEditFormConfigPrivate : public QSharedData

QMap< QString, bool> mFieldEditables;
QMap< QString, bool> mLabelOnTop;
QMap< QString, QString> mLabelExpressions;

QMap<QString, QVariantMap > mWidgetConfigs;

Expand Down
25 changes: 23 additions & 2 deletions src/gui/attributeformconfig/qgsattributetypedialog.cpp
Expand Up @@ -73,6 +73,9 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx
mExpressionWidget->registerExpressionContextGenerator( this );
mExpressionWidget->setLayer( mLayer );

mAliasExpression->registerExpressionContextGenerator( this );
mAliasExpression->setLayer( mLayer );

connect( mExpressionWidget, &QgsExpressionLineEdit::expressionChanged, this, &QgsAttributeTypeDialog::defaultExpressionChanged );
connect( mUniqueCheckBox, &QCheckBox::toggled, this, [ = ]( bool checked )
{
Expand All @@ -92,6 +95,8 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx

constraintExpressionWidget->setAllowEmptyFieldName( true );
constraintExpressionWidget->setLayer( vl );

// TODO: mAliasExpression->registerExpressionContextGenerator( ... );
}

QgsAttributeTypeDialog::~QgsAttributeTypeDialog()
Expand Down Expand Up @@ -303,8 +308,14 @@ QgsExpressionContext QgsAttributeTypeDialog::createExpressionContext() const
<< QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( QgsProject::instance() )
<< QgsExpressionContextUtils::layerScope( mLayer )
<< QgsExpressionContextUtils::formScope( QgsFeature( mLayer->fields() ) )
<< QgsExpressionContextUtils::mapToolCaptureScope( QList<QgsPointLocator::Match>() );

context.setHighlightedFunctions( QStringList() << QStringLiteral( "current_value" ) );
context.setHighlightedVariables( QStringList() << QStringLiteral( "current_geometry" )
<< QStringLiteral( "current_feature" )
<< QStringLiteral( "form_mode" ) );

return context;
}

Expand Down Expand Up @@ -339,12 +350,22 @@ void QgsAttributeTypeDialog::setFieldEditable( bool editable )

void QgsAttributeTypeDialog::setAlias( const QString &alias )
{
leAlias->setText( alias );
mAlias->setText( alias );
}

QString QgsAttributeTypeDialog::alias() const
{
return leAlias->text();
return mAlias->text();
}

void QgsAttributeTypeDialog::setAliasExpression( const QString &aliasExpression )
{
mAliasExpression->setExpression( aliasExpression );
}

QString QgsAttributeTypeDialog::aliasExpression() const
{
return mAliasExpression->expression();
}

void QgsAttributeTypeDialog::setComment( const QString &comment )
Expand Down
12 changes: 12 additions & 0 deletions src/gui/attributeformconfig/qgsattributetypedialog.h
Expand Up @@ -76,6 +76,18 @@ class GUI_EXPORT QgsAttributeTypeDialog: public QWidget, private Ui::QgsAttribut
*/
QString alias() const;

/**
* Sets expression for for label alias
* \since QGIS 3.14
*/
void setAliasExpression( const QString &aliasExpression );

/**
* Returns the expression for the label alias
* \since QGIS 3.14
*/
QString aliasExpression() const;

/**
* Setter for label comment
*/
Expand Down
49 changes: 44 additions & 5 deletions src/gui/qgsattributeform.cpp
Expand Up @@ -86,6 +86,7 @@ QgsAttributeForm::QgsAttributeForm( QgsVectorLayer *vl, const QgsFeature &featur
connect( this, &QgsAttributeForm::modeChanged, this, &QgsAttributeForm::updateContainersVisibility );

updateContainersVisibility();

}

QgsAttributeForm::~QgsAttributeForm()
Expand Down Expand Up @@ -305,6 +306,7 @@ void QgsAttributeForm::setFeature( const QgsFeature &feature )
}
}
mIsSettingFeature = false;
mExpressionContext.setFeature( feature );
}

bool QgsAttributeForm::saveEdits()
Expand Down Expand Up @@ -899,6 +901,9 @@ void QgsAttributeForm::onAttributeChanged( const QVariant &value, const QVariant
updateDefaultValues( eww->fieldIdx() );
mAlreadyUpdatedFields.removeAll( eww->fieldIdx() );

// Updates expression controlled labels
updateLabels();

if ( !signalEmitted )
{
Q_NOWARN_DEPRECATED_PUSH
Expand Down Expand Up @@ -1002,6 +1007,28 @@ void QgsAttributeForm::updateConstraint( const QgsFeature &ft, QgsEditorWidgetWr
eww->updateConstraint( ft, constraintOrigin );
}

void QgsAttributeForm::updateLabels()
{
if ( ! mExpressionLabels.isEmpty() )
{
QgsFeature currentFeature;
if ( currentFormFeature( currentFeature ) )
{
mExpressionContext << QgsExpressionContextUtils::formScope( currentFeature, mContext.attributeFormModeString() );
mExpressionContext.setFields( mLayer->fields() );
for ( auto it = mExpressionLabels.constBegin() ; it != mExpressionLabels.constEnd(); ++it )
{
QLabel *label { it.key() };
QgsExpression exp { it.value() };
if ( exp.prepare( &mExpressionContext ) && ! exp.hasParserError() )
{
label->setText( exp.evaluate( &mExpressionContext ).toString() );
}
}
}
}
}

bool QgsAttributeForm::currentFormFeature( QgsFeature &feature )
{
bool rc = true;
Expand Down Expand Up @@ -1443,6 +1470,10 @@ void QgsAttributeForm::init()
}
else
{
if ( ! widgetInfo.labelExpression.isEmpty() )
{
mExpressionLabels[ label ] = QgsExpression( widgetInfo.labelExpression );
}
layout->addWidget( label, row, column++ );
layout->addWidget( widgetInfo.widget, row, column++ );
}
Expand Down Expand Up @@ -1498,6 +1529,7 @@ void QgsAttributeForm::init()
QString labelText = fieldName;
labelText.replace( '&', QStringLiteral( "&&" ) ); // need to escape '&' or they'll be replace by _ in the label text

const QString labelExpression { mLayer->editFormConfig().labelExpression( idx ) };
const QgsEditorWidgetSetup widgetSetup = QgsGui::editorWidgetRegistry()->findBest( mLayer, field.name() );

if ( widgetSetup.type() == QLatin1String( "Hidden" ) )
Expand All @@ -1506,11 +1538,16 @@ void QgsAttributeForm::init()
bool labelOnTop = mLayer->editFormConfig().labelOnTop( idx );

// This will also create the widget
QLabel *l = new QLabel( labelText );
l->setToolTip( QgsFieldModel::fieldToolTipExtended( field, mLayer ) );
QLabel *label = new QLabel( labelText );
label->setToolTip( QgsFieldModel::fieldToolTipExtended( field, mLayer ) );
QSvgWidget *i = new QSvgWidget();
i->setFixedSize( 18, 18 );

if ( ! labelExpression.isEmpty() )
{
mExpressionLabels[ label ] = QgsExpression( labelExpression );
}

QgsEditorWidgetWrapper *eww = QgsGui::editorWidgetRegistry()->create( widgetSetup.type(), mLayer, idx, widgetSetup.config(), nullptr, this, mContext );

QWidget *w = nullptr;
Expand All @@ -1522,7 +1559,7 @@ void QgsAttributeForm::init()
mFormWidgets.append( formWidget );
formWidget->createSearchWidgetWrappers( mContext );

l->setBuddy( eww->widget() );
label->setBuddy( eww->widget() );
}
else
{
Expand All @@ -1541,16 +1578,17 @@ void QgsAttributeForm::init()

if ( labelOnTop )
{
gridLayout->addWidget( l, row++, 0, 1, 2 );
gridLayout->addWidget( label, row++, 0, 1, 2 );
gridLayout->addWidget( w, row++, 0, 1, 2 );
gridLayout->addWidget( i, row++, 0, 1, 2 );
}
else
{
gridLayout->addWidget( l, row, 0 );
gridLayout->addWidget( label, row, 0 );
gridLayout->addWidget( w, row, 1 );
gridLayout->addWidget( i, row++, 2 );
}

}

const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer );
Expand Down Expand Up @@ -2048,6 +2086,7 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt
}

newWidgetInfo.showLabel = widgetDef->showLabel();
newWidgetInfo.labelExpression = widgetDef->labelExpression();

return newWidgetInfo;
}
Expand Down

0 comments on commit 7334ad0

Please sign in to comment.