80#include "moc_qgsattributeform.cpp"
82using namespace Qt::StringLiterals;
84int QgsAttributeForm::sFormCounter = 0;
90 , mFormNr( sFormCounter++ )
91 , mEditCommandMessage( tr(
"Attributes changed" ) )
103 updateContainersVisibility();
105 updateEditableState();
111 qDeleteAll( mInterfaces );
138 mInterfaces.append( iface );
143 return mFeature.isValid() && mLayer->isEditable();
154 if ( mUnsavedMultiEditChanges )
157 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ), tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
158 if ( res == QMessageBox::Yes )
163 clearMultiEditMessages();
165 mUnsavedMultiEditChanges =
false;
221 w->setContext( newContext );
224 auto setRelationWidgetsVisible = [
this](
bool relationWidgetsVisible ) {
227 w->setVisible( relationWidgetsVisible );
234 setRelationWidgetsVisible(
true );
236 mSearchButtonBox->setVisible(
false );
240 setRelationWidgetsVisible(
true );
242 mSearchButtonBox->setVisible(
false );
246 setRelationWidgetsVisible(
true );
248 mSearchButtonBox->setVisible(
false );
252 setRelationWidgetsVisible(
true );
253 resetMultiEdit(
false );
255 mSearchButtonBox->setVisible(
false );
259 setRelationWidgetsVisible(
true );
260 mSearchButtonBox->setVisible(
true );
266 setRelationWidgetsVisible(
false );
267 mSearchButtonBox->setVisible(
false );
273 setRelationWidgetsVisible(
true );
276 mSearchButtonBox->setVisible(
false );
280 setRelationWidgetsVisible(
false );
282 mSearchButtonBox->setVisible(
false );
291 const auto constMWidgets = mWidgets;
306 QVariant mainValue = eww->
value();
308 additionalFieldValues[index] = value;
309 eww->
setValues( mainValue, additionalFieldValues );
318 mFeature.setGeometry( geometry );
323 mIsSettingFeature =
true;
341 mIsSettingFeature =
false;
342 const auto constMInterfaces = mInterfaces;
345 iface->featureChanged();
361 mIsSettingFeature =
false;
364bool QgsAttributeForm::saveEdits( QString *error )
367 bool changedLayer =
false;
372 bool doUpdate =
false;
381 QgsAttributes src = mFeature.attributes();
382 QgsAttributes dst = mFeature.attributes();
384 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
386 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
390 QgsTextEditWrapper *textEdit = qobject_cast<QgsTextEditWrapper *>( eww );
394 *error = tr(
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
397 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
398 QVariantList srcVars = QVariantList() << eww->
value();
399 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
403 for (
const QString &fieldName : additionalFields )
407 dstVars << dst.at( idx );
411 Q_ASSERT( dstVars.count() == srcVars.count() );
413 for (
int i = 0; i < dstVars.count(); i++ )
415 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
417 dst[fieldIndexes[i]] = srcVars[i];
427 const auto constMInterfaces = mInterfaces;
428 for ( QgsAttributeFormInterface *iface : constMInterfaces )
430 if ( !iface->acceptChanges( updatedFeature ) )
440 mFeature = updatedFeature;
444 mFeature.setValid(
true );
445 mLayer->beginEditCommand( mEditCommandMessage );
446 bool res = mLayer->addFeature( updatedFeature );
449 mFeature.setAttributes( updatedFeature.
attributes() );
450 mLayer->endEditCommand();
452 const QgsFields fields = mLayer->fields();
453 const QgsAttributes newValues = updatedFeature.
attributes();
454 const QVariant lastUsedValuesVariant = mLayer->property(
"AttributeFormLastUsedValues" );
457 for (
int idx = 0; idx < fields.
count(); ++idx )
462 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
463 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
464 if ( !rememberLastUsedValues.contains( idx ) )
467 rememberLastUsedValues[idx] = remember;
468 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
471 const QVariant newValue = rememberLastUsedValues[idx] ? newValues.at( idx ) : QVariant();
472 if ( !lastUsedValues.contains( idx ) || lastUsedValues[idx] != newValue )
474 lastUsedValues[idx] = newValue;
475 QgsDebugMsgLevel( u
"Saving %1 for %2"_s.arg( ( newValue.toString() ).arg( idx ) ), 2 );
479 mLayer->setProperty(
"AttributeFormLastUsedValues", QVariant::fromValue<QgsAttributeMap>( lastUsedValues ) );
485 mLayer->destroyEditCommand();
489 mLayer->beginEditCommand( mEditCommandMessage );
495 for (
int i = 0; i < dst.count(); ++i )
498 || !dst.at( i ).isValid()
499 || !fieldIsEditable( i ) )
505 QgsDebugMsgLevel( u
"dst:'%1' (type:%2, isNull:%3, isValid:%4)"_s.arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( dst.at( i ) ) ).arg( dst.at( i ).isValid() ), 2 );
506 QgsDebugMsgLevel( u
"src:'%1' (type:%2, isNull:%3, isValid:%4)"_s.arg( src.at( i ).toString(), src.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( src.at( i ) ) ).arg( src.at( i ).isValid() ), 2 );
508 newValues[i] = dst.at( i );
509 oldValues[i] = src.at( i );
514 auto context = std::make_unique<QgsVectorLayerToolsContext>();
515 QgsExpressionContext expressionContext = createExpressionContext( updatedFeature );
516 context->setExpressionContext( &expressionContext );
517 success = mLayer->changeAttributeValues( mFeature.id(), newValues, oldValues,
false, context.get() );
519 if ( success && n > 0 )
521 mLayer->endEditCommand();
522 mFeature.setAttributes( dst );
527 mLayer->destroyEditCommand();
541 mLayer->triggerRepaint();
546QgsFeature QgsAttributeForm::getUpdatedFeature()
const
549 QgsFeature updatedFeature = QgsFeature( mFeature );
551 QgsAttributes featureAttributes = mFeature.attributes();
552 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
554 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
558 QVariantList dstVars = QVariantList() << featureAttributes.at( eww->
fieldIdx() );
559 QVariantList srcVars = QVariantList() << eww->
value();
560 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
564 for (
const QString &fieldName : additionalFields )
568 dstVars << featureAttributes.at( idx );
572 Q_ASSERT( dstVars.count() == srcVars.count() );
574 for (
int i = 0; i < dstVars.count(); i++ )
576 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
577 featureAttributes[fieldIndexes[i]] = srcVars[i];
582 return updatedFeature;
585void QgsAttributeForm::updateValuesDependencies(
const int originIdx )
587 updateValuesDependenciesDefaultValues( originIdx );
588 updateValuesDependenciesVirtualFields( originIdx );
591void QgsAttributeForm::updateValuesDependenciesDefaultValues(
const int originIdx )
593 if ( !mDefaultValueDependencies.contains( originIdx ) )
596 if ( !mFeature.isValid() )
615 QgsFeature updatedFeature = getUpdatedFeature();
618 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
619 for ( QgsWidgetWrapper *ww : std::as_const( relevantWidgets ) )
621 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
635 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
638 QgsExpressionContext context = createExpressionContext( updatedFeature );
640 const QVariant value = mLayer->defaultValue( eww->
fieldIdx(), updatedFeature, &context );
642 mCurrentFormFeature.setAttribute( eww->
field().
name(), value );
647void QgsAttributeForm::updateValuesDependenciesVirtualFields(
const int originIdx )
649 if ( !mVirtualFieldsDependencies.contains( originIdx ) )
652 if ( !mFeature.isValid() )
656 QgsFeature updatedFeature = getUpdatedFeature();
659 const QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
660 for ( QgsWidgetWrapper *ww : relevantWidgets )
662 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
667 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
671 QgsExpressionContext context = createExpressionContext( updatedFeature );
672 QgsExpression exp( mLayer->expressionField( eww->
fieldIdx() ) );
673 const QVariant value = exp.evaluate( &context );
679void QgsAttributeForm::updateValuesDependenciesParent()
682 QgsFeature updatedFeature = getUpdatedFeature();
683 QList<int> updatedFields;
686 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mParentDependencies;
687 for ( QgsEditorWidgetWrapper *eww : relevantWidgets )
690 if ( updatedFields.contains( eww->
fieldIdx() ) )
695 QgsExpressionContext context = createExpressionContext( updatedFeature );
696 const QVariant value = mLayer->defaultValue( eww->
fieldIdx(), updatedFeature, &context );
701void QgsAttributeForm::updateRelatedLayerFields()
704 updateRelatedLayerFieldsDependencies();
706 if ( mRelatedLayerFieldsDependencies.isEmpty() )
709 if ( !mFeature.isValid() )
713 QgsFeature updatedFeature = getUpdatedFeature();
716 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mRelatedLayerFieldsDependencies;
717 for ( QgsEditorWidgetWrapper *eww : relevantWidgets )
720 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
724 QgsExpressionContext context = createExpressionContext( updatedFeature );
725 QgsExpression exp( mLayer->expressionField( eww->
fieldIdx() ) );
726 QVariant value = exp.evaluate( &context );
731void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
736 mUnsavedMultiEditChanges =
false;
740void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
742 clearMultiEditMessages();
743 resetMultiEdit( link ==
"#apply"_L1 );
746void QgsAttributeForm::filterTriggered()
748 QString filter = createFilterExpression();
754void QgsAttributeForm::searchZoomTo()
756 QString filter = createFilterExpression();
757 if ( filter.isEmpty() )
763void QgsAttributeForm::searchFlash()
765 QString filter = createFilterExpression();
766 if ( filter.isEmpty() )
772void QgsAttributeForm::filterAndTriggered()
774 QString filter = createFilterExpression();
775 if ( filter.isEmpty() )
783void QgsAttributeForm::filterOrTriggered()
785 QString filter = createFilterExpression();
786 if ( filter.isEmpty() )
794void QgsAttributeForm::pushSelectedFeaturesMessage()
796 int count = mLayer->selectedFeatureCount();
799 mMessageBar->pushMessage( QString(), tr(
"%n matching feature(s) selected",
"matching features", count ),
Qgis::MessageLevel::Info );
814 QString filter = createFilterExpression();
815 if ( filter.isEmpty() )
819 pushSelectedFeaturesMessage();
824void QgsAttributeForm::searchSetSelection()
829void QgsAttributeForm::searchAddToSelection()
834void QgsAttributeForm::searchRemoveFromSelection()
839void QgsAttributeForm::searchIntersectSelection()
844bool QgsAttributeForm::saveMultiEdits()
848 const QList<int> fieldIndexes = mFormEditorWidgets.uniqueKeys();
849 for (
int fieldIndex : fieldIndexes )
851 const QList<QgsAttributeFormEditorWidget *> widgets = mFormEditorWidgets.values( fieldIndex );
852 if ( !widgets.first()->hasChanged() )
855 if ( !widgets.first()->currentValue().isValid()
856 || !fieldIsEditable( fieldIndex ) )
862 for ( QgsAttributeFormEditorWidget *widget : widgets )
863 widget->changesCommitted();
865 newAttributeValues.insert( fieldIndex, widgets.first()->currentValue() );
868 if ( newAttributeValues.isEmpty() )
876 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
877 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
878 if ( res != QMessageBox::Ok )
885 mLayer->beginEditCommand( tr(
"Updated multiple feature attributes" ) );
889 const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
892 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
893 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
895 success &= mLayer->changeAttributeValue( fid, aIt.key(), aIt.value() );
899 clearMultiEditMessages();
902 mLayer->endEditCommand();
903 mLayer->triggerRepaint();
904 mMultiEditMessageBarItem =
new QgsMessageBarItem( tr(
"Attribute changes for multiple features applied." ),
Qgis::MessageLevel::Success, -1 );
908 mLayer->destroyEditCommand();
912 if ( !mButtonBox->isVisible() )
913 mMessageBar->pushItem( mMultiEditMessageBarItem );
939 wrapper->notifyAboutToSave();
982 success = saveEdits( error );
986 success = saveMultiEdits();
994 mUnsavedMultiEditChanges =
false;
1003 mValuesInitialized =
false;
1004 const auto constMWidgets = mWidgets;
1007 ww->setFeature( mFeature );
1018 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1019 updateValuesDependenciesVirtualFields( eww->
fieldIdx() );
1020 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1023 mValuesInitialized =
true;
1029 const auto widgets { findChildren<QgsAttributeFormEditorWidget *>() };
1036void QgsAttributeForm::clearMultiEditMessages()
1038 if ( mMultiEditUnsavedMessageBarItem )
1040 if ( !mButtonBox->isVisible() )
1041 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
1042 mMultiEditUnsavedMessageBarItem =
nullptr;
1044 if ( mMultiEditMessageBarItem )
1046 if ( !mButtonBox->isVisible() )
1047 mMessageBar->popWidget( mMultiEditMessageBarItem );
1048 mMultiEditMessageBarItem =
nullptr;
1052QString QgsAttributeForm::createFilterExpression()
const
1054 QStringList filters;
1055 for ( QgsAttributeFormWidget *w : std::as_const( mFormWidgets ) )
1057 QString filter = w->currentFilterExpression();
1058 if ( !filter.isEmpty() )
1062 if ( filters.isEmpty() )
1065 QString filter = filters.join(
") AND ("_L1 ).prepend(
'(' ).append(
')' );
1071 QgsExpressionContext context;
1074 if ( mExtraContextScope )
1076 context.
appendScope(
new QgsExpressionContextScope( *mExtraContextScope.get() ) );
1078 if ( mContext.parentFormFeature().isValid() )
1087void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
1089 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( sender() );
1092 bool signalEmitted =
false;
1094 if ( mValuesInitialized )
1097 mCurrentFormFeature.setAttribute( eww->
field().
name(), value );
1101 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1102 for ( QgsAttributeFormEditorWidget *formEditorWidget : std::as_const( formEditorWidgets ) )
1104 if ( formEditorWidget->editorWidget() == eww )
1108 formEditorWidget->editorWidget()->setValue( value );
1126 for (
int i = 0; i < additionalFields.count(); i++ )
1128 const QString fieldName = additionalFields.at( i );
1129 const QVariant value = additionalFieldValues.at( i );
1133 signalEmitted =
true;
1135 if ( mValuesInitialized )
1136 updateJoinedFields( *eww );
1142 if ( !mIsSettingMultiEditFeatures )
1144 mUnsavedMultiEditChanges =
true;
1146 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
1147 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
1148 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1149 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
1150 clearMultiEditMessages();
1153 if ( !mButtonBox->isVisible() )
1154 mMessageBar->pushItem( mMultiEditUnsavedMessageBarItem );
1157 signalEmitted =
true;
1167 updateConstraints( eww );
1170 if ( mValuesInitialized && !mIsSettingMultiEditFeatures )
1173 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1174 updateValuesDependencies( eww->
fieldIdx() );
1175 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1180 updateEditableState();
1182 if ( !signalEmitted )
1187 bool attributeHasChanged = !mIsSettingFeature;
1189 attributeHasChanged &= !mIsSettingMultiEditFeatures;
1195void QgsAttributeForm::updateAllConstraints()
1197 const auto constMWidgets = mWidgets;
1198 for ( QgsWidgetWrapper *ww : constMWidgets )
1200 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1202 updateConstraints( eww );
1210 if ( currentFormValuesFeature( ft ) )
1222 updateConstraint( ft, eww );
1225 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
1227 for ( QgsEditorWidgetWrapper *depsEww : deps )
1228 updateConstraint( ft, depsEww );
1233 QgsExpressionContext context = createExpressionContext( ft );
1236 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
1237 for ( ContainerInformation *info : infos )
1239 info->apply( &context );
1244void QgsAttributeForm::updateContainersVisibility()
1246 QgsExpressionContext context = createExpressionContext( mFeature );
1248 const QVector<ContainerInformation *> infos = mContainerVisibilityCollapsedInformation;
1250 for ( ContainerInformation *info : infos )
1252 info->apply( &context );
1262 updateAllConstraints();
1277 if ( mJoinedFeatures.contains( info ) )
1293void QgsAttributeForm::updateLabels()
1295 if ( !mLabelDataDefinedProperties.isEmpty() )
1297 QgsFeature currentFeature;
1298 if ( currentFormValuesFeature( currentFeature ) )
1300 QgsExpressionContext context = createExpressionContext( currentFeature );
1302 for (
auto it = mLabelDataDefinedProperties.constBegin(); it != mLabelDataDefinedProperties.constEnd(); ++it )
1304 QLabel *label { it.key() };
1306 const QString value { it->valueAsString( context, QString(), &ok ) };
1307 if ( ok && !value.isEmpty() )
1309 label->setText( value );
1314 if ( !mCustomCommentDataDefinedProperties.isEmpty() )
1316 QgsFeature currentFeature;
1317 if ( currentFormValuesFeature( currentFeature ) )
1319 QgsExpressionContext context = createExpressionContext( currentFeature );
1321 for (
auto it = mCustomCommentDataDefinedProperties.constBegin(); it != mCustomCommentDataDefinedProperties.constEnd(); ++it )
1323 QWidget *labelWidget { it.key() };
1325 const QString value { it->valueAsString( context, QString(), &ok ) };
1326 if ( ok && !value.isEmpty() )
1328 const QString fieldName = labelWidget->objectName();
1329 const QgsFields fields = mLayer->fields();
1342void QgsAttributeForm::updateEditableState()
1344 if ( !mEditableDataDefinedProperties.isEmpty() )
1346 QgsFeature currentFeature;
1347 if ( currentFormValuesFeature( currentFeature ) )
1349 QgsExpressionContext context = createExpressionContext( currentFeature );
1351 for (
auto it = mEditableDataDefinedProperties.constBegin(); it != mEditableDataDefinedProperties.constEnd(); ++it )
1353 QWidget *w { it.key() };
1355 const bool isEditable { it->valueAsBool( context,
true, &ok ) && mLayer && mLayer->isEditable() };
1358 QgsAttributeFormEditorWidget *editorWidget { qobject_cast<QgsAttributeFormEditorWidget *>( w ) };
1365 w->setEnabled( isEditable );
1373bool QgsAttributeForm::currentFormValuesFeature(
QgsFeature &feature )
1376 feature = QgsFeature( mFeature );
1377 QgsAttributes dst =
feature.attributes();
1379 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1381 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1386 if ( dst.count() > eww->
fieldIdx() )
1388 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1389 QVariantList srcVars = QVariantList() << eww->
value();
1390 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1394 for (
const QString &fieldName : additionalFields )
1397 fieldIndexes << idx;
1398 dstVars << dst.at( idx );
1402 Q_ASSERT( dstVars.count() == srcVars.count() );
1404 for (
int i = 0; i < dstVars.count(); i++ )
1410 dst[fieldIndexes[i]] = srcVars[i];
1427void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1429 mContainerVisibilityCollapsedInformation.append( info );
1433 for (
const QString &col : referencedColumns )
1435 mContainerInformationDependency[col].append( info );
1439bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1441 bool valid {
true };
1443 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1445 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1463bool QgsAttributeForm::currentFormValidHardConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1465 bool valid {
true };
1467 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1469 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1484void QgsAttributeForm::onAttributeAdded(
int idx )
1486 mPreventFeatureRefresh =
false;
1487 if ( mFeature.isValid() )
1489 QgsAttributes attrs = mFeature.attributes();
1491 mFeature.setFields(
layer()->fields() );
1492 mFeature.setAttributes( attrs );
1498void QgsAttributeForm::onAttributeDeleted(
int idx )
1500 mPreventFeatureRefresh =
false;
1501 if ( mFeature.isValid() )
1503 QgsAttributes attrs = mFeature.attributes();
1504 attrs.remove( idx );
1505 mFeature.setFields(
layer()->fields() );
1506 mFeature.setAttributes( attrs );
1512void QgsAttributeForm::onRelatedFeaturesChanged()
1514 updateRelatedLayerFields();
1517void QgsAttributeForm::onUpdatedFields()
1519 mPreventFeatureRefresh =
false;
1520 if ( mFeature.isValid() )
1522 QgsAttributes attrs(
layer()->fields().size() );
1525 int idx = mFeature.fields().indexFromName(
layer()->fields().at( i ).name() );
1528 attrs[i] = mFeature.attributes().at( idx );
1529 if ( mFeature.attributes().at( idx ).userType() !=
layer()->fields().at( i ).type() )
1531 attrs[i].convert(
layer()->fields().at( i ).type() );
1539 mFeature.setFields(
layer()->fields() );
1540 mFeature.setAttributes( attrs );
1548 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( sender() );
1551 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1553 for ( QgsAttributeFormEditorWidget *formEditorWidget : formEditorWidgets )
1555 formEditorWidget->setConstraintStatus( constraint, description, err, result );
1556 if ( formEditorWidget->editorWidget() != eww )
1558 formEditorWidget->editorWidget()->updateConstraint( result, err );
1565 QList<QgsEditorWidgetWrapper *> wDeps;
1569 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1572 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1577 if ( name != ewwName )
1584 for (
const QString &colName : referencedColumns )
1586 if ( name.compare( colName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
1588 wDeps.append( eww );
1601 return setupRelationWidgetWrapper( QString(), rel, context );
1606 QgsRelationWidgetWrapper *rww =
new QgsRelationWidgetWrapper( relationWidgetTypeId, mLayer, rel,
nullptr,
this );
1607 const QVariantMap config = mLayer->editFormConfig().widgetConfig( rel.
id() );
1614QToolButton *QgsAttributeForm::createCommentInfoButton( QWidget *labelWidget )
1616 QToolButton *infoButton =
new QToolButton( labelWidget );
1617 infoButton->setIcon(
QgsApplication::getThemeIcon( u
"/mIndicatorFieldComment.svg"_s, QgsApplication::palette().color( QPalette::WindowText ) ) );
1618 infoButton->setFocusPolicy( Qt::NoFocus );
1620 infoButton->setAutoRaise(
true );
1621 infoButton->setStyleSheet(
1624 " background-color: transparent; "
1629 connect( infoButton, &QToolButton::clicked, labelWidget, [labelWidget]() { QToolTip::showText( QCursor::pos(), labelWidget->toolTip(), labelWidget ); } );
1631 infoButton->setVisible(
false );
1635void QgsAttributeForm::preventFeatureRefresh()
1637 mPreventFeatureRefresh =
true;
1642 if ( mPreventFeatureRefresh || mLayer->isEditable() || !mFeature.isValid() )
1647 if ( !mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( mFeature ) )
1656 if ( mContext.parentFormFeature().isValid() )
1658 QgsFeature parentFormFeature = mContext.parentFormFeature();
1660 mContext.setParentFormFeature( parentFormFeature );
1663 updateValuesDependenciesParent();
1677 return mNeedsGeometry;
1680void QgsAttributeForm::synchronizeState()
1682 bool isEditable =
false;
1703 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1705 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1708 const QList<QgsAttributeFormEditorWidget *> formWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1710 for ( QgsAttributeFormEditorWidget *formWidget : formWidgets )
1712 formWidget->setConstraintResultVisible( isEditable );
1717 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1718 ww->setEnabled( enabled );
1724 ww->setEnabled( isEditable );
1734 if ( mConstraintsFailMessageBarItem )
1736 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1738 mConstraintsFailMessageBarItem =
new QgsMessageBarItem( tr(
"Multi edit mode requires at least one selected feature." ),
Qgis::MessageLevel::Info, -1 );
1739 mMessageBar->pushItem( mConstraintsFailMessageBarItem );
1743 QStringList invalidFields, descriptions;
1744 mValidConstraints = currentFormValidHardConstraints( invalidFields, descriptions );
1748 if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
1750 mConstraintsFailMessageBarItem
1751 =
new QgsMessageBarItem( tr(
"Changes to this form will not be saved. %n field(s) don't meet their constraints.",
"invalid fields", invalidFields.size() ),
Qgis::MessageLevel::Warning, -1 );
1752 mMessageBar->pushItem( mConstraintsFailMessageBarItem );
1754 else if ( mValidConstraints && mConstraintsFailMessageBarItem )
1756 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1757 mConstraintsFailMessageBarItem =
nullptr;
1760 else if ( mConstraintsFailMessageBarItem )
1762 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1763 mConstraintsFailMessageBarItem =
nullptr;
1766 isEditable = isEditable & mValidConstraints;
1771 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1774 okButton->setEnabled( isEditable );
1778void QgsAttributeForm::init()
1780 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1783 QWidget *formWidget =
nullptr;
1784 mNeedsGeometry =
false;
1786 bool buttonBoxVisible =
true;
1790 buttonBoxVisible = mButtonBox->isVisible();
1792 mButtonBox =
nullptr;
1795 if ( mSearchButtonBox )
1797 delete mSearchButtonBox;
1798 mSearchButtonBox =
nullptr;
1801 qDeleteAll( mWidgets );
1804 while ( QWidget *w = this->findChild<QWidget *>() )
1810 QVBoxLayout *vl =
new QVBoxLayout();
1811 vl->setContentsMargins( 0, 0, 0, 0 );
1812 mMessageBar =
new QgsMessageBar(
this );
1813 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1814 vl->addWidget( mMessageBar );
1819 QGridLayout *layout =
new QGridLayout();
1820 QWidget *container =
new QWidget();
1821 container->setLayout( layout );
1822 vl->addWidget( container );
1824 mFormEditorWidgets.clear();
1825 mFormWidgets.clear();
1828 setContentsMargins( 0, 0, 0, 0 );
1833 QgsDebugMsgLevel( u
"loading form: %1"_s.arg( mLayer->editFormConfig().uiForm() ), 2 );
1834 const QString path = mLayer->editFormConfig().uiForm();
1836 if ( file && file->open( QFile::ReadOnly ) )
1840 QFileInfo fi( file->fileName() );
1841 loader.setWorkingDirectory( fi.dir() );
1842 formWidget = loader.load( file,
this );
1845 formWidget->setWindowFlags( Qt::Widget );
1846 layout->addWidget( formWidget );
1849 mButtonBox = findChild<QDialogButtonBox *>();
1852 formWidget->installEventFilter(
this );
1857 QgsTabWidget *tabWidget =
nullptr;
1864 int columnCount = 1;
1865 bool hasRootFields =
false;
1866 bool addSpacer =
true;
1868 const QList<QgsAttributeEditorElement *> tabs = mLayer->editFormConfig().tabs();
1870 for ( QgsAttributeEditorElement *widgDef : tabs )
1874 QgsAttributeEditorContainer *containerDef =
dynamic_cast<QgsAttributeEditorContainer *
>( widgDef );
1875 if ( !containerDef )
1878 switch ( containerDef->
type() )
1882 tabWidget =
nullptr;
1883 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1884 if ( widgetInfo.labelStyle.overrideColor )
1886 if ( widgetInfo.labelStyle.color.isValid() )
1888 widgetInfo.widget->setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1891 if ( widgetInfo.labelStyle.overrideFont )
1893 widgetInfo.widget->setFont( widgetInfo.labelStyle.font );
1896 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1897 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1899 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1901 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1903 layout->setRowStretch( row, widgDef->verticalStretch() );
1908 if ( widgetInfo.expandingNeeded )
1911 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
1915 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
1921 registerContainerInformation(
new ContainerInformation(
1934 tabWidget =
nullptr;
1935 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1936 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1937 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1939 layout->setRowStretch( row, widgDef->verticalStretch() );
1944 if ( widgetInfo.expandingNeeded )
1947 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
1951 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
1954 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1956 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1961 registerContainerInformation(
new ContainerInformation(
1976 tabWidget =
new QgsTabWidget();
1977 layout->addWidget( tabWidget, row, column, 1, 2 );
1981 QWidget *tabPage =
new QWidget( tabWidget );
1982 tabWidget->addTab( tabPage, widgDef->name() );
1983 tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
1987 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1989 QGridLayout *tabPageLayout =
new QGridLayout();
1990 tabPage->setLayout( tabPageLayout );
1992 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1993 if ( widgetInfo.expandingNeeded )
1996 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
1998 tabPageLayout->addWidget( widgetInfo.widget );
2005 hasRootFields =
true;
2006 tabWidget =
nullptr;
2007 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
2008 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox();
2010 if ( widgetInfo.showLabel )
2012 if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
2014 collapsibleGroupBox->
setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2017 if ( widgetInfo.labelStyle.overrideFont )
2019 collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
2022 collapsibleGroupBox->setTitle( widgetInfo.labelText );
2025 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
2026 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
2027 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
2029 QVBoxLayout *
c =
new QVBoxLayout();
2030 c->addWidget( collapsibleGroupBox );
2031 layout->addLayout(
c, row, column, 1, 2 );
2033 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2034 layout->setRowStretch( row, widgDef->verticalStretch() );
2035 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
2036 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
2045 hasRootFields =
true;
2046 tabWidget =
nullptr;
2047 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
2048 QWidget *labelWidget =
new QWidget();
2049 QLabel *label =
new QLabel( widgetInfo.labelText, labelWidget );
2051 if ( widgetInfo.labelStyle.overrideColor )
2053 if ( widgetInfo.labelStyle.color.isValid() )
2055 label->setStyleSheet( u
"QLabel { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2059 if ( widgetInfo.labelStyle.overrideFont )
2061 label->setFont( widgetInfo.labelStyle.font );
2064 labelWidget->setToolTip( widgetInfo.toolTip );
2065 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2067 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2070 label->setBuddy( widgetInfo.widget );
2073 QToolButton *commentInfoButton = createCommentInfoButton( labelWidget );
2075 if ( !widgetInfo.hint.isEmpty() )
2076 commentInfoButton->setVisible(
true );
2079 if ( widgetInfo.widget
2080 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2081 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2082 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2085 QHBoxLayout *labelLayout =
new QHBoxLayout( labelWidget );
2086 labelLayout->addWidget( label );
2087 labelLayout->addWidget( commentInfoButton );
2088 labelLayout->setContentsMargins( 0, 0, 0, 0 );
2090 if ( !widgetInfo.showLabel )
2092 QVBoxLayout *
c =
new QVBoxLayout();
2093 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2094 c->addWidget( widgetInfo.widget );
2095 layout->addLayout(
c, row, column, 1, 2 );
2097 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2099 layout->setRowStretch( row, widgDef->verticalStretch() );
2102 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
2104 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
2109 else if ( widgetInfo.labelOnTop )
2111 QVBoxLayout *
c =
new QVBoxLayout();
2112 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2113 c->addWidget( labelWidget );
2114 c->addWidget( widgetInfo.widget );
2115 layout->addLayout(
c, row, column, 1, 2 );
2117 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2119 layout->setRowStretch( row, widgDef->verticalStretch() );
2122 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
2124 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
2131 const int widgetColumn = column + 1;
2132 layout->addWidget( labelWidget, row, column++ );
2133 layout->addWidget( widgetInfo.widget, row, column++ );
2135 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2137 layout->setRowStretch( row, widgDef->verticalStretch() );
2140 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( widgetColumn ) )
2142 layout->setColumnStretch( widgetColumn, widgDef->horizontalStretch() );
2149 const QgsAttributeEditorField *fieldElement {
static_cast<QgsAttributeEditorField *
>( widgDef ) };
2150 const int fieldIdx = fieldElement->
idx();
2151 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
2153 const QString fieldName { mLayer->fields().at( fieldIdx ).name() };
2154 label->setObjectName( fieldName );
2159 if ( property.isActive() )
2161 mLabelDataDefinedProperties[label] = property;
2164 labelWidget->setObjectName( fieldName );
2168 if ( property.isActive() )
2170 mCustomCommentDataDefinedProperties[labelWidget] = property;
2171 commentInfoButton->setVisible(
true );
2177 if ( property.isActive() )
2179 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2186 if ( column >= columnCount * 2 )
2193 if ( hasRootFields && addSpacer )
2195 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2196 layout->addItem( spacerItem, row, 0 );
2197 layout->setRowStretch( row, 1 );
2200 formWidget = container;
2209 formWidget =
new QWidget(
this );
2210 QGridLayout *gridLayout =
new QGridLayout( formWidget );
2211 formWidget->setLayout( gridLayout );
2216 QgsScrollArea *scrollArea =
new QgsScrollArea(
this );
2217 scrollArea->setWidget( formWidget );
2218 scrollArea->setWidgetResizable(
true );
2219 scrollArea->setFrameShape( QFrame::NoFrame );
2220 scrollArea->setFrameShadow( QFrame::Plain );
2221 scrollArea->setFocusProxy(
this );
2222 layout->addWidget( scrollArea );
2226 layout->addWidget( formWidget );
2231 const QgsFields fields = mLayer->fields();
2236 QSet<int> compositeHiddenFields;
2238 for (
const QgsRelation &rel : referencingRelations )
2243 const QList<QgsRelation::FieldPair> fieldPairs = rel.
fieldPairs();
2244 if ( fieldPairs.size() > 1 )
2246 for (
int i = 1; i < fieldPairs.size(); i++ )
2248 const int idx = fields.
lookupField( fieldPairs.at( i ).referencingField() );
2250 compositeHiddenFields.insert( idx );
2255 for (
const QgsField &field : fields )
2257 const QString fieldName = field.name();
2258 int idx = fields.lookupField( fieldName );
2262 if ( compositeHiddenFields.contains( idx ) )
2266 QString labelText = mLayer->attributeDisplayName( idx );
2267 labelText.replace(
'&',
"&&"_L1 );
2271 if ( widgetSetup.
type() ==
"Hidden"_L1 )
2274 bool labelOnTop = mLayer->editFormConfig().labelOnTop( idx );
2277 QWidget *labelWidget =
new QWidget();
2278 QLabel *label =
new QLabel( labelText, labelWidget );
2279 label->setObjectName( fieldName );
2284 QToolButton *commentInfoButton = createCommentInfoButton( labelWidget );
2286 if ( !field.customComment().isEmpty() || ( field.customComment().isNull() && !field.comment().isEmpty() ) )
2287 commentInfoButton->setVisible(
true );
2289 QSvgWidget *i =
new QSvgWidget();
2290 i->setFixedSize( 18, 18 );
2295 if ( property.isActive() )
2297 mLabelDataDefinedProperties[label] = property;
2300 labelWidget->setObjectName( fieldName );
2304 if ( property.isActive() )
2306 mCustomCommentDataDefinedProperties[labelWidget] = property;
2307 commentInfoButton->setVisible(
true );
2313 QWidget *w =
nullptr;
2316 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2318 mFormEditorWidgets.insert( idx, formWidget );
2319 mFormWidgets.append( formWidget );
2325 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2326 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2327 if ( rememberLastUsedValues.contains( idx ) )
2329 remember = rememberLastUsedValues[idx];
2334 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2335 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2336 rememberLastUsedValues[idx] = remember;
2337 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2343 label->setBuddy( eww->
widget() );
2348 if ( property.isActive() )
2350 mEditableDataDefinedProperties[formWidget] = property;
2356 w =
new QLabel( u
"<p style=\"color: red; font-style: italic;\">%1</p>"_s.arg( tr(
"Failed to create widget with type '%1'" ).arg( widgetSetup.
type() ) ) );
2361 w->setObjectName( field.name() );
2365 mWidgets.append( eww );
2366 mIconMap[eww->
widget()] = i;
2369 QHBoxLayout *labelLayout =
new QHBoxLayout( labelWidget );
2370 labelLayout->addWidget( label );
2371 labelLayout->addWidget( commentInfoButton );
2372 labelLayout->setContentsMargins( 0, 0, 0, 0 );
2376 gridLayout->addWidget( labelWidget, row++, 0, 1, 2 );
2377 gridLayout->addWidget( w, row++, 0, 1, 2 );
2378 gridLayout->addWidget( i, row++, 0, 1, 2 );
2382 int widgetColumn = 0;
2383 gridLayout->addWidget( labelWidget, row, widgetColumn++ );
2384 gridLayout->addWidget( w, row, widgetColumn++ );
2385 gridLayout->addWidget( i, row++, widgetColumn++ );
2390 for (
const QgsRelation &rel : relations )
2392 QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( u
"relation_editor"_s, rel, mContext );
2394 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2397 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox( rel.
name() );
2398 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
2399 collapsibleGroupBoxLayout->addWidget( formWidget );
2400 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
2402 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
2404 mWidgets.append( rww );
2405 mFormWidgets.append( formWidget );
2410 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2411 gridLayout->addItem( spacerItem, row, 0 );
2412 gridLayout->setRowStretch( row, 1 );
2418 updateFieldDependencies();
2422 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
2423 mButtonBox->setObjectName( u
"buttonBox"_s );
2424 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2426 mButtonBox->setVisible( buttonBoxVisible );
2428 if ( !mSearchButtonBox )
2430 mSearchButtonBox =
new QWidget();
2431 QHBoxLayout *boxLayout =
new QHBoxLayout();
2432 boxLayout->setContentsMargins( 0, 0, 0, 0 );
2433 mSearchButtonBox->setLayout( boxLayout );
2434 mSearchButtonBox->setObjectName( u
"searchButtonBox"_s );
2436 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
2438 boxLayout->addWidget( clearButton );
2439 boxLayout->addStretch( 1 );
2441 QPushButton *flashButton =
new QPushButton();
2442 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2443 flashButton->setText( tr(
"&Flash Features" ) );
2444 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
2445 boxLayout->addWidget( flashButton );
2447 QPushButton *openAttributeTableButton =
new QPushButton();
2448 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2449 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
2450 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
2452 boxLayout->addWidget( openAttributeTableButton );
2454 QPushButton *zoomButton =
new QPushButton();
2455 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2456 zoomButton->setText( tr(
"&Zoom to Features" ) );
2457 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
2458 boxLayout->addWidget( zoomButton );
2460 QToolButton *selectButton =
new QToolButton();
2461 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2462 selectButton->setText( tr(
"&Select Features" ) );
2464 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
2465 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
2466 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
2467 QMenu *selectMenu =
new QMenu( selectButton );
2468 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
2470 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
2471 selectMenu->addAction( selectAction );
2472 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
2474 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
2475 selectMenu->addAction( addSelectAction );
2476 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
2478 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
2479 selectMenu->addAction( deselectAction );
2480 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
2482 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
2483 selectMenu->addAction( filterSelectAction );
2484 selectButton->setMenu( selectMenu );
2485 boxLayout->addWidget( selectButton );
2489 QToolButton *filterButton =
new QToolButton();
2490 filterButton->setText( tr(
"Filter Features" ) );
2491 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
2492 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2493 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
2494 QMenu *filterMenu =
new QMenu( filterButton );
2495 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
2496 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
2497 filterMenu->addAction( filterAndAction );
2498 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
2499 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
2500 filterMenu->addAction( filterOrAction );
2501 filterButton->setMenu( filterMenu );
2502 boxLayout->addWidget( filterButton );
2506 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
2508 closeButton->setShortcut( Qt::Key_Escape );
2509 boxLayout->addWidget( closeButton );
2512 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2530 const auto constMInterfaces = mInterfaces;
2531 for ( QgsAttributeFormInterface *iface : constMInterfaces )
2541 QApplication::restoreOverrideCursor();
2544void QgsAttributeForm::cleanPython()
2546 if ( !mPyFormVarName.isNull() )
2548 QString expr = u
"if '%1' in locals(): del %1\n"_s.arg( mPyFormVarName );
2553void QgsAttributeForm::initPython()
2564 mMessageBar->pushMessage( tr(
"Security warning" ), tr(
"The attribute form contains an embedded script which has been denied execution." ),
Qgis::MessageLevel::Warning );
2568 QString initFunction = mLayer->editFormConfig().initFunction();
2569 QString initFilePath = mLayer->editFormConfig().initFilePath();
2572 switch ( mLayer->editFormConfig().initCodeSource() )
2575 if ( !initFilePath.isEmpty() )
2579 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2582 QTextStream inf( inputFile );
2583 initCode = inf.readAll();
2588 QgsLogger::warning( u
"The external python file path %1 could not be opened!"_s.arg( initFilePath ) );
2598 initCode = mLayer->editFormConfig().initCode();
2599 if ( initCode.isEmpty() )
2612 if ( !initCode.isEmpty() )
2620 mMessageBar->pushMessage( QString(), tr(
"Python macro could not be run due to missing permissions." ),
Qgis::MessageLevel::Warning );
2628 if (
QgsPythonRunner::eval( u
"len(inspect.getfullargspec(%1)[0])"_s.arg( initFunction ), numArgs ) )
2630 static int sFormId = 0;
2631 mPyFormVarName = u
"_qgis_featureform_%1_%2"_s.arg( mFormNr ).arg( sFormId++ );
2633 QString form = u
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )"_s.arg( mPyFormVarName ).arg( ( quint64 )
this );
2637 QgsDebugMsgLevel( u
"running featureForm init: %1"_s.arg( mPyFormVarName ), 2 );
2640 if ( numArgs ==
"3"_L1 )
2642 addInterface(
new QgsAttributeFormLegacyInterface( initFunction, mPyFormVarName,
this ) );
2649 tr(
"The python init function (<code>%1</code>) does not accept three arguments as expected!<br>Please check the function name in the <b>Fields</b> tab of the layer properties." ).arg( initFunction )
2653 QString expr = QString(
"%1(%2)" )
2654 .arg( mLayer->editFormInit() )
2655 .arg( mPyFormVarName );
2656 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2666 msgBox.setText( tr(
"The python init function (<code>%1</code>) could not be found!<br>Please check the function name in the <b>Fields</b> tab of the layer properties." ).arg( initFunction ) );
2674 WidgetInfo newWidgetInfo;
2676 newWidgetInfo.labelStyle = widgetDef->
labelStyle();
2678 switch ( widgetDef->
type() )
2682 const QgsAttributeEditorAction *elementDef =
dynamic_cast<const QgsAttributeEditorAction *
>( widgetDef );
2686 QgsActionWidgetWrapper *actionWrapper =
new QgsActionWidgetWrapper( mLayer,
nullptr,
this, mMessageBar );
2690 mWidgets.append( actionWrapper );
2691 newWidgetInfo.widget = actionWrapper->
widget();
2692 newWidgetInfo.showLabel =
false;
2699 const QgsAttributeEditorField *fieldDef =
dynamic_cast<const QgsAttributeEditorField *
>( widgetDef );
2703 const QgsFields fields = vl->
fields();
2705 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2710 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2711 mFormEditorWidgets.insert( fldIdx, formWidget );
2712 mFormWidgets.append( formWidget );
2718 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2719 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2720 if ( rememberLastUsedValues.contains( fldIdx ) )
2722 remember = rememberLastUsedValues[fldIdx];
2726 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2727 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2728 rememberLastUsedValues[idx] = remember;
2729 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2735 newWidgetInfo.widget = formWidget;
2736 mWidgets.append( eww );
2738 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2740 newWidgetInfo.hint = customComment.isNull() ? fields.
at( fldIdx ).
comment() : customComment;
2742 newWidgetInfo.labelOnTop = mLayer->editFormConfig().labelOnTop( fldIdx );
2743 newWidgetInfo.labelText = mLayer->attributeDisplayName( fldIdx );
2744 newWidgetInfo.labelText.replace(
'&',
"&&"_L1 );
2750 QgsDebugError( u
"Attribute form references unknown field '%1' on layer '%2' - skipping widget"_s.arg( fieldDef->
name(), mLayer ? mLayer->name() : QString() ) );
2751 newWidgetInfo.labelOnTop =
false;
2752 newWidgetInfo.labelText = fieldDef->
name();
2753 newWidgetInfo.labelText.replace(
'&',
"&&"_L1 );
2754 newWidgetInfo.toolTip.clear();
2757 newWidgetInfo.showLabel = widgetDef->
showLabel();
2764 const QgsAttributeEditorRelation *relDef =
static_cast<const QgsAttributeEditorRelation *
>( widgetDef );
2768 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2778 mWidgets.append( rww );
2779 mFormWidgets.append( formWidget );
2781 newWidgetInfo.widget = formWidget;
2782 newWidgetInfo.showLabel = relDef->
showLabel();
2783 newWidgetInfo.labelText = relDef->
label();
2784 if ( newWidgetInfo.labelText.isEmpty() )
2786 newWidgetInfo.labelOnTop =
true;
2792 const QgsAttributeEditorContainer *container =
dynamic_cast<const QgsAttributeEditorContainer *
>( widgetDef );
2798 if ( columnCount <= 0 )
2802 QWidget *myContainer =
nullptr;
2803 bool removeLayoutMargin =
false;
2804 switch ( container->
type() )
2808 QgsCollapsibleGroupBoxBasic *groupBox =
new QgsCollapsibleGroupBoxBasic();
2809 widgetName = u
"QGroupBox"_s;
2812 groupBox->setTitle( container->
name() );
2813 if ( newWidgetInfo.labelStyle.overrideColor )
2815 if ( newWidgetInfo.labelStyle.color.isValid() )
2817 groupBox->
setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2820 if ( newWidgetInfo.labelStyle.overrideFont )
2822 groupBox->setFont( newWidgetInfo.labelStyle.font );
2825 myContainer = groupBox;
2826 newWidgetInfo.widget = myContainer;
2833 QWidget *rowWidget =
new QWidget();
2834 widgetName = u
"Row"_s;
2835 myContainer = rowWidget;
2836 newWidgetInfo.widget = myContainer;
2837 removeLayoutMargin =
true;
2838 columnCount = container->
children().size();
2844 myContainer =
new QWidget();
2846 QgsScrollArea *scrollArea =
new QgsScrollArea( parent );
2848 scrollArea->setWidget( myContainer );
2849 scrollArea->setWidgetResizable(
true );
2850 scrollArea->setFrameShape( QFrame::NoFrame );
2851 widgetName = u
"QScrollArea QWidget"_s;
2853 newWidgetInfo.widget = scrollArea;
2860 QString style { u
"background-color: %1;"_s.arg( container->
backgroundColor().name() ) };
2861 newWidgetInfo.widget->setStyleSheet( style );
2864 QGridLayout *gbLayout =
new QGridLayout();
2865 if ( removeLayoutMargin )
2866 gbLayout->setContentsMargins( 0, 0, 0, 0 );
2867 myContainer->setLayout( gbLayout );
2871 bool addSpacer =
true;
2873 const QList<QgsAttributeEditorElement *> children = container->
children();
2875 for ( QgsAttributeEditorElement *childDef : children )
2877 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2881 QgsAttributeEditorContainer *containerDef =
static_cast<QgsAttributeEditorContainer *
>( childDef );
2884 registerContainerInformation(
new ContainerInformation(
2891 if ( childDef->verticalStretch() == 0 )
2893 if ( widgetInfo.expandingNeeded )
2895 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
2899 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
2905 int widgetColumn = column;
2907 if ( widgetInfo.labelText.isNull() || !widgetInfo.showLabel )
2909 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2910 widgetColumn = column + 1;
2915 QWidget *labelWidget =
new QWidget();
2916 QLabel *mypLabel =
new QLabel( widgetInfo.labelText, labelWidget );
2918 if ( widgetInfo.labelStyle.overrideColor )
2920 if ( widgetInfo.labelStyle.color.isValid() )
2922 mypLabel->setStyleSheet( u
"QLabel { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2926 if ( widgetInfo.labelStyle.overrideFont )
2928 mypLabel->setFont( widgetInfo.labelStyle.font );
2932 QToolButton *commentInfoButton = createCommentInfoButton( labelWidget );
2934 if ( !widgetInfo.hint.isEmpty() )
2935 commentInfoButton->setVisible(
true );
2940 const QgsAttributeEditorField *fieldDef {
static_cast<QgsAttributeEditorField *
>( childDef ) };
2941 const QgsFields fields = vl->
fields();
2942 const int fldIdx = fieldDef->
idx();
2943 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2945 const QString fieldName { fields.
at( fldIdx ).
name() };
2946 mypLabel->setObjectName( fieldName );
2951 if ( property.isActive() )
2953 mLabelDataDefinedProperties[mypLabel] = property;
2959 if ( property.isActive() )
2961 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2964 labelWidget->setObjectName( fieldName );
2968 if ( property.isActive() )
2970 mCustomCommentDataDefinedProperties[labelWidget] = property;
2976 labelWidget->setToolTip( widgetInfo.toolTip );
2977 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2979 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2982 mypLabel->setBuddy( widgetInfo.widget );
2984 QHBoxLayout *labelLayout =
new QHBoxLayout( labelWidget );
2985 labelLayout->addWidget( mypLabel );
2986 labelLayout->addWidget( commentInfoButton );
2987 labelLayout->setContentsMargins( 0, 0, 0, 0 );
2989 if ( widgetInfo.labelOnTop )
2991 widgetColumn = column + 1;
2992 QVBoxLayout *
c =
new QVBoxLayout();
2993 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2994 c->addWidget( labelWidget );
2995 c->layout()->addWidget( widgetInfo.widget );
2996 gbLayout->addLayout(
c, row, column, 1, 2 );
3001 widgetColumn = column + 1;
3002 gbLayout->addWidget( labelWidget, row, column++ );
3003 gbLayout->addWidget( widgetInfo.widget, row, column++ );
3007 const int childHorizontalStretch = childDef->horizontalStretch();
3008 const int existingColumnStretch = gbLayout->columnStretch( widgetColumn );
3009 if ( childHorizontalStretch > 0 && childHorizontalStretch > existingColumnStretch )
3011 gbLayout->setColumnStretch( widgetColumn, childHorizontalStretch );
3014 if ( childDef->verticalStretch() > 0 && childDef->verticalStretch() > gbLayout->rowStretch( row ) )
3016 gbLayout->setRowStretch( row, childDef->verticalStretch() );
3019 if ( column >= columnCount * 2 )
3025 if ( widgetInfo.widget
3026 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
3027 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
3028 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
3032 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
3038 QWidget *spacer =
new QWidget();
3039 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
3040 gbLayout->addWidget( spacer, ++row, 0 );
3041 gbLayout->setRowStretch( row, 1 );
3044 newWidgetInfo.labelText = QString();
3045 newWidgetInfo.labelOnTop =
true;
3046 newWidgetInfo.showLabel = widgetDef->
showLabel();
3047 newWidgetInfo.expandingNeeded |= !addSpacer;
3053 const QgsAttributeEditorQmlElement *elementDef =
static_cast<const QgsAttributeEditorQmlElement *
>( widgetDef );
3054 QgsQmlWidgetWrapper *qmlWrapper =
new QgsQmlWidgetWrapper( mLayer,
nullptr,
this );
3059 mWidgets.append( qmlWrapper );
3061 newWidgetInfo.widget = qmlWrapper->
widget();
3062 newWidgetInfo.labelText = elementDef->
name();
3063 newWidgetInfo.labelOnTop =
true;
3064 newWidgetInfo.showLabel = widgetDef->
showLabel();
3070 const QgsAttributeEditorHtmlElement *elementDef =
static_cast<const QgsAttributeEditorHtmlElement *
>( widgetDef );
3072 QgsHtmlWidgetWrapper *htmlWrapper =
new QgsHtmlWidgetWrapper( mLayer,
nullptr,
this );
3076 mWidgets.append( htmlWrapper );
3078 newWidgetInfo.widget = htmlWrapper->
widget();
3079 newWidgetInfo.labelText = elementDef->
name();
3080 newWidgetInfo.labelOnTop =
true;
3081 newWidgetInfo.showLabel = widgetDef->
showLabel();
3088 const QgsAttributeEditorTextElement *elementDef =
static_cast<const QgsAttributeEditorTextElement *
>( widgetDef );
3090 QgsTextWidgetWrapper *textWrapper =
new QgsTextWidgetWrapper( mLayer,
nullptr,
this );
3094 mWidgets.append( textWrapper );
3096 newWidgetInfo.widget = textWrapper->
widget();
3097 newWidgetInfo.labelText = elementDef->
name();
3098 newWidgetInfo.labelOnTop =
false;
3099 newWidgetInfo.showLabel = widgetDef->
showLabel();
3106 const QgsAttributeEditorSpacerElement *elementDef =
static_cast<const QgsAttributeEditorSpacerElement *
>( widgetDef );
3107 QgsSpacerWidgetWrapper *spacerWrapper =
new QgsSpacerWidgetWrapper( mLayer,
nullptr,
this );
3110 mWidgets.append( spacerWrapper );
3112 newWidgetInfo.widget = spacerWrapper->
widget();
3113 newWidgetInfo.labelOnTop =
false;
3114 newWidgetInfo.showLabel =
false;
3119 QgsDebugError( u
"Unknown attribute editor widget type encountered..."_s );
3123 return newWidgetInfo;
3126void QgsAttributeForm::createWrappers()
3128 QList<QWidget *> myWidgets = findChildren<QWidget *>();
3129 const QList<QgsField> fields = mLayer->fields().
toList();
3131 const auto constMyWidgets = myWidgets;
3132 for ( QWidget *myWidget : constMyWidgets )
3135 QVariant vRel = myWidget->property(
"qgisRelation" );
3136 if ( vRel.isValid() )
3139 QgsRelation relation = relMgr->
relation( vRel.toString() );
3142 QgsRelationWidgetWrapper *rww =
new QgsRelationWidgetWrapper( mLayer, relation, myWidget,
this );
3143 rww->
setConfig( mLayer->editFormConfig().widgetConfig( relation.
id() ) );
3146 mWidgets.append( rww );
3151 const auto constFields = fields;
3152 for (
const QgsField &field : constFields )
3154 if ( field.name() == myWidget->objectName() )
3156 int idx = mLayer->fields().lookupField( field.name() );
3159 mWidgets.append( eww );
3166void QgsAttributeForm::afterWidgetInit()
3168 bool isFirstEww =
true;
3170 const auto constMWidgets = mWidgets;
3171 for ( QgsWidgetWrapper *ww : constMWidgets )
3173 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3179 setFocusProxy( eww->
widget() );
3188 QgsRelationWidgetWrapper *relationWidgetWrapper = qobject_cast<QgsRelationWidgetWrapper *>( ww );
3189 if ( relationWidgetWrapper )
3202 if ( e->type() == QEvent::KeyPress )
3204 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
3205 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
3216void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet<int> &mixedValueFields, QHash<int, QVariant> &fieldSharedValues )
const
3218 mixedValueFields.clear();
3219 fieldSharedValues.clear();
3225 for (
int i = 0; i < mLayer->
fields().count(); ++i )
3227 if ( mixedValueFields.contains( i ) )
3232 fieldSharedValues[i] = f.
attribute( i );
3236 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
3238 fieldSharedValues.remove( i );
3239 mixedValueFields.insert( i );
3245 if ( mixedValueFields.count() == mLayer->fields().count() )
3254void QgsAttributeForm::layerSelectionChanged()
3268 resetMultiEdit(
true );
3275 mIsSettingMultiEditFeatures =
true;
3276 mMultiEditFeatureIds = fids;
3278 if ( fids.isEmpty() )
3281 QMultiMap<int, QgsAttributeFormEditorWidget *>::const_iterator wIt = mFormEditorWidgets.constBegin();
3282 for ( ; wIt != mFormEditorWidgets.constEnd(); ++wIt )
3284 wIt.value()->initialize( QVariant() );
3286 mIsSettingMultiEditFeatures =
false;
3293 QSet<int> mixedValueFields;
3294 QHash<int, QVariant> fieldSharedValues;
3295 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
3298 fit = mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( *fids.constBegin() ) );
3304 if ( mCurrentFormFeature.id() != firstFeature.
id() )
3309 const auto constMixedValueFields = mixedValueFields;
3310 for (
int fieldIndex : std::as_const( mixedValueFields ) )
3312 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( fieldIndex );
3313 if ( formEditorWidgets.isEmpty() )
3316 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3317 QVariantList additionalFieldValues;
3318 for (
const QString &additionalField : additionalFields )
3319 additionalFieldValues << firstFeature.
attribute( additionalField );
3322 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
3324 QHash<int, QVariant>::const_iterator sharedValueIt = fieldSharedValues.constBegin();
3325 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
3327 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( sharedValueIt.key() );
3328 if ( formEditorWidgets.isEmpty() )
3332 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3333 for (
const QString &additionalField : additionalFields )
3335 int index = mLayer->fields().indexFromName( additionalField );
3336 if ( constMixedValueFields.contains( index ) )
3343 QVariantList additionalFieldValues;
3346 for (
const QString &additionalField : additionalFields )
3347 additionalFieldValues << firstFeature.
attribute( additionalField );
3349 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
3353 for (
const QString &additionalField : additionalFields )
3355 int index = mLayer->fields().indexFromName( additionalField );
3356 Q_ASSERT( fieldSharedValues.contains( index ) );
3357 additionalFieldValues << fieldSharedValues.value( index );
3360 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
3364 setMultiEditFeatureIdsRelations( fids );
3366 mIsSettingMultiEditFeatures =
false;
3371 if ( mOwnsMessageBar )
3373 mOwnsMessageBar =
false;
3374 mMessageBar = messageBar;
3384 QStringList filters;
3387 QString filter = widget->currentFilterExpression();
3388 if ( !filter.isNull() )
3389 filters <<
'(' + filter +
')';
3392 return filters.join(
" AND "_L1 );
3397 mExtraContextScope.reset( extraScope );
3402 const bool newVisibility = expression.
evaluate( expressionContext ).toBool();
3412 widget->setVisible( newVisibility );
3415 isVisible = newVisibility;
3418 const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
3420 if ( collapsedExpression.isValid() && !collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
3422 if ( QgsCollapsibleGroupBoxBasic * collapsibleGroupBox { qobject_cast<QgsCollapsibleGroupBoxBasic *>( widget ) } )
3424 collapsibleGroupBox->
setCollapsed( newCollapsedState );
3425 isCollapsed = newCollapsedState;
3430void QgsAttributeForm::updateJoinedFields(
const QgsEditorWidgetWrapper &eww )
3435 QgsFeature formFeature;
3439 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
3442 const QString hint = tr(
"No feature joined" );
3443 const auto constInfos = infos;
3444 for (
const QgsVectorLayerJoinInfo *info : constInfos )
3446 if ( !info->isDynamicFormEnabled() )
3449 QgsFeature joinFeature = mLayer->joinBuffer()->joinedFeatureOf( info, formFeature );
3451 mJoinedFeatures[info] = joinFeature;
3453 if ( info->hasSubset() )
3457 const auto constSubsetNames = subsetNames;
3458 for (
const QString &field : constSubsetNames )
3460 QString prefixedName = info->prefixedFieldName( field );
3462 QString hintText = hint;
3475 const QgsFields joinFields = joinFeature.
fields();
3476 for (
const QgsField &field : joinFields )
3478 QString prefixedName = info->prefixedFieldName( field );
3480 QString hintText = hint;
3494bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
3500void QgsAttributeForm::updateFieldDependencies()
3502 mDefaultValueDependencies.clear();
3503 mVirtualFieldsDependencies.clear();
3504 mRelatedLayerFieldsDependencies.clear();
3505 mParentDependencies.clear();
3508 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3510 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3514 updateFieldDependenciesParent( eww );
3515 updateFieldDependenciesDefaultValue( eww );
3516 updateFieldDependenciesVirtualFields( eww );
3517 updateRelatedLayerFieldsDependencies( eww );
3521void QgsAttributeForm::updateFieldDependenciesDefaultValue( QgsEditorWidgetWrapper *eww )
3525 if ( exp.needsGeometry() )
3526 mNeedsGeometry =
true;
3531 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3533 for (
const int id : allAttributeIds )
3535 mDefaultValueDependencies.insertMulti(
id, eww );
3541 const QSet<QString> referencedColumns = exp.referencedColumns();
3542 for (
const QString &referencedColumn : referencedColumns )
3544 mDefaultValueDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3549void QgsAttributeForm::updateFieldDependenciesVirtualFields( QgsEditorWidgetWrapper *eww )
3552 if ( expressionField.isEmpty() )
3555 QgsExpression exp( expressionField );
3557 if ( exp.needsGeometry() )
3558 mNeedsGeometry =
true;
3563 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3565 for (
const int id : allAttributeIds )
3567 mVirtualFieldsDependencies.insertMulti(
id, eww );
3573 const QSet<QString> referencedColumns = exp.referencedColumns();
3574 for (
const QString &referencedColumn : referencedColumns )
3576 mVirtualFieldsDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3581void QgsAttributeForm::updateRelatedLayerFieldsDependencies( QgsEditorWidgetWrapper *eww )
3586 if ( expressionField.contains( u
"relation_aggregate"_s ) || expressionField.contains( u
"get_features"_s ) )
3587 mRelatedLayerFieldsDependencies.insert( eww );
3591 mRelatedLayerFieldsDependencies.clear();
3593 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3595 QgsEditorWidgetWrapper *editorWidgetWrapper = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3596 if ( !editorWidgetWrapper )
3599 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
3604void QgsAttributeForm::updateFieldDependenciesParent( QgsEditorWidgetWrapper *eww )
3609 const QSet<QString> referencedVariablesAndFunctions = expression.referencedVariables() + expression.referencedFunctions();
3610 for (
const QString &referenced : referencedVariablesAndFunctions )
3612 if ( referenced.startsWith(
"current_parent"_L1 ) )
3614 mParentDependencies.insert( eww );
3621void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
3623 for ( QgsAttributeFormWidget *formWidget : mFormWidgets )
3625 QgsAttributeFormRelationEditorWidget *relationEditorWidget =
dynamic_cast<QgsAttributeFormRelationEditorWidget *
>( formWidget );
3626 if ( !relationEditorWidget )
3633void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww )
3639 mIconMap[eww->
widget()]->hide();
3641 if ( !eww->
widget()->isEnabled() && mLayer->isEditable() )
3646 const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( eww->
fieldIdx(), mLayer->fields(), srcFieldIndex );
3653 const QString file = u
"/mIconJoinNotEditable.svg"_s;
3654 const QString tooltip = tr(
"Join settings do not allow editing" );
3655 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3659 const QString file = u
"mIconJoinHasNotUpsertOnEdit.svg"_s;
3660 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
3661 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3665 const QString file = u
"/mIconJoinedLayerNotEditable.svg"_s;
3666 const QString tooltip = tr(
"Joined layer is not toggled editable" );
3667 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3673void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
3676 sw->setToolTip( tooltip );
3683 QgsDebugMsgLevel( u
"reuseAllLastValues: %1"_s.arg( reuseAllLastValues ), 2 );
3687 for (
int idx = 0; idx < fields.
count(); ++idx )
3689 if ( attributes.contains( idx ) )
3691 initialAttributeValues.insert( idx, attributes.value( idx ) );
3695 const QVariant lastUsedValuesVariant =
layer->property(
"AttributeFormLastUsedValues" );
3697 if ( lastUsedValues.contains( idx ) &&
layer->dataProvider() &&
layer->dataProvider()->defaultValueClause( idx ) != lastUsedValues[idx] )
3699 initialAttributeValues.insert( idx, lastUsedValues[idx] );
@ Trusted
The project has been determined by the user as trusted.
@ Row
A row of editors (horizontal layout).
AttributeFormReuseLastValuePolicy
Attribute form policy for reusing last entered values.
@ AllowedDefaultOn
Reuse of last values allowed and enabled by default.
@ NotAllowed
Reuse of last values not allowed.
@ File
Load the Python code from an external file.
@ Environment
Use the Python code available in the Python environment.
@ NoSource
Do not use Python code at all.
@ Dialog
Use the Python code provided in the dialog.
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
@ UiFile
Load a .ui file for the layout. Needs to be configured.
@ Warning
Warning message.
@ Info
Information message.
@ Success
Used for reporting a successful operation.
@ Normal
A normal relation.
@ Join
Field originates from a joined layer.
@ Action
A layer action element.
@ QmlElement
A QML element.
@ HtmlElement
A HTML element.
@ TextElement
A text element.
@ SpacerElement
A spacer element.
SelectBehavior
Specifies how a selection should be applied.
@ SetSelection
Set selection, removing any existing selection.
@ AddToSelection
Add selection to current selection.
@ IntersectSelection
Modify current selection to include only select features which match.
@ RemoveFromSelection
Remove from current selection.
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
const QgsAction & action(const QgsVectorLayer *layer) const
Returns the (possibly lazy loaded) action for the given layer.
QgsOptionalExpression visibilityExpression() const
The visibility expression is used in the attribute form to show or hide this container based on an ex...
QgsOptionalExpression collapsedExpression() const
The collapsed expression is used in the attribute form to set the collapsed status of the group box c...
bool collapsed() const
For group box containers returns true if this group box is collapsed.
Qgis::AttributeEditorContainerType type() const
Returns the container type.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
QColor backgroundColor() const
Returns the background color of the container.
int columnCount() const
Gets the number of columns in this group.
Contains context information for attribute editor widgets.
FormMode formMode() const
Returns the form mode.
@ Embed
A form was embedded as a widget on another form.
@ SearchMode
Form values are used for searching/filtering the layer.
@ FixAttributeMode
Fix feature mode, for modifying the feature attributes without saving. The updated feature is availab...
@ IdentifyMode
Identify the feature.
@ SingleEditMode
Single edit mode, for editing a single feature.
@ AggregateSearchMode
Form is in aggregate search mode, show each widget in this mode.
@ MultiEditMode
Multi edit mode, for editing fields of multiple features at once.
@ PreviewMode
Preview mode, for previewing attribute configurations.
void setAttributeFormMode(const Mode &attributeFormMode)
Set attributeFormMode for the edited form.
An abstract base class for any elements of a drag and drop form.
LabelStyle labelStyle() const
Returns the label style.
Qgis::AttributeEditorType type() const
The type of this element.
bool showLabel() const
Controls if this element should be labeled with a title (field, relation or groupname).
QString name() const
Returns the name of this element.
int idx() const
Returns the index of the field.
QString htmlCode() const
The Html code that will be represented within this widget.
QString qmlCode() const
The QML code that will be represented within this widget.
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
QVariantMap relationEditorConfiguration() const
Returns the relation editor widget configuration.
QVariant nmRelationId() const
Determines the relation id of the second relation involved in an N:M relation.
bool forceSuppressFormPopup() const
Determines the force suppress form popup status.
QString relationWidgetTypeId() const
Returns the current relation widget type id.
QString label() const
Determines the label of this element.
bool drawLine() const
Returns true if the spacer element will contain an horizontal line.
QString text() const
The Text that will be represented within this widget.
void setStyleSheet(const QString &style)
Overridden to prepare base call and avoid crash due to specific QT versions.
void setCollapsed(bool collapse)
Collapse or uncollapse this groupbox.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * parentFormScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current parent attribute form/tab...
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table form...
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QVariant evaluate()
Evaluate the feature and return the result.
bool isValid() const
Checks if this expression is valid.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
ConstraintOrigin
Origin of constraints.
@ ConstraintOriginNotSet
Constraint is not set.
@ ConstraintOriginLayer
Constraint was set by layer.
QString constraintExpression() const
Returns the constraint expression for the field, if set.
static QString fieldToolTipExtended(const QgsField &field, const QgsVectorLayer *layer, const QString &predefinedComment=QString())
Returns a HTML formatted tooltip string for a field, containing details like the field name,...
QString displayName() const
Returns the name to use when displaying this field.
QgsDefaultValue defaultValueDefinition
QgsFieldConstraints constraints
Container of fields for a vector layer.
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
Qgis::FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Q_INVOKABLE bool exists(int i) const
Returns if a field index is valid.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A geometry is the spatial representation of a feature.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
static bool allowExecutionOfEmbeddedScripts(QgsProject *project, QgsMessageBar *messageBar=nullptr)
Returns true if python embedded in a project is currently allowed to be loaded.
static void warning(const QString &msg)
Goes to qWarning.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
void editingStarted()
Emitted when editing on this layer has started.
A bar for displaying non-blocking messages to the user.
bool popWidget(QgsMessageBarItem *item)
Remove the specified item from the bar, and display the next most recent one in the stack.
QFile * localFile(const QString &filePathOrUrl)
Returns a QFile from a local file or to a temporary file previously fetched by the registry.
bool enabled() const
Check if this optional is enabled.
T data() const
Access the payload data.
static Qgis::ProjectTrustStatus checkUserTrust(QgsProject *project)
Returns the current trust status of the specified project.
QgsRelationManager * relationManager
static QgsProject * instance()
Returns the QgsProject singleton instance.
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement.
static bool eval(const QString &command, QString &result)
Eval a Python statement.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
QList< QgsRelation > referencingRelations(const QgsVectorLayer *layer=nullptr, int fieldIdx=-2) const
Gets all relations where the specified layer (and field) is the referencing part (i....
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
Qgis::RelationshipType type() const
Returns the type of the relation.
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryBool * settingsDigitizingReuseLastValues
Settings entry digitizing reuseLastValues.
bool isInvalidJSON() const
Returns whether the text edit widget contains Invalid JSON.
bool needsGeometry() const
Returns true if the widget needs feature geometry.
void setText(const QString &text)
Sets the text code to htmlCode.
void reinitWidget()
Clears the content and makes new initialization.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
bool isDynamicFormEnabled() const
Returns whether the form has to be dynamically updated with joined fields when a feature is being cre...
bool hasUpsertOnEdit() const
Returns whether a feature created on the target layer has to impact the joined layer by creating a ne...
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet).
static bool fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature, QgsVectorLayerUtils::FieldIsEditableFlags flags=QgsVectorLayerUtils::FieldIsEditableFlags())
Tests whether a field is editable for a particular feature.
static QgsFeature createFeature(const QgsVectorLayer *layer, const QgsGeometry &geometry=QgsGeometry(), const QgsAttributeMap &attributes=QgsAttributeMap(), QgsExpressionContext *context=nullptr)
Creates a new feature ready for insertion into a layer.
QFlags< FieldIsEditableFlag > FieldIsEditableFlags
@ IgnoreLayerEditability
Ignores the vector layer's editable state.
Represents a vector layer which manages a vector based dataset.
bool isEditable() const final
Returns true if the provider is in editing mode.
void beforeRemovingExpressionField(int idx)
Will be emitted, when an expression field is going to be deleted from this vector layer.
QString expressionField(int index) const
Returns the expression used for a given expression field.
Q_INVOKABLE void selectByExpression(const QString &expression, Qgis::SelectBehavior behavior=Qgis::SelectBehavior::SetSelection, QgsExpressionContext *context=nullptr)
Selects matching features using an expression.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
void beforeAddingExpressionField(const QString &fieldName)
Will be emitted, when an expression field is going to be added to this vector layer.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
void beforeModifiedCheck() const
Emitted when the layer is checked for modifications. Use for last-minute additions.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantEqual(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether they are equal, two NULL values are always treated a...
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
QMap< int, QVariant > QgsAttributeMap
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)