79#include "moc_qgsattributeform.cpp"
81using namespace Qt::StringLiterals;
83int QgsAttributeForm::sFormCounter = 0;
89 , mFormNr( sFormCounter++ )
90 , mEditCommandMessage( tr(
"Attributes changed" ) )
102 updateContainersVisibility();
104 updateEditableState();
110 qDeleteAll( mInterfaces );
137 mInterfaces.append( iface );
142 return mFeature.isValid() && mLayer->isEditable();
153 if ( mUnsavedMultiEditChanges )
156 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ), tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
157 if ( res == QMessageBox::Yes )
162 clearMultiEditMessages();
164 mUnsavedMultiEditChanges =
false;
220 w->setContext( newContext );
223 auto setRelationWidgetsVisible = [
this](
bool relationWidgetsVisible ) {
226 w->setVisible( relationWidgetsVisible );
233 setRelationWidgetsVisible(
true );
235 mSearchButtonBox->setVisible(
false );
239 setRelationWidgetsVisible(
true );
241 mSearchButtonBox->setVisible(
false );
245 setRelationWidgetsVisible(
true );
247 mSearchButtonBox->setVisible(
false );
251 setRelationWidgetsVisible(
true );
252 resetMultiEdit(
false );
254 mSearchButtonBox->setVisible(
false );
258 setRelationWidgetsVisible(
true );
259 mSearchButtonBox->setVisible(
true );
265 setRelationWidgetsVisible(
false );
266 mSearchButtonBox->setVisible(
false );
272 setRelationWidgetsVisible(
true );
275 mSearchButtonBox->setVisible(
false );
279 setRelationWidgetsVisible(
false );
281 mSearchButtonBox->setVisible(
false );
290 const auto constMWidgets = mWidgets;
305 QVariant mainValue = eww->
value();
307 additionalFieldValues[index] = value;
308 eww->
setValues( mainValue, additionalFieldValues );
317 mFeature.setGeometry( geometry );
322 mIsSettingFeature =
true;
340 mIsSettingFeature =
false;
341 const auto constMInterfaces = mInterfaces;
344 iface->featureChanged();
360 mIsSettingFeature =
false;
363bool QgsAttributeForm::saveEdits( QString *error )
366 bool changedLayer =
false;
371 bool doUpdate =
false;
380 QgsAttributes src = mFeature.attributes();
381 QgsAttributes dst = mFeature.attributes();
383 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
385 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
389 QgsTextEditWrapper *textEdit = qobject_cast<QgsTextEditWrapper *>( eww );
393 *error = tr(
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
396 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
397 QVariantList srcVars = QVariantList() << eww->
value();
398 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
402 for (
const QString &fieldName : additionalFields )
406 dstVars << dst.at( idx );
410 Q_ASSERT( dstVars.count() == srcVars.count() );
412 for (
int i = 0; i < dstVars.count(); i++ )
414 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
416 dst[fieldIndexes[i]] = srcVars[i];
426 const auto constMInterfaces = mInterfaces;
427 for ( QgsAttributeFormInterface *iface : constMInterfaces )
429 if ( !iface->acceptChanges( updatedFeature ) )
439 mFeature = updatedFeature;
443 mFeature.setValid(
true );
444 mLayer->beginEditCommand( mEditCommandMessage );
445 bool res = mLayer->addFeature( updatedFeature );
448 mFeature.setAttributes( updatedFeature.
attributes() );
449 mLayer->endEditCommand();
451 const QgsFields fields = mLayer->fields();
452 const QgsAttributes newValues = updatedFeature.
attributes();
453 const QVariant lastUsedValuesVariant = mLayer->property(
"AttributeFormLastUsedValues" );
455 for (
int idx = 0; idx < fields.
count(); ++idx )
460 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
461 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
462 if ( !rememberLastUsedValues.contains( idx ) )
465 rememberLastUsedValues[idx] = remember;
466 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
469 const QVariant newValue = rememberLastUsedValues[idx] ? newValues.at( idx ) : QVariant();
470 if ( !lastUsedValues.contains( idx ) || lastUsedValues[idx] != newValue )
472 lastUsedValues[idx] = newValue;
473 QgsDebugMsgLevel( u
"Saving %1 for %2"_s.arg( ( newValue.toString() ).arg( idx ) ), 2 );
477 mLayer->setProperty(
"AttributeFormLastUsedValues", QVariant::fromValue<QgsAttributeMap>( lastUsedValues ) );
483 mLayer->destroyEditCommand();
487 mLayer->beginEditCommand( mEditCommandMessage );
493 for (
int i = 0; i < dst.count(); ++i )
496 || !dst.at( i ).isValid()
497 || !fieldIsEditable( i ) )
503 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 );
504 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 );
506 newValues[i] = dst.at( i );
507 oldValues[i] = src.at( i );
512 auto context = std::make_unique<QgsVectorLayerToolsContext>();
513 QgsExpressionContext expressionContext = createExpressionContext( updatedFeature );
514 context->setExpressionContext( &expressionContext );
515 success = mLayer->changeAttributeValues( mFeature.id(), newValues, oldValues,
false, context.get() );
517 if ( success && n > 0 )
519 mLayer->endEditCommand();
520 mFeature.setAttributes( dst );
525 mLayer->destroyEditCommand();
539 mLayer->triggerRepaint();
544QgsFeature QgsAttributeForm::getUpdatedFeature()
const
547 QgsFeature updatedFeature = QgsFeature( mFeature );
549 QgsAttributes featureAttributes = mFeature.attributes();
550 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
552 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
556 QVariantList dstVars = QVariantList() << featureAttributes.at( eww->
fieldIdx() );
557 QVariantList srcVars = QVariantList() << eww->
value();
558 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
562 for (
const QString &fieldName : additionalFields )
566 dstVars << featureAttributes.at( idx );
570 Q_ASSERT( dstVars.count() == srcVars.count() );
572 for (
int i = 0; i < dstVars.count(); i++ )
574 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
575 featureAttributes[fieldIndexes[i]] = srcVars[i];
580 return updatedFeature;
583void QgsAttributeForm::updateValuesDependencies(
const int originIdx )
585 updateValuesDependenciesDefaultValues( originIdx );
586 updateValuesDependenciesVirtualFields( originIdx );
589void QgsAttributeForm::updateValuesDependenciesDefaultValues(
const int originIdx )
591 if ( !mDefaultValueDependencies.contains( originIdx ) )
594 if ( !mFeature.isValid() )
613 QgsFeature updatedFeature = getUpdatedFeature();
616 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
617 for ( QgsWidgetWrapper *ww : std::as_const( relevantWidgets ) )
619 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
633 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
636 QgsExpressionContext context = createExpressionContext( updatedFeature );
638 const QVariant value = mLayer->defaultValue( eww->
fieldIdx(), updatedFeature, &context );
640 mCurrentFormFeature.setAttribute( eww->
field().
name(), value );
645void QgsAttributeForm::updateValuesDependenciesVirtualFields(
const int originIdx )
647 if ( !mVirtualFieldsDependencies.contains( originIdx ) )
650 if ( !mFeature.isValid() )
654 QgsFeature updatedFeature = getUpdatedFeature();
657 const QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
658 for ( QgsWidgetWrapper *ww : relevantWidgets )
660 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
665 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
669 QgsExpressionContext context = createExpressionContext( updatedFeature );
670 QgsExpression exp( mLayer->expressionField( eww->
fieldIdx() ) );
671 const QVariant value = exp.evaluate( &context );
677void QgsAttributeForm::updateValuesDependenciesParent()
680 QgsFeature updatedFeature = getUpdatedFeature();
681 QList<int> updatedFields;
684 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mParentDependencies;
685 for ( QgsEditorWidgetWrapper *eww : relevantWidgets )
688 if ( updatedFields.contains( eww->
fieldIdx() ) )
693 QgsExpressionContext context = createExpressionContext( updatedFeature );
694 const QVariant value = mLayer->defaultValue( eww->
fieldIdx(), updatedFeature, &context );
699void QgsAttributeForm::updateRelatedLayerFields()
702 updateRelatedLayerFieldsDependencies();
704 if ( mRelatedLayerFieldsDependencies.isEmpty() )
707 if ( !mFeature.isValid() )
711 QgsFeature updatedFeature = getUpdatedFeature();
714 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mRelatedLayerFieldsDependencies;
715 for ( QgsEditorWidgetWrapper *eww : relevantWidgets )
718 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
722 QgsExpressionContext context = createExpressionContext( updatedFeature );
723 QgsExpression exp( mLayer->expressionField( eww->
fieldIdx() ) );
724 QVariant value = exp.evaluate( &context );
729void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
734 mUnsavedMultiEditChanges =
false;
738void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
740 clearMultiEditMessages();
741 resetMultiEdit( link ==
"#apply"_L1 );
744void QgsAttributeForm::filterTriggered()
746 QString filter = createFilterExpression();
752void QgsAttributeForm::searchZoomTo()
754 QString filter = createFilterExpression();
755 if ( filter.isEmpty() )
761void QgsAttributeForm::searchFlash()
763 QString filter = createFilterExpression();
764 if ( filter.isEmpty() )
770void QgsAttributeForm::filterAndTriggered()
772 QString filter = createFilterExpression();
773 if ( filter.isEmpty() )
781void QgsAttributeForm::filterOrTriggered()
783 QString filter = createFilterExpression();
784 if ( filter.isEmpty() )
792void QgsAttributeForm::pushSelectedFeaturesMessage()
794 int count = mLayer->selectedFeatureCount();
797 mMessageBar->pushMessage( QString(), tr(
"%n matching feature(s) selected",
"matching features", count ),
Qgis::MessageLevel::Info );
812 QString filter = createFilterExpression();
813 if ( filter.isEmpty() )
817 pushSelectedFeaturesMessage();
822void QgsAttributeForm::searchSetSelection()
827void QgsAttributeForm::searchAddToSelection()
832void QgsAttributeForm::searchRemoveFromSelection()
837void QgsAttributeForm::searchIntersectSelection()
842bool QgsAttributeForm::saveMultiEdits()
846 const QList<int> fieldIndexes = mFormEditorWidgets.uniqueKeys();
847 for (
int fieldIndex : fieldIndexes )
849 const QList<QgsAttributeFormEditorWidget *> widgets = mFormEditorWidgets.values( fieldIndex );
850 if ( !widgets.first()->hasChanged() )
853 if ( !widgets.first()->currentValue().isValid()
854 || !fieldIsEditable( fieldIndex ) )
860 for ( QgsAttributeFormEditorWidget *widget : widgets )
861 widget->changesCommitted();
863 newAttributeValues.insert( fieldIndex, widgets.first()->currentValue() );
866 if ( newAttributeValues.isEmpty() )
874 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
875 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
876 if ( res != QMessageBox::Ok )
883 mLayer->beginEditCommand( tr(
"Updated multiple feature attributes" ) );
887 const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
890 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
891 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
893 success &= mLayer->changeAttributeValue( fid, aIt.key(), aIt.value() );
897 clearMultiEditMessages();
900 mLayer->endEditCommand();
901 mLayer->triggerRepaint();
902 mMultiEditMessageBarItem =
new QgsMessageBarItem( tr(
"Attribute changes for multiple features applied." ),
Qgis::MessageLevel::Success, -1 );
906 mLayer->destroyEditCommand();
910 if ( !mButtonBox->isVisible() )
911 mMessageBar->pushItem( mMultiEditMessageBarItem );
937 wrapper->notifyAboutToSave();
980 success = saveEdits( error );
984 success = saveMultiEdits();
992 mUnsavedMultiEditChanges =
false;
1001 mValuesInitialized =
false;
1002 const auto constMWidgets = mWidgets;
1005 ww->setFeature( mFeature );
1016 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1017 updateValuesDependenciesVirtualFields( eww->
fieldIdx() );
1018 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1021 mValuesInitialized =
true;
1027 const auto widgets { findChildren<QgsAttributeFormEditorWidget *>() };
1034void QgsAttributeForm::clearMultiEditMessages()
1036 if ( mMultiEditUnsavedMessageBarItem )
1038 if ( !mButtonBox->isVisible() )
1039 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
1040 mMultiEditUnsavedMessageBarItem =
nullptr;
1042 if ( mMultiEditMessageBarItem )
1044 if ( !mButtonBox->isVisible() )
1045 mMessageBar->popWidget( mMultiEditMessageBarItem );
1046 mMultiEditMessageBarItem =
nullptr;
1050QString QgsAttributeForm::createFilterExpression()
const
1052 QStringList filters;
1053 for ( QgsAttributeFormWidget *w : std::as_const( mFormWidgets ) )
1055 QString filter = w->currentFilterExpression();
1056 if ( !filter.isEmpty() )
1060 if ( filters.isEmpty() )
1063 QString filter = filters.join(
") AND ("_L1 ).prepend(
'(' ).append(
')' );
1069 QgsExpressionContext context;
1072 if ( mExtraContextScope )
1074 context.
appendScope(
new QgsExpressionContextScope( *mExtraContextScope.get() ) );
1076 if ( mContext.parentFormFeature().isValid() )
1085void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
1087 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( sender() );
1090 bool signalEmitted =
false;
1092 if ( mValuesInitialized )
1095 mCurrentFormFeature.setAttribute( eww->
field().
name(), value );
1099 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1100 for ( QgsAttributeFormEditorWidget *formEditorWidget : std::as_const( formEditorWidgets ) )
1102 if ( formEditorWidget->editorWidget() == eww )
1106 formEditorWidget->editorWidget()->setValue( value );
1124 for (
int i = 0; i < additionalFields.count(); i++ )
1126 const QString fieldName = additionalFields.at( i );
1127 const QVariant value = additionalFieldValues.at( i );
1131 signalEmitted =
true;
1133 if ( mValuesInitialized )
1134 updateJoinedFields( *eww );
1140 if ( !mIsSettingMultiEditFeatures )
1142 mUnsavedMultiEditChanges =
true;
1144 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
1145 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
1146 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1147 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
1148 clearMultiEditMessages();
1151 if ( !mButtonBox->isVisible() )
1152 mMessageBar->pushItem( mMultiEditUnsavedMessageBarItem );
1155 signalEmitted =
true;
1165 updateConstraints( eww );
1168 if ( mValuesInitialized && !mIsSettingMultiEditFeatures )
1171 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1172 updateValuesDependencies( eww->
fieldIdx() );
1173 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1178 updateEditableState();
1180 if ( !signalEmitted )
1185 bool attributeHasChanged = !mIsSettingFeature;
1187 attributeHasChanged &= !mIsSettingMultiEditFeatures;
1193void QgsAttributeForm::updateAllConstraints()
1195 const auto constMWidgets = mWidgets;
1196 for ( QgsWidgetWrapper *ww : constMWidgets )
1198 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1200 updateConstraints( eww );
1208 if ( currentFormValuesFeature( ft ) )
1220 updateConstraint( ft, eww );
1223 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
1225 for ( QgsEditorWidgetWrapper *depsEww : deps )
1226 updateConstraint( ft, depsEww );
1231 QgsExpressionContext context = createExpressionContext( ft );
1234 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
1235 for ( ContainerInformation *info : infos )
1237 info->apply( &context );
1242void QgsAttributeForm::updateContainersVisibility()
1244 QgsExpressionContext context = createExpressionContext( mFeature );
1246 const QVector<ContainerInformation *> infos = mContainerVisibilityCollapsedInformation;
1248 for ( ContainerInformation *info : infos )
1250 info->apply( &context );
1260 updateAllConstraints();
1275 if ( mJoinedFeatures.contains( info ) )
1291void QgsAttributeForm::updateLabels()
1293 if ( !mLabelDataDefinedProperties.isEmpty() )
1295 QgsFeature currentFeature;
1296 if ( currentFormValuesFeature( currentFeature ) )
1298 QgsExpressionContext context = createExpressionContext( currentFeature );
1300 for (
auto it = mLabelDataDefinedProperties.constBegin(); it != mLabelDataDefinedProperties.constEnd(); ++it )
1302 QLabel *label { it.key() };
1304 const QString value { it->valueAsString( context, QString(), &ok ) };
1305 if ( ok && !value.isEmpty() )
1307 label->setText( value );
1314void QgsAttributeForm::updateEditableState()
1316 if ( !mEditableDataDefinedProperties.isEmpty() )
1318 QgsFeature currentFeature;
1319 if ( currentFormValuesFeature( currentFeature ) )
1321 QgsExpressionContext context = createExpressionContext( currentFeature );
1323 for (
auto it = mEditableDataDefinedProperties.constBegin(); it != mEditableDataDefinedProperties.constEnd(); ++it )
1325 QWidget *w { it.key() };
1327 const bool isEditable { it->valueAsBool( context,
true, &ok ) && mLayer && mLayer->isEditable() };
1330 QgsAttributeFormEditorWidget *editorWidget { qobject_cast<QgsAttributeFormEditorWidget *>( w ) };
1337 w->setEnabled( isEditable );
1345bool QgsAttributeForm::currentFormValuesFeature(
QgsFeature &feature )
1348 feature = QgsFeature( mFeature );
1349 QgsAttributes dst =
feature.attributes();
1351 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1353 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1358 if ( dst.count() > eww->
fieldIdx() )
1360 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1361 QVariantList srcVars = QVariantList() << eww->
value();
1362 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1366 for (
const QString &fieldName : additionalFields )
1369 fieldIndexes << idx;
1370 dstVars << dst.at( idx );
1374 Q_ASSERT( dstVars.count() == srcVars.count() );
1376 for (
int i = 0; i < dstVars.count(); i++ )
1382 dst[fieldIndexes[i]] = srcVars[i];
1399void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1401 mContainerVisibilityCollapsedInformation.append( info );
1405 for (
const QString &col : referencedColumns )
1407 mContainerInformationDependency[col].append( info );
1411bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1413 bool valid {
true };
1415 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1417 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1435bool QgsAttributeForm::currentFormValidHardConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1437 bool valid {
true };
1439 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1441 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1456void QgsAttributeForm::onAttributeAdded(
int idx )
1458 mPreventFeatureRefresh =
false;
1459 if ( mFeature.isValid() )
1461 QgsAttributes attrs = mFeature.attributes();
1463 mFeature.setFields(
layer()->fields() );
1464 mFeature.setAttributes( attrs );
1470void QgsAttributeForm::onAttributeDeleted(
int idx )
1472 mPreventFeatureRefresh =
false;
1473 if ( mFeature.isValid() )
1475 QgsAttributes attrs = mFeature.attributes();
1476 attrs.remove( idx );
1477 mFeature.setFields(
layer()->fields() );
1478 mFeature.setAttributes( attrs );
1484void QgsAttributeForm::onRelatedFeaturesChanged()
1486 updateRelatedLayerFields();
1489void QgsAttributeForm::onUpdatedFields()
1491 mPreventFeatureRefresh =
false;
1492 if ( mFeature.isValid() )
1494 QgsAttributes attrs(
layer()->fields().size() );
1497 int idx = mFeature.fields().indexFromName(
layer()->fields().at( i ).name() );
1500 attrs[i] = mFeature.attributes().at( idx );
1501 if ( mFeature.attributes().at( idx ).userType() !=
layer()->fields().at( i ).type() )
1503 attrs[i].convert(
layer()->fields().at( i ).type() );
1511 mFeature.setFields(
layer()->fields() );
1512 mFeature.setAttributes( attrs );
1520 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( sender() );
1523 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1525 for ( QgsAttributeFormEditorWidget *formEditorWidget : formEditorWidgets )
1527 formEditorWidget->setConstraintStatus( constraint, description, err, result );
1528 if ( formEditorWidget->editorWidget() != eww )
1530 formEditorWidget->editorWidget()->updateConstraint( result, err );
1537 QList<QgsEditorWidgetWrapper *> wDeps;
1541 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1544 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1549 if ( name != ewwName )
1556 for (
const QString &colName : referencedColumns )
1558 if ( name.compare( colName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
1560 wDeps.append( eww );
1573 return setupRelationWidgetWrapper( QString(), rel, context );
1578 QgsRelationWidgetWrapper *rww =
new QgsRelationWidgetWrapper( relationWidgetTypeId, mLayer, rel,
nullptr,
this );
1579 const QVariantMap config = mLayer->editFormConfig().widgetConfig( rel.
id() );
1586void QgsAttributeForm::preventFeatureRefresh()
1588 mPreventFeatureRefresh =
true;
1593 if ( mPreventFeatureRefresh || mLayer->isEditable() || !mFeature.isValid() )
1598 if ( !mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( mFeature ) )
1607 if ( mContext.parentFormFeature().isValid() )
1609 QgsFeature parentFormFeature = mContext.parentFormFeature();
1611 mContext.setParentFormFeature( parentFormFeature );
1614 updateValuesDependenciesParent();
1628 return mNeedsGeometry;
1631void QgsAttributeForm::synchronizeState()
1633 bool isEditable =
false;
1654 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1656 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1659 const QList<QgsAttributeFormEditorWidget *> formWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1661 for ( QgsAttributeFormEditorWidget *formWidget : formWidgets )
1663 formWidget->setConstraintResultVisible( isEditable );
1668 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1669 ww->setEnabled( enabled );
1675 ww->setEnabled( isEditable );
1685 if ( mConstraintsFailMessageBarItem )
1687 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1689 mConstraintsFailMessageBarItem =
new QgsMessageBarItem( tr(
"Multi edit mode requires at least one selected feature." ),
Qgis::MessageLevel::Info, -1 );
1690 mMessageBar->pushItem( mConstraintsFailMessageBarItem );
1694 QStringList invalidFields, descriptions;
1695 mValidConstraints = currentFormValidHardConstraints( invalidFields, descriptions );
1699 if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
1701 mConstraintsFailMessageBarItem
1702 =
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 );
1703 mMessageBar->pushItem( mConstraintsFailMessageBarItem );
1705 else if ( mValidConstraints && mConstraintsFailMessageBarItem )
1707 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1708 mConstraintsFailMessageBarItem =
nullptr;
1711 else if ( mConstraintsFailMessageBarItem )
1713 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1714 mConstraintsFailMessageBarItem =
nullptr;
1717 isEditable = isEditable & mValidConstraints;
1722 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1725 okButton->setEnabled( isEditable );
1729void QgsAttributeForm::init()
1731 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1734 QWidget *formWidget =
nullptr;
1735 mNeedsGeometry =
false;
1737 bool buttonBoxVisible =
true;
1741 buttonBoxVisible = mButtonBox->isVisible();
1743 mButtonBox =
nullptr;
1746 if ( mSearchButtonBox )
1748 delete mSearchButtonBox;
1749 mSearchButtonBox =
nullptr;
1752 qDeleteAll( mWidgets );
1755 while ( QWidget *w = this->findChild<QWidget *>() )
1761 QVBoxLayout *vl =
new QVBoxLayout();
1762 vl->setContentsMargins( 0, 0, 0, 0 );
1763 mMessageBar =
new QgsMessageBar(
this );
1764 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1765 vl->addWidget( mMessageBar );
1770 QGridLayout *layout =
new QGridLayout();
1771 QWidget *container =
new QWidget();
1772 container->setLayout( layout );
1773 vl->addWidget( container );
1775 mFormEditorWidgets.clear();
1776 mFormWidgets.clear();
1779 setContentsMargins( 0, 0, 0, 0 );
1784 QgsDebugMsgLevel( u
"loading form: %1"_s.arg( mLayer->editFormConfig().uiForm() ), 2 );
1785 const QString path = mLayer->editFormConfig().uiForm();
1787 if ( file && file->open( QFile::ReadOnly ) )
1791 QFileInfo fi( file->fileName() );
1792 loader.setWorkingDirectory( fi.dir() );
1793 formWidget = loader.load( file,
this );
1796 formWidget->setWindowFlags( Qt::Widget );
1797 layout->addWidget( formWidget );
1800 mButtonBox = findChild<QDialogButtonBox *>();
1803 formWidget->installEventFilter(
this );
1808 QgsTabWidget *tabWidget =
nullptr;
1815 int columnCount = 1;
1816 bool hasRootFields =
false;
1817 bool addSpacer =
true;
1819 const QList<QgsAttributeEditorElement *> tabs = mLayer->editFormConfig().tabs();
1821 for ( QgsAttributeEditorElement *widgDef : tabs )
1825 QgsAttributeEditorContainer *containerDef =
dynamic_cast<QgsAttributeEditorContainer *
>( widgDef );
1826 if ( !containerDef )
1829 switch ( containerDef->
type() )
1833 tabWidget =
nullptr;
1834 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1835 if ( widgetInfo.labelStyle.overrideColor )
1837 if ( widgetInfo.labelStyle.color.isValid() )
1839 widgetInfo.widget->setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1842 if ( widgetInfo.labelStyle.overrideFont )
1844 widgetInfo.widget->setFont( widgetInfo.labelStyle.font );
1847 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1848 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1850 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1852 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1854 layout->setRowStretch( row, widgDef->verticalStretch() );
1859 if ( widgetInfo.expandingNeeded )
1862 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
1866 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
1872 registerContainerInformation(
new ContainerInformation(
1885 tabWidget =
nullptr;
1886 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1887 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1888 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1890 layout->setRowStretch( row, widgDef->verticalStretch() );
1895 if ( widgetInfo.expandingNeeded )
1898 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
1902 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
1905 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1907 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1912 registerContainerInformation(
new ContainerInformation(
1927 tabWidget =
new QgsTabWidget();
1928 layout->addWidget( tabWidget, row, column, 1, 2 );
1932 QWidget *tabPage =
new QWidget( tabWidget );
1933 tabWidget->addTab( tabPage, widgDef->name() );
1934 tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
1938 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1940 QGridLayout *tabPageLayout =
new QGridLayout();
1941 tabPage->setLayout( tabPageLayout );
1943 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1944 if ( widgetInfo.expandingNeeded )
1947 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
1949 tabPageLayout->addWidget( widgetInfo.widget );
1956 hasRootFields =
true;
1957 tabWidget =
nullptr;
1958 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1959 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox();
1961 if ( widgetInfo.showLabel )
1963 if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
1965 collapsibleGroupBox->
setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1968 if ( widgetInfo.labelStyle.overrideFont )
1970 collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
1973 collapsibleGroupBox->setTitle( widgetInfo.labelText );
1976 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1977 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
1978 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1980 QVBoxLayout *
c =
new QVBoxLayout();
1981 c->addWidget( collapsibleGroupBox );
1982 layout->addLayout(
c, row, column, 1, 2 );
1984 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1985 layout->setRowStretch( row, widgDef->verticalStretch() );
1986 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1987 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1996 hasRootFields =
true;
1997 tabWidget =
nullptr;
1998 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1999 QLabel *label =
new QLabel( widgetInfo.labelText );
2001 if ( widgetInfo.labelStyle.overrideColor )
2003 if ( widgetInfo.labelStyle.color.isValid() )
2005 label->setStyleSheet( u
"QLabel { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2009 if ( widgetInfo.labelStyle.overrideFont )
2011 label->setFont( widgetInfo.labelStyle.font );
2014 label->setToolTip( widgetInfo.toolTip );
2015 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2017 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2020 label->setBuddy( widgetInfo.widget );
2023 if ( widgetInfo.widget
2024 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2025 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2026 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2029 if ( !widgetInfo.showLabel )
2031 QVBoxLayout *
c =
new QVBoxLayout();
2032 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2033 c->addWidget( widgetInfo.widget );
2034 layout->addLayout(
c, row, column, 1, 2 );
2036 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2038 layout->setRowStretch( row, widgDef->verticalStretch() );
2041 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
2043 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
2048 else if ( widgetInfo.labelOnTop )
2050 QVBoxLayout *
c =
new QVBoxLayout();
2051 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2052 c->addWidget( label );
2053 c->addWidget( widgetInfo.widget );
2054 layout->addLayout(
c, row, column, 1, 2 );
2056 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2058 layout->setRowStretch( row, widgDef->verticalStretch() );
2061 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
2063 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
2070 const int widgetColumn = column + 1;
2071 layout->addWidget( label, row, column++ );
2072 layout->addWidget( widgetInfo.widget, row, column++ );
2074 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2076 layout->setRowStretch( row, widgDef->verticalStretch() );
2079 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( widgetColumn ) )
2081 layout->setColumnStretch( widgetColumn, widgDef->horizontalStretch() );
2088 const QgsAttributeEditorField *fieldElement {
static_cast<QgsAttributeEditorField *
>( widgDef ) };
2089 const int fieldIdx = fieldElement->
idx();
2090 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
2092 const QString fieldName { mLayer->fields().at( fieldIdx ).name() };
2096 if ( property.isActive() )
2098 mLabelDataDefinedProperties[label] = property;
2104 if ( property.isActive() )
2106 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2113 if ( column >= columnCount * 2 )
2120 if ( hasRootFields && addSpacer )
2122 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2123 layout->addItem( spacerItem, row, 0 );
2124 layout->setRowStretch( row, 1 );
2127 formWidget = container;
2136 formWidget =
new QWidget(
this );
2137 QGridLayout *gridLayout =
new QGridLayout( formWidget );
2138 formWidget->setLayout( gridLayout );
2143 QgsScrollArea *scrollArea =
new QgsScrollArea(
this );
2144 scrollArea->setWidget( formWidget );
2145 scrollArea->setWidgetResizable(
true );
2146 scrollArea->setFrameShape( QFrame::NoFrame );
2147 scrollArea->setFrameShadow( QFrame::Plain );
2148 scrollArea->setFocusProxy(
this );
2149 layout->addWidget( scrollArea );
2153 layout->addWidget( formWidget );
2158 const QgsFields fields = mLayer->fields();
2163 QSet<int> compositeHiddenFields;
2165 for (
const QgsRelation &rel : referencingRelations )
2170 const QList<QgsRelation::FieldPair> fieldPairs = rel.
fieldPairs();
2171 if ( fieldPairs.size() > 1 )
2173 for (
int i = 1; i < fieldPairs.size(); i++ )
2175 const int idx = fields.
lookupField( fieldPairs.at( i ).referencingField() );
2177 compositeHiddenFields.insert( idx );
2182 for (
const QgsField &field : fields )
2184 int idx = fields.lookupField( field.name() );
2188 if ( compositeHiddenFields.contains( idx ) )
2192 QString fieldName = mLayer->attributeDisplayName( idx );
2193 QString labelText = fieldName;
2194 labelText.replace(
'&',
"&&"_L1 );
2198 if ( widgetSetup.
type() ==
"Hidden"_L1 )
2201 bool labelOnTop = mLayer->editFormConfig().labelOnTop( idx );
2204 QLabel *label =
new QLabel( labelText );
2206 QSvgWidget *i =
new QSvgWidget();
2207 i->setFixedSize( 18, 18 );
2212 if ( property.isActive() )
2214 mLabelDataDefinedProperties[label] = property;
2220 QWidget *w =
nullptr;
2223 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2225 mFormEditorWidgets.insert( idx, formWidget );
2226 mFormWidgets.append( formWidget );
2232 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2233 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2234 if ( rememberLastUsedValues.contains( idx ) )
2236 remember = rememberLastUsedValues[idx];
2241 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2242 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2243 rememberLastUsedValues[idx] = remember;
2244 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2250 label->setBuddy( eww->
widget() );
2255 if ( property.isActive() )
2257 mEditableDataDefinedProperties[formWidget] = property;
2263 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() ) ) );
2268 w->setObjectName( field.name() );
2272 mWidgets.append( eww );
2273 mIconMap[eww->
widget()] = i;
2278 gridLayout->addWidget( label, row++, 0, 1, 2 );
2279 gridLayout->addWidget( w, row++, 0, 1, 2 );
2280 gridLayout->addWidget( i, row++, 0, 1, 2 );
2284 gridLayout->addWidget( label, row, 0 );
2285 gridLayout->addWidget( w, row, 1 );
2286 gridLayout->addWidget( i, row++, 2 );
2291 for (
const QgsRelation &rel : relations )
2293 QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( u
"relation_editor"_s, rel, mContext );
2295 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2298 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox( rel.
name() );
2299 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
2300 collapsibleGroupBoxLayout->addWidget( formWidget );
2301 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
2303 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
2305 mWidgets.append( rww );
2306 mFormWidgets.append( formWidget );
2311 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2312 gridLayout->addItem( spacerItem, row, 0 );
2313 gridLayout->setRowStretch( row, 1 );
2319 updateFieldDependencies();
2323 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
2324 mButtonBox->setObjectName( u
"buttonBox"_s );
2325 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2327 mButtonBox->setVisible( buttonBoxVisible );
2329 if ( !mSearchButtonBox )
2331 mSearchButtonBox =
new QWidget();
2332 QHBoxLayout *boxLayout =
new QHBoxLayout();
2333 boxLayout->setContentsMargins( 0, 0, 0, 0 );
2334 mSearchButtonBox->setLayout( boxLayout );
2335 mSearchButtonBox->setObjectName( u
"searchButtonBox"_s );
2337 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
2339 boxLayout->addWidget( clearButton );
2340 boxLayout->addStretch( 1 );
2342 QPushButton *flashButton =
new QPushButton();
2343 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2344 flashButton->setText( tr(
"&Flash Features" ) );
2345 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
2346 boxLayout->addWidget( flashButton );
2348 QPushButton *openAttributeTableButton =
new QPushButton();
2349 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2350 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
2351 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
2353 boxLayout->addWidget( openAttributeTableButton );
2355 QPushButton *zoomButton =
new QPushButton();
2356 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2357 zoomButton->setText( tr(
"&Zoom to Features" ) );
2358 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
2359 boxLayout->addWidget( zoomButton );
2361 QToolButton *selectButton =
new QToolButton();
2362 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2363 selectButton->setText( tr(
"&Select Features" ) );
2365 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
2366 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
2367 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
2368 QMenu *selectMenu =
new QMenu( selectButton );
2369 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
2371 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
2372 selectMenu->addAction( selectAction );
2373 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
2375 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
2376 selectMenu->addAction( addSelectAction );
2377 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
2379 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
2380 selectMenu->addAction( deselectAction );
2381 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
2383 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
2384 selectMenu->addAction( filterSelectAction );
2385 selectButton->setMenu( selectMenu );
2386 boxLayout->addWidget( selectButton );
2390 QToolButton *filterButton =
new QToolButton();
2391 filterButton->setText( tr(
"Filter Features" ) );
2392 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
2393 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2394 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
2395 QMenu *filterMenu =
new QMenu( filterButton );
2396 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
2397 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
2398 filterMenu->addAction( filterAndAction );
2399 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
2400 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
2401 filterMenu->addAction( filterOrAction );
2402 filterButton->setMenu( filterMenu );
2403 boxLayout->addWidget( filterButton );
2407 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
2409 closeButton->setShortcut( Qt::Key_Escape );
2410 boxLayout->addWidget( closeButton );
2413 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2431 const auto constMInterfaces = mInterfaces;
2432 for ( QgsAttributeFormInterface *iface : constMInterfaces )
2442 QApplication::restoreOverrideCursor();
2445void QgsAttributeForm::cleanPython()
2447 if ( !mPyFormVarName.isNull() )
2449 QString expr = u
"if '%1' in locals(): del %1\n"_s.arg( mPyFormVarName );
2454void QgsAttributeForm::initPython()
2465 mMessageBar->pushMessage( tr(
"Security warning" ), tr(
"The attribute form contains an embedded script which has been denied execution." ),
Qgis::MessageLevel::Warning );
2469 QString initFunction = mLayer->editFormConfig().initFunction();
2470 QString initFilePath = mLayer->editFormConfig().initFilePath();
2473 switch ( mLayer->editFormConfig().initCodeSource() )
2476 if ( !initFilePath.isEmpty() )
2480 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2483 QTextStream inf( inputFile );
2484 initCode = inf.readAll();
2489 QgsLogger::warning( u
"The external python file path %1 could not be opened!"_s.arg( initFilePath ) );
2499 initCode = mLayer->editFormConfig().initCode();
2500 if ( initCode.isEmpty() )
2513 if ( !initCode.isEmpty() )
2521 mMessageBar->pushMessage( QString(), tr(
"Python macro could not be run due to missing permissions." ),
Qgis::MessageLevel::Warning );
2529 if (
QgsPythonRunner::eval( u
"len(inspect.getfullargspec(%1)[0])"_s.arg( initFunction ), numArgs ) )
2531 static int sFormId = 0;
2532 mPyFormVarName = u
"_qgis_featureform_%1_%2"_s.arg( mFormNr ).arg( sFormId++ );
2534 QString form = u
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )"_s.arg( mPyFormVarName ).arg( ( quint64 )
this );
2538 QgsDebugMsgLevel( u
"running featureForm init: %1"_s.arg( mPyFormVarName ), 2 );
2541 if ( numArgs ==
"3"_L1 )
2543 addInterface(
new QgsAttributeFormLegacyInterface( initFunction, mPyFormVarName,
this ) );
2550 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 )
2554 QString expr = QString(
"%1(%2)" )
2555 .arg( mLayer->editFormInit() )
2556 .arg( mPyFormVarName );
2557 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2567 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 ) );
2575 WidgetInfo newWidgetInfo;
2577 newWidgetInfo.labelStyle = widgetDef->
labelStyle();
2579 switch ( widgetDef->
type() )
2583 const QgsAttributeEditorAction *elementDef =
dynamic_cast<const QgsAttributeEditorAction *
>( widgetDef );
2587 QgsActionWidgetWrapper *actionWrapper =
new QgsActionWidgetWrapper( mLayer,
nullptr,
this, mMessageBar );
2591 mWidgets.append( actionWrapper );
2592 newWidgetInfo.widget = actionWrapper->
widget();
2593 newWidgetInfo.showLabel =
false;
2600 const QgsAttributeEditorField *fieldDef =
dynamic_cast<const QgsAttributeEditorField *
>( widgetDef );
2604 const QgsFields fields = vl->
fields();
2606 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2611 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2612 mFormEditorWidgets.insert( fldIdx, formWidget );
2613 mFormWidgets.append( formWidget );
2619 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2620 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2621 if ( rememberLastUsedValues.contains( fldIdx ) )
2623 remember = rememberLastUsedValues[fldIdx];
2627 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2628 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2629 rememberLastUsedValues[idx] = remember;
2630 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2636 newWidgetInfo.widget = formWidget;
2637 mWidgets.append( eww );
2639 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2640 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
2643 newWidgetInfo.labelOnTop = mLayer->editFormConfig().labelOnTop( fldIdx );
2644 newWidgetInfo.labelText = mLayer->attributeDisplayName( fldIdx );
2645 newWidgetInfo.labelText.replace(
'&',
"&&"_L1 );
2646 newWidgetInfo.toolTip = u
"<b>%1</b><p>%2</p>"_s.arg( mLayer->attributeDisplayName( fldIdx ), newWidgetInfo.hint );
2647 newWidgetInfo.showLabel = widgetDef->
showLabel();
2654 const QgsAttributeEditorRelation *relDef =
static_cast<const QgsAttributeEditorRelation *
>( widgetDef );
2658 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2668 mWidgets.append( rww );
2669 mFormWidgets.append( formWidget );
2671 newWidgetInfo.widget = formWidget;
2672 newWidgetInfo.showLabel = relDef->
showLabel();
2673 newWidgetInfo.labelText = relDef->
label();
2674 if ( newWidgetInfo.labelText.isEmpty() )
2676 newWidgetInfo.labelOnTop =
true;
2682 const QgsAttributeEditorContainer *container =
dynamic_cast<const QgsAttributeEditorContainer *
>( widgetDef );
2688 if ( columnCount <= 0 )
2692 QWidget *myContainer =
nullptr;
2693 bool removeLayoutMargin =
false;
2694 switch ( container->
type() )
2698 QgsCollapsibleGroupBoxBasic *groupBox =
new QgsCollapsibleGroupBoxBasic();
2699 widgetName = u
"QGroupBox"_s;
2702 groupBox->setTitle( container->
name() );
2703 if ( newWidgetInfo.labelStyle.overrideColor )
2705 if ( newWidgetInfo.labelStyle.color.isValid() )
2707 groupBox->
setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2710 if ( newWidgetInfo.labelStyle.overrideFont )
2712 groupBox->setFont( newWidgetInfo.labelStyle.font );
2715 myContainer = groupBox;
2716 newWidgetInfo.widget = myContainer;
2723 QWidget *rowWidget =
new QWidget();
2724 widgetName = u
"Row"_s;
2725 myContainer = rowWidget;
2726 newWidgetInfo.widget = myContainer;
2727 removeLayoutMargin =
true;
2728 columnCount = container->
children().size();
2734 myContainer =
new QWidget();
2736 QgsScrollArea *scrollArea =
new QgsScrollArea( parent );
2738 scrollArea->setWidget( myContainer );
2739 scrollArea->setWidgetResizable(
true );
2740 scrollArea->setFrameShape( QFrame::NoFrame );
2741 widgetName = u
"QScrollArea QWidget"_s;
2743 newWidgetInfo.widget = scrollArea;
2750 QString style { u
"background-color: %1;"_s.arg( container->
backgroundColor().name() ) };
2751 newWidgetInfo.widget->setStyleSheet( style );
2754 QGridLayout *gbLayout =
new QGridLayout();
2755 if ( removeLayoutMargin )
2756 gbLayout->setContentsMargins( 0, 0, 0, 0 );
2757 myContainer->setLayout( gbLayout );
2761 bool addSpacer =
true;
2763 const QList<QgsAttributeEditorElement *> children = container->
children();
2765 for ( QgsAttributeEditorElement *childDef : children )
2767 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2771 QgsAttributeEditorContainer *containerDef =
static_cast<QgsAttributeEditorContainer *
>( childDef );
2774 registerContainerInformation(
new ContainerInformation(
2781 if ( childDef->verticalStretch() == 0 )
2783 if ( widgetInfo.expandingNeeded )
2785 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
2789 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
2795 int widgetColumn = column;
2797 if ( widgetInfo.labelText.isNull() || !widgetInfo.showLabel )
2799 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2800 widgetColumn = column + 1;
2805 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2807 if ( widgetInfo.labelStyle.overrideColor )
2809 if ( widgetInfo.labelStyle.color.isValid() )
2811 mypLabel->setStyleSheet( u
"QLabel { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2815 if ( widgetInfo.labelStyle.overrideFont )
2817 mypLabel->setFont( widgetInfo.labelStyle.font );
2823 const QgsAttributeEditorField *fieldDef {
static_cast<QgsAttributeEditorField *
>( childDef ) };
2824 const QgsFields fields = vl->
fields();
2825 const int fldIdx = fieldDef->
idx();
2826 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2828 const QString fieldName { fields.
at( fldIdx ).
name() };
2832 if ( property.isActive() )
2834 mLabelDataDefinedProperties[mypLabel] = property;
2840 if ( property.isActive() )
2842 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2848 mypLabel->setToolTip( widgetInfo.toolTip );
2849 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2851 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2854 mypLabel->setBuddy( widgetInfo.widget );
2856 if ( widgetInfo.labelOnTop )
2858 widgetColumn = column + 1;
2859 QVBoxLayout *
c =
new QVBoxLayout();
2860 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2861 c->layout()->addWidget( mypLabel );
2862 c->layout()->addWidget( widgetInfo.widget );
2863 gbLayout->addLayout(
c, row, column, 1, 2 );
2868 widgetColumn = column + 1;
2869 gbLayout->addWidget( mypLabel, row, column++ );
2870 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2874 const int childHorizontalStretch = childDef->horizontalStretch();
2875 const int existingColumnStretch = gbLayout->columnStretch( widgetColumn );
2876 if ( childHorizontalStretch > 0 && childHorizontalStretch > existingColumnStretch )
2878 gbLayout->setColumnStretch( widgetColumn, childHorizontalStretch );
2881 if ( childDef->verticalStretch() > 0 && childDef->verticalStretch() > gbLayout->rowStretch( row ) )
2883 gbLayout->setRowStretch( row, childDef->verticalStretch() );
2886 if ( column >= columnCount * 2 )
2892 if ( widgetInfo.widget
2893 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2894 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2895 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2899 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
2905 QWidget *spacer =
new QWidget();
2906 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2907 gbLayout->addWidget( spacer, ++row, 0 );
2908 gbLayout->setRowStretch( row, 1 );
2911 newWidgetInfo.labelText = QString();
2912 newWidgetInfo.labelOnTop =
true;
2913 newWidgetInfo.showLabel = widgetDef->
showLabel();
2914 newWidgetInfo.expandingNeeded |= !addSpacer;
2920 const QgsAttributeEditorQmlElement *elementDef =
static_cast<const QgsAttributeEditorQmlElement *
>( widgetDef );
2922 QgsQmlWidgetWrapper *qmlWrapper =
new QgsQmlWidgetWrapper( mLayer,
nullptr,
this );
2927 mWidgets.append( qmlWrapper );
2929 newWidgetInfo.widget = qmlWrapper->
widget();
2930 newWidgetInfo.labelText = elementDef->
name();
2931 newWidgetInfo.labelOnTop =
true;
2932 newWidgetInfo.showLabel = widgetDef->
showLabel();
2938 const QgsAttributeEditorHtmlElement *elementDef =
static_cast<const QgsAttributeEditorHtmlElement *
>( widgetDef );
2940 QgsHtmlWidgetWrapper *htmlWrapper =
new QgsHtmlWidgetWrapper( mLayer,
nullptr,
this );
2944 mWidgets.append( htmlWrapper );
2946 newWidgetInfo.widget = htmlWrapper->
widget();
2947 newWidgetInfo.labelText = elementDef->
name();
2948 newWidgetInfo.labelOnTop =
true;
2949 newWidgetInfo.showLabel = widgetDef->
showLabel();
2956 const QgsAttributeEditorTextElement *elementDef =
static_cast<const QgsAttributeEditorTextElement *
>( widgetDef );
2958 QgsTextWidgetWrapper *textWrapper =
new QgsTextWidgetWrapper( mLayer,
nullptr,
this );
2962 mWidgets.append( textWrapper );
2964 newWidgetInfo.widget = textWrapper->
widget();
2965 newWidgetInfo.labelText = elementDef->
name();
2966 newWidgetInfo.labelOnTop =
false;
2967 newWidgetInfo.showLabel = widgetDef->
showLabel();
2974 const QgsAttributeEditorSpacerElement *elementDef =
static_cast<const QgsAttributeEditorSpacerElement *
>( widgetDef );
2975 QgsSpacerWidgetWrapper *spacerWrapper =
new QgsSpacerWidgetWrapper( mLayer,
nullptr,
this );
2978 mWidgets.append( spacerWrapper );
2980 newWidgetInfo.widget = spacerWrapper->
widget();
2981 newWidgetInfo.labelOnTop =
false;
2982 newWidgetInfo.showLabel =
false;
2987 QgsDebugError( u
"Unknown attribute editor widget type encountered..."_s );
2991 return newWidgetInfo;
2994void QgsAttributeForm::createWrappers()
2996 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2997 const QList<QgsField> fields = mLayer->fields().
toList();
2999 const auto constMyWidgets = myWidgets;
3000 for ( QWidget *myWidget : constMyWidgets )
3003 QVariant vRel = myWidget->property(
"qgisRelation" );
3004 if ( vRel.isValid() )
3007 QgsRelation relation = relMgr->
relation( vRel.toString() );
3010 QgsRelationWidgetWrapper *rww =
new QgsRelationWidgetWrapper( mLayer, relation, myWidget,
this );
3011 rww->
setConfig( mLayer->editFormConfig().widgetConfig( relation.
id() ) );
3014 mWidgets.append( rww );
3019 const auto constFields = fields;
3020 for (
const QgsField &field : constFields )
3022 if ( field.name() == myWidget->objectName() )
3024 int idx = mLayer->fields().lookupField( field.name() );
3027 mWidgets.append( eww );
3034void QgsAttributeForm::afterWidgetInit()
3036 bool isFirstEww =
true;
3038 const auto constMWidgets = mWidgets;
3039 for ( QgsWidgetWrapper *ww : constMWidgets )
3041 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3047 setFocusProxy( eww->
widget() );
3056 QgsRelationWidgetWrapper *relationWidgetWrapper = qobject_cast<QgsRelationWidgetWrapper *>( ww );
3057 if ( relationWidgetWrapper )
3070 if ( e->type() == QEvent::KeyPress )
3072 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
3073 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
3084void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet<int> &mixedValueFields, QHash<int, QVariant> &fieldSharedValues )
const
3086 mixedValueFields.clear();
3087 fieldSharedValues.clear();
3093 for (
int i = 0; i < mLayer->
fields().count(); ++i )
3095 if ( mixedValueFields.contains( i ) )
3100 fieldSharedValues[i] = f.
attribute( i );
3104 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
3106 fieldSharedValues.remove( i );
3107 mixedValueFields.insert( i );
3113 if ( mixedValueFields.count() == mLayer->fields().count() )
3122void QgsAttributeForm::layerSelectionChanged()
3136 resetMultiEdit(
true );
3143 mIsSettingMultiEditFeatures =
true;
3144 mMultiEditFeatureIds = fids;
3146 if ( fids.isEmpty() )
3149 QMultiMap<int, QgsAttributeFormEditorWidget *>::const_iterator wIt = mFormEditorWidgets.constBegin();
3150 for ( ; wIt != mFormEditorWidgets.constEnd(); ++wIt )
3152 wIt.value()->initialize( QVariant() );
3154 mIsSettingMultiEditFeatures =
false;
3161 QSet<int> mixedValueFields;
3162 QHash<int, QVariant> fieldSharedValues;
3163 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
3166 fit = mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( *fids.constBegin() ) );
3172 if ( mCurrentFormFeature.id() != firstFeature.
id() )
3177 const auto constMixedValueFields = mixedValueFields;
3178 for (
int fieldIndex : std::as_const( mixedValueFields ) )
3180 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( fieldIndex );
3181 if ( formEditorWidgets.isEmpty() )
3184 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3185 QVariantList additionalFieldValues;
3186 for (
const QString &additionalField : additionalFields )
3187 additionalFieldValues << firstFeature.
attribute( additionalField );
3190 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
3192 QHash<int, QVariant>::const_iterator sharedValueIt = fieldSharedValues.constBegin();
3193 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
3195 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( sharedValueIt.key() );
3196 if ( formEditorWidgets.isEmpty() )
3200 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3201 for (
const QString &additionalField : additionalFields )
3203 int index = mLayer->fields().indexFromName( additionalField );
3204 if ( constMixedValueFields.contains( index ) )
3211 QVariantList additionalFieldValues;
3214 for (
const QString &additionalField : additionalFields )
3215 additionalFieldValues << firstFeature.
attribute( additionalField );
3217 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
3221 for (
const QString &additionalField : additionalFields )
3223 int index = mLayer->fields().indexFromName( additionalField );
3224 Q_ASSERT( fieldSharedValues.contains( index ) );
3225 additionalFieldValues << fieldSharedValues.value( index );
3228 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
3232 setMultiEditFeatureIdsRelations( fids );
3234 mIsSettingMultiEditFeatures =
false;
3239 if ( mOwnsMessageBar )
3241 mOwnsMessageBar =
false;
3242 mMessageBar = messageBar;
3252 QStringList filters;
3255 QString filter = widget->currentFilterExpression();
3256 if ( !filter.isNull() )
3257 filters <<
'(' + filter +
')';
3260 return filters.join(
" AND "_L1 );
3265 mExtraContextScope.reset( extraScope );
3270 const bool newVisibility = expression.
evaluate( expressionContext ).toBool();
3280 widget->setVisible( newVisibility );
3283 isVisible = newVisibility;
3286 const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
3288 if ( collapsedExpression.isValid() && !collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
3290 if ( QgsCollapsibleGroupBoxBasic * collapsibleGroupBox { qobject_cast<QgsCollapsibleGroupBoxBasic *>( widget ) } )
3292 collapsibleGroupBox->
setCollapsed( newCollapsedState );
3293 isCollapsed = newCollapsedState;
3298void QgsAttributeForm::updateJoinedFields(
const QgsEditorWidgetWrapper &eww )
3303 QgsFeature formFeature;
3307 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
3310 const QString hint = tr(
"No feature joined" );
3311 const auto constInfos = infos;
3312 for (
const QgsVectorLayerJoinInfo *info : constInfos )
3314 if ( !info->isDynamicFormEnabled() )
3317 QgsFeature joinFeature = mLayer->joinBuffer()->joinedFeatureOf( info, formFeature );
3319 mJoinedFeatures[info] = joinFeature;
3321 if ( info->hasSubset() )
3325 const auto constSubsetNames = subsetNames;
3326 for (
const QString &field : constSubsetNames )
3328 QString prefixedName = info->prefixedFieldName( field );
3330 QString hintText = hint;
3343 const QgsFields joinFields = joinFeature.
fields();
3344 for (
const QgsField &field : joinFields )
3346 QString prefixedName = info->prefixedFieldName( field );
3348 QString hintText = hint;
3362bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
3368void QgsAttributeForm::updateFieldDependencies()
3370 mDefaultValueDependencies.clear();
3371 mVirtualFieldsDependencies.clear();
3372 mRelatedLayerFieldsDependencies.clear();
3373 mParentDependencies.clear();
3376 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3378 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3382 updateFieldDependenciesParent( eww );
3383 updateFieldDependenciesDefaultValue( eww );
3384 updateFieldDependenciesVirtualFields( eww );
3385 updateRelatedLayerFieldsDependencies( eww );
3389void QgsAttributeForm::updateFieldDependenciesDefaultValue( QgsEditorWidgetWrapper *eww )
3393 if ( exp.needsGeometry() )
3394 mNeedsGeometry =
true;
3399 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3401 for (
const int id : allAttributeIds )
3403 mDefaultValueDependencies.insertMulti(
id, eww );
3409 const QSet<QString> referencedColumns = exp.referencedColumns();
3410 for (
const QString &referencedColumn : referencedColumns )
3412 mDefaultValueDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3417void QgsAttributeForm::updateFieldDependenciesVirtualFields( QgsEditorWidgetWrapper *eww )
3420 if ( expressionField.isEmpty() )
3423 QgsExpression exp( expressionField );
3425 if ( exp.needsGeometry() )
3426 mNeedsGeometry =
true;
3431 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3433 for (
const int id : allAttributeIds )
3435 mVirtualFieldsDependencies.insertMulti(
id, eww );
3441 const QSet<QString> referencedColumns = exp.referencedColumns();
3442 for (
const QString &referencedColumn : referencedColumns )
3444 mVirtualFieldsDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3449void QgsAttributeForm::updateRelatedLayerFieldsDependencies( QgsEditorWidgetWrapper *eww )
3454 if ( expressionField.contains( u
"relation_aggregate"_s ) || expressionField.contains( u
"get_features"_s ) )
3455 mRelatedLayerFieldsDependencies.insert( eww );
3459 mRelatedLayerFieldsDependencies.clear();
3461 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3463 QgsEditorWidgetWrapper *editorWidgetWrapper = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3464 if ( !editorWidgetWrapper )
3467 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
3472void QgsAttributeForm::updateFieldDependenciesParent( QgsEditorWidgetWrapper *eww )
3477 const QSet<QString> referencedVariablesAndFunctions = expression.referencedVariables() + expression.referencedFunctions();
3478 for (
const QString &referenced : referencedVariablesAndFunctions )
3480 if ( referenced.startsWith(
"current_parent"_L1 ) )
3482 mParentDependencies.insert( eww );
3489void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
3491 for ( QgsAttributeFormWidget *formWidget : mFormWidgets )
3493 QgsAttributeFormRelationEditorWidget *relationEditorWidget =
dynamic_cast<QgsAttributeFormRelationEditorWidget *
>( formWidget );
3494 if ( !relationEditorWidget )
3501void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww )
3507 mIconMap[eww->
widget()]->hide();
3509 if ( !eww->
widget()->isEnabled() && mLayer->isEditable() )
3514 const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( eww->
fieldIdx(), mLayer->fields(), srcFieldIndex );
3521 const QString file = u
"/mIconJoinNotEditable.svg"_s;
3522 const QString tooltip = tr(
"Join settings do not allow editing" );
3523 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3527 const QString file = u
"mIconJoinHasNotUpsertOnEdit.svg"_s;
3528 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
3529 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3533 const QString file = u
"/mIconJoinedLayerNotEditable.svg"_s;
3534 const QString tooltip = tr(
"Joined layer is not toggled editable" );
3535 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3541void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
3544 sw->setToolTip( tooltip );
3551 QgsDebugMsgLevel( u
"reuseAllLastValues: %1"_s.arg( reuseAllLastValues ), 2 );
3555 for (
int idx = 0; idx < fields.
count(); ++idx )
3557 if ( attributes.contains( idx ) )
3559 initialAttributeValues.insert( idx, attributes.value( idx ) );
3563 const QVariant lastUsedValuesVariant =
layer->property(
"AttributeFormLastUsedValues" );
3565 if ( lastUsedValues.contains( idx ) &&
layer->dataProvider() &&
layer->dataProvider()->defaultValueClause( idx ) != lastUsedValues[idx] )
3567 initialAttributeValues.insert( idx, lastUsedValues[idx] );
@ Trusted
The project trust has not yet been determined by the user.
@ 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)
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...
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)