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 =
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 );
1702 mMessageBar->pushItem( mConstraintsFailMessageBarItem );
1704 else if ( mValidConstraints && mConstraintsFailMessageBarItem )
1706 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1707 mConstraintsFailMessageBarItem =
nullptr;
1710 else if ( mConstraintsFailMessageBarItem )
1712 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1713 mConstraintsFailMessageBarItem =
nullptr;
1716 isEditable = isEditable & mValidConstraints;
1721 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1724 okButton->setEnabled( isEditable );
1728void QgsAttributeForm::init()
1730 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1733 QWidget *formWidget =
nullptr;
1734 mNeedsGeometry =
false;
1736 bool buttonBoxVisible =
true;
1740 buttonBoxVisible = mButtonBox->isVisible();
1742 mButtonBox =
nullptr;
1745 if ( mSearchButtonBox )
1747 delete mSearchButtonBox;
1748 mSearchButtonBox =
nullptr;
1751 qDeleteAll( mWidgets );
1754 while ( QWidget *w = this->findChild<QWidget *>() )
1760 QVBoxLayout *vl =
new QVBoxLayout();
1761 vl->setContentsMargins( 0, 0, 0, 0 );
1762 mMessageBar =
new QgsMessageBar(
this );
1763 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1764 vl->addWidget( mMessageBar );
1769 QGridLayout *layout =
new QGridLayout();
1770 QWidget *container =
new QWidget();
1771 container->setLayout( layout );
1772 vl->addWidget( container );
1774 mFormEditorWidgets.clear();
1775 mFormWidgets.clear();
1778 setContentsMargins( 0, 0, 0, 0 );
1783 QgsDebugMsgLevel( u
"loading form: %1"_s.arg( mLayer->editFormConfig().uiForm() ), 2 );
1784 const QString path = mLayer->editFormConfig().uiForm();
1786 if ( file && file->open( QFile::ReadOnly ) )
1790 QFileInfo fi( file->fileName() );
1791 loader.setWorkingDirectory( fi.dir() );
1792 formWidget = loader.load( file,
this );
1795 formWidget->setWindowFlags( Qt::Widget );
1796 layout->addWidget( formWidget );
1799 mButtonBox = findChild<QDialogButtonBox *>();
1802 formWidget->installEventFilter(
this );
1807 QgsTabWidget *tabWidget =
nullptr;
1814 int columnCount = 1;
1815 bool hasRootFields =
false;
1816 bool addSpacer =
true;
1818 const QList<QgsAttributeEditorElement *> tabs = mLayer->editFormConfig().tabs();
1820 for ( QgsAttributeEditorElement *widgDef : tabs )
1824 QgsAttributeEditorContainer *containerDef =
dynamic_cast<QgsAttributeEditorContainer *
>( widgDef );
1825 if ( !containerDef )
1828 switch ( containerDef->
type() )
1832 tabWidget =
nullptr;
1833 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1834 if ( widgetInfo.labelStyle.overrideColor )
1836 if ( widgetInfo.labelStyle.color.isValid() )
1838 widgetInfo.widget->setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1841 if ( widgetInfo.labelStyle.overrideFont )
1843 widgetInfo.widget->setFont( widgetInfo.labelStyle.font );
1846 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1847 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1849 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1851 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1853 layout->setRowStretch( row, widgDef->verticalStretch() );
1858 if ( widgetInfo.expandingNeeded )
1861 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
1865 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
1879 tabWidget =
nullptr;
1880 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1881 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1882 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1884 layout->setRowStretch( row, widgDef->verticalStretch() );
1889 if ( widgetInfo.expandingNeeded )
1892 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
1896 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
1899 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1901 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1916 tabWidget =
new QgsTabWidget();
1917 layout->addWidget( tabWidget, row, column, 1, 2 );
1921 QWidget *tabPage =
new QWidget( tabWidget );
1922 tabWidget->addTab( tabPage, widgDef->name() );
1923 tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
1927 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1929 QGridLayout *tabPageLayout =
new QGridLayout();
1930 tabPage->setLayout( tabPageLayout );
1932 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1933 if ( widgetInfo.expandingNeeded )
1936 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
1938 tabPageLayout->addWidget( widgetInfo.widget );
1945 hasRootFields =
true;
1946 tabWidget =
nullptr;
1947 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1948 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox();
1950 if ( widgetInfo.showLabel )
1952 if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
1954 collapsibleGroupBox->
setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1957 if ( widgetInfo.labelStyle.overrideFont )
1959 collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
1962 collapsibleGroupBox->setTitle( widgetInfo.labelText );
1965 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1966 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
1967 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1969 QVBoxLayout *
c =
new QVBoxLayout();
1970 c->addWidget( collapsibleGroupBox );
1971 layout->addLayout(
c, row, column, 1, 2 );
1973 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1974 layout->setRowStretch( row, widgDef->verticalStretch() );
1975 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1976 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1985 hasRootFields =
true;
1986 tabWidget =
nullptr;
1987 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1988 QLabel *label =
new QLabel( widgetInfo.labelText );
1990 if ( widgetInfo.labelStyle.overrideColor )
1992 if ( widgetInfo.labelStyle.color.isValid() )
1994 label->setStyleSheet( u
"QLabel { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1998 if ( widgetInfo.labelStyle.overrideFont )
2000 label->setFont( widgetInfo.labelStyle.font );
2003 label->setToolTip( widgetInfo.toolTip );
2004 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2006 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2009 label->setBuddy( widgetInfo.widget );
2012 if ( widgetInfo.widget
2013 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2014 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2015 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2018 if ( !widgetInfo.showLabel )
2020 QVBoxLayout *
c =
new QVBoxLayout();
2021 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2022 c->addWidget( widgetInfo.widget );
2023 layout->addLayout(
c, row, column, 1, 2 );
2025 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2027 layout->setRowStretch( row, widgDef->verticalStretch() );
2030 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
2032 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
2037 else if ( widgetInfo.labelOnTop )
2039 QVBoxLayout *
c =
new QVBoxLayout();
2040 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2041 c->addWidget( label );
2042 c->addWidget( widgetInfo.widget );
2043 layout->addLayout(
c, row, column, 1, 2 );
2045 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2047 layout->setRowStretch( row, widgDef->verticalStretch() );
2050 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
2052 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
2059 const int widgetColumn = column + 1;
2060 layout->addWidget( label, row, column++ );
2061 layout->addWidget( widgetInfo.widget, row, column++ );
2063 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2065 layout->setRowStretch( row, widgDef->verticalStretch() );
2068 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( widgetColumn ) )
2070 layout->setColumnStretch( widgetColumn, widgDef->horizontalStretch() );
2077 const QgsAttributeEditorField *fieldElement {
static_cast<QgsAttributeEditorField *
>( widgDef ) };
2078 const int fieldIdx = fieldElement->
idx();
2079 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
2081 const QString fieldName { mLayer->fields().at( fieldIdx ).name() };
2085 if ( property.isActive() )
2087 mLabelDataDefinedProperties[label] = property;
2093 if ( property.isActive() )
2095 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2102 if ( column >= columnCount * 2 )
2109 if ( hasRootFields && addSpacer )
2111 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2112 layout->addItem( spacerItem, row, 0 );
2113 layout->setRowStretch( row, 1 );
2116 formWidget = container;
2125 formWidget =
new QWidget(
this );
2126 QGridLayout *gridLayout =
new QGridLayout( formWidget );
2127 formWidget->setLayout( gridLayout );
2132 QgsScrollArea *scrollArea =
new QgsScrollArea(
this );
2133 scrollArea->setWidget( formWidget );
2134 scrollArea->setWidgetResizable(
true );
2135 scrollArea->setFrameShape( QFrame::NoFrame );
2136 scrollArea->setFrameShadow( QFrame::Plain );
2137 scrollArea->setFocusProxy(
this );
2138 layout->addWidget( scrollArea );
2142 layout->addWidget( formWidget );
2147 const QgsFields fields = mLayer->fields();
2152 QSet<int> compositeHiddenFields;
2154 for (
const QgsRelation &rel : referencingRelations )
2159 const QList<QgsRelation::FieldPair> fieldPairs = rel.
fieldPairs();
2160 if ( fieldPairs.size() > 1 )
2162 for (
int i = 1; i < fieldPairs.size(); i++ )
2164 const int idx = fields.
lookupField( fieldPairs.at( i ).referencingField() );
2166 compositeHiddenFields.insert( idx );
2171 for (
const QgsField &field : fields )
2173 int idx = fields.lookupField( field.name() );
2177 if ( compositeHiddenFields.contains( idx ) )
2181 QString fieldName = mLayer->attributeDisplayName( idx );
2182 QString labelText = fieldName;
2183 labelText.replace(
'&',
"&&"_L1 );
2187 if ( widgetSetup.
type() ==
"Hidden"_L1 )
2190 bool labelOnTop = mLayer->editFormConfig().labelOnTop( idx );
2193 QLabel *label =
new QLabel( labelText );
2195 QSvgWidget *i =
new QSvgWidget();
2196 i->setFixedSize( 18, 18 );
2201 if ( property.isActive() )
2203 mLabelDataDefinedProperties[label] = property;
2209 QWidget *w =
nullptr;
2212 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2214 mFormEditorWidgets.insert( idx, formWidget );
2215 mFormWidgets.append( formWidget );
2221 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2222 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2223 if ( rememberLastUsedValues.contains( idx ) )
2225 remember = rememberLastUsedValues[idx];
2230 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2231 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2232 rememberLastUsedValues[idx] = remember;
2233 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2239 label->setBuddy( eww->
widget() );
2244 if ( property.isActive() )
2246 mEditableDataDefinedProperties[formWidget] = property;
2252 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() ) ) );
2257 w->setObjectName( field.name() );
2261 mWidgets.append( eww );
2262 mIconMap[eww->
widget()] = i;
2267 gridLayout->addWidget( label, row++, 0, 1, 2 );
2268 gridLayout->addWidget( w, row++, 0, 1, 2 );
2269 gridLayout->addWidget( i, row++, 0, 1, 2 );
2273 gridLayout->addWidget( label, row, 0 );
2274 gridLayout->addWidget( w, row, 1 );
2275 gridLayout->addWidget( i, row++, 2 );
2280 for (
const QgsRelation &rel : relations )
2282 QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( u
"relation_editor"_s, rel, mContext );
2284 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2287 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox( rel.
name() );
2288 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
2289 collapsibleGroupBoxLayout->addWidget( formWidget );
2290 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
2292 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
2294 mWidgets.append( rww );
2295 mFormWidgets.append( formWidget );
2300 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2301 gridLayout->addItem( spacerItem, row, 0 );
2302 gridLayout->setRowStretch( row, 1 );
2308 updateFieldDependencies();
2312 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
2313 mButtonBox->setObjectName( u
"buttonBox"_s );
2314 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2316 mButtonBox->setVisible( buttonBoxVisible );
2318 if ( !mSearchButtonBox )
2320 mSearchButtonBox =
new QWidget();
2321 QHBoxLayout *boxLayout =
new QHBoxLayout();
2322 boxLayout->setContentsMargins( 0, 0, 0, 0 );
2323 mSearchButtonBox->setLayout( boxLayout );
2324 mSearchButtonBox->setObjectName( u
"searchButtonBox"_s );
2326 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
2328 boxLayout->addWidget( clearButton );
2329 boxLayout->addStretch( 1 );
2331 QPushButton *flashButton =
new QPushButton();
2332 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2333 flashButton->setText( tr(
"&Flash Features" ) );
2334 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
2335 boxLayout->addWidget( flashButton );
2337 QPushButton *openAttributeTableButton =
new QPushButton();
2338 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2339 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
2340 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
2341 connect( openAttributeTableButton, &QToolButton::clicked,
this, [
this] {
2344 boxLayout->addWidget( openAttributeTableButton );
2346 QPushButton *zoomButton =
new QPushButton();
2347 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2348 zoomButton->setText( tr(
"&Zoom to Features" ) );
2349 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
2350 boxLayout->addWidget( zoomButton );
2352 QToolButton *selectButton =
new QToolButton();
2353 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2354 selectButton->setText( tr(
"&Select Features" ) );
2356 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
2357 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
2358 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
2359 QMenu *selectMenu =
new QMenu( selectButton );
2360 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
2362 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
2363 selectMenu->addAction( selectAction );
2364 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
2366 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
2367 selectMenu->addAction( addSelectAction );
2368 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
2370 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
2371 selectMenu->addAction( deselectAction );
2372 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
2374 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
2375 selectMenu->addAction( filterSelectAction );
2376 selectButton->setMenu( selectMenu );
2377 boxLayout->addWidget( selectButton );
2381 QToolButton *filterButton =
new QToolButton();
2382 filterButton->setText( tr(
"Filter Features" ) );
2383 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
2384 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2385 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
2386 QMenu *filterMenu =
new QMenu( filterButton );
2387 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
2388 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
2389 filterMenu->addAction( filterAndAction );
2390 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
2391 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
2392 filterMenu->addAction( filterOrAction );
2393 filterButton->setMenu( filterMenu );
2394 boxLayout->addWidget( filterButton );
2398 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
2400 closeButton->setShortcut( Qt::Key_Escape );
2401 boxLayout->addWidget( closeButton );
2404 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2422 const auto constMInterfaces = mInterfaces;
2423 for ( QgsAttributeFormInterface *iface : constMInterfaces )
2433 QApplication::restoreOverrideCursor();
2436void QgsAttributeForm::cleanPython()
2438 if ( !mPyFormVarName.isNull() )
2440 QString expr = u
"if '%1' in locals(): del %1\n"_s.arg( mPyFormVarName );
2445void QgsAttributeForm::initPython()
2451 if ( !mLayer->editFormConfig().initFunction().isEmpty()
2457 mMessageBar->pushMessage(
2458 tr(
"Security warning" ),
2459 tr(
"The attribute form contains an embedded script which has been denied execution." ),
2465 QString initFunction = mLayer->editFormConfig().initFunction();
2466 QString initFilePath = mLayer->editFormConfig().initFilePath();
2469 switch ( mLayer->editFormConfig().initCodeSource() )
2472 if ( !initFilePath.isEmpty() )
2476 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2479 QTextStream inf( inputFile );
2480 initCode = inf.readAll();
2485 QgsLogger::warning( u
"The external python file path %1 could not be opened!"_s.arg( initFilePath ) );
2495 initCode = mLayer->editFormConfig().initCode();
2496 if ( initCode.isEmpty() )
2509 if ( !initCode.isEmpty() )
2517 mMessageBar->pushMessage( QString(), tr(
"Python macro could not be run due to missing permissions." ),
Qgis::MessageLevel::Warning );
2525 if (
QgsPythonRunner::eval( u
"len(inspect.getfullargspec(%1)[0])"_s.arg( initFunction ), numArgs ) )
2527 static int sFormId = 0;
2528 mPyFormVarName = u
"_qgis_featureform_%1_%2"_s.arg( mFormNr ).arg( sFormId++ );
2530 QString form = u
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )"_s
2531 .arg( mPyFormVarName )
2532 .arg( ( quint64 )
this );
2536 QgsDebugMsgLevel( u
"running featureForm init: %1"_s.arg( mPyFormVarName ), 2 );
2539 if ( numArgs ==
"3"_L1 )
2541 addInterface(
new QgsAttributeFormLegacyInterface( initFunction, mPyFormVarName,
this ) );
2547 msgBox.setText( 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 ) );
2550 QString expr = QString(
"%1(%2)" )
2551 .arg( mLayer->editFormInit() )
2552 .arg( mPyFormVarName );
2553 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2563 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 ) );
2571 WidgetInfo newWidgetInfo;
2573 newWidgetInfo.labelStyle = widgetDef->
labelStyle();
2575 switch ( widgetDef->
type() )
2579 const QgsAttributeEditorAction *elementDef =
dynamic_cast<const QgsAttributeEditorAction *
>( widgetDef );
2583 QgsActionWidgetWrapper *actionWrapper =
new QgsActionWidgetWrapper( mLayer,
nullptr,
this, mMessageBar );
2587 mWidgets.append( actionWrapper );
2588 newWidgetInfo.widget = actionWrapper->
widget();
2589 newWidgetInfo.showLabel =
false;
2596 const QgsAttributeEditorField *fieldDef =
dynamic_cast<const QgsAttributeEditorField *
>( widgetDef );
2600 const QgsFields fields = vl->
fields();
2602 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2607 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2608 mFormEditorWidgets.insert( fldIdx, formWidget );
2609 mFormWidgets.append( formWidget );
2615 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2616 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2617 if ( rememberLastUsedValues.contains( fldIdx ) )
2619 remember = rememberLastUsedValues[fldIdx];
2623 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2624 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2625 rememberLastUsedValues[idx] = remember;
2626 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2632 newWidgetInfo.widget = formWidget;
2633 mWidgets.append( eww );
2635 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2636 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
2639 newWidgetInfo.labelOnTop = mLayer->editFormConfig().labelOnTop( fldIdx );
2640 newWidgetInfo.labelText = mLayer->attributeDisplayName( fldIdx );
2641 newWidgetInfo.labelText.replace(
'&',
"&&"_L1 );
2642 newWidgetInfo.toolTip = u
"<b>%1</b><p>%2</p>"_s.arg( mLayer->attributeDisplayName( fldIdx ), newWidgetInfo.hint );
2643 newWidgetInfo.showLabel = widgetDef->
showLabel();
2650 const QgsAttributeEditorRelation *relDef =
static_cast<const QgsAttributeEditorRelation *
>( widgetDef );
2654 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2664 mWidgets.append( rww );
2665 mFormWidgets.append( formWidget );
2667 newWidgetInfo.widget = formWidget;
2668 newWidgetInfo.showLabel = relDef->
showLabel();
2669 newWidgetInfo.labelText = relDef->
label();
2670 if ( newWidgetInfo.labelText.isEmpty() )
2672 newWidgetInfo.labelOnTop =
true;
2678 const QgsAttributeEditorContainer *container =
dynamic_cast<const QgsAttributeEditorContainer *
>( widgetDef );
2684 if ( columnCount <= 0 )
2688 QWidget *myContainer =
nullptr;
2689 bool removeLayoutMargin =
false;
2690 switch ( container->
type() )
2694 QgsCollapsibleGroupBoxBasic *groupBox =
new QgsCollapsibleGroupBoxBasic();
2695 widgetName = u
"QGroupBox"_s;
2698 groupBox->setTitle( container->
name() );
2699 if ( newWidgetInfo.labelStyle.overrideColor )
2701 if ( newWidgetInfo.labelStyle.color.isValid() )
2703 groupBox->
setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2706 if ( newWidgetInfo.labelStyle.overrideFont )
2708 groupBox->setFont( newWidgetInfo.labelStyle.font );
2711 myContainer = groupBox;
2712 newWidgetInfo.widget = myContainer;
2719 QWidget *rowWidget =
new QWidget();
2720 widgetName = u
"Row"_s;
2721 myContainer = rowWidget;
2722 newWidgetInfo.widget = myContainer;
2723 removeLayoutMargin =
true;
2724 columnCount = container->
children().size();
2730 myContainer =
new QWidget();
2732 QgsScrollArea *scrollArea =
new QgsScrollArea( parent );
2734 scrollArea->setWidget( myContainer );
2735 scrollArea->setWidgetResizable(
true );
2736 scrollArea->setFrameShape( QFrame::NoFrame );
2737 widgetName = u
"QScrollArea QWidget"_s;
2739 newWidgetInfo.widget = scrollArea;
2746 QString style { u
"background-color: %1;"_s.arg( container->
backgroundColor().name() ) };
2747 newWidgetInfo.widget->setStyleSheet( style );
2750 QGridLayout *gbLayout =
new QGridLayout();
2751 if ( removeLayoutMargin )
2752 gbLayout->setContentsMargins( 0, 0, 0, 0 );
2753 myContainer->setLayout( gbLayout );
2757 bool addSpacer =
true;
2759 const QList<QgsAttributeEditorElement *> children = container->
children();
2761 for ( QgsAttributeEditorElement *childDef : children )
2763 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2767 QgsAttributeEditorContainer *containerDef =
static_cast<QgsAttributeEditorContainer *
>( childDef );
2772 if ( childDef->verticalStretch() == 0 )
2774 if ( widgetInfo.expandingNeeded )
2776 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding );
2780 widgetInfo.widget->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Maximum );
2786 int widgetColumn = column;
2788 if ( widgetInfo.labelText.isNull() || !widgetInfo.showLabel )
2790 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2791 widgetColumn = column + 1;
2796 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2798 if ( widgetInfo.labelStyle.overrideColor )
2800 if ( widgetInfo.labelStyle.color.isValid() )
2802 mypLabel->setStyleSheet( u
"QLabel { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2806 if ( widgetInfo.labelStyle.overrideFont )
2808 mypLabel->setFont( widgetInfo.labelStyle.font );
2814 const QgsAttributeEditorField *fieldDef {
static_cast<QgsAttributeEditorField *
>( childDef ) };
2815 const QgsFields fields = vl->
fields();
2816 const int fldIdx = fieldDef->
idx();
2817 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2819 const QString fieldName { fields.
at( fldIdx ).
name() };
2823 if ( property.isActive() )
2825 mLabelDataDefinedProperties[mypLabel] = property;
2831 if ( property.isActive() )
2833 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2839 mypLabel->setToolTip( widgetInfo.toolTip );
2840 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2842 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2845 mypLabel->setBuddy( widgetInfo.widget );
2847 if ( widgetInfo.labelOnTop )
2849 widgetColumn = column + 1;
2850 QVBoxLayout *
c =
new QVBoxLayout();
2851 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2852 c->layout()->addWidget( mypLabel );
2853 c->layout()->addWidget( widgetInfo.widget );
2854 gbLayout->addLayout(
c, row, column, 1, 2 );
2859 widgetColumn = column + 1;
2860 gbLayout->addWidget( mypLabel, row, column++ );
2861 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2865 const int childHorizontalStretch = childDef->horizontalStretch();
2866 const int existingColumnStretch = gbLayout->columnStretch( widgetColumn );
2867 if ( childHorizontalStretch > 0 && childHorizontalStretch > existingColumnStretch )
2869 gbLayout->setColumnStretch( widgetColumn, childHorizontalStretch );
2872 if ( childDef->verticalStretch() > 0 && childDef->verticalStretch() > gbLayout->rowStretch( row ) )
2874 gbLayout->setRowStretch( row, childDef->verticalStretch() );
2877 if ( column >= columnCount * 2 )
2883 if ( widgetInfo.widget
2884 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2885 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2886 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2890 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
2896 QWidget *spacer =
new QWidget();
2897 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2898 gbLayout->addWidget( spacer, ++row, 0 );
2899 gbLayout->setRowStretch( row, 1 );
2902 newWidgetInfo.labelText = QString();
2903 newWidgetInfo.labelOnTop =
true;
2904 newWidgetInfo.showLabel = widgetDef->
showLabel();
2905 newWidgetInfo.expandingNeeded |= !addSpacer;
2911 const QgsAttributeEditorQmlElement *elementDef =
static_cast<const QgsAttributeEditorQmlElement *
>( widgetDef );
2913 QgsQmlWidgetWrapper *qmlWrapper =
new QgsQmlWidgetWrapper( mLayer,
nullptr,
this );
2918 mWidgets.append( qmlWrapper );
2920 newWidgetInfo.widget = qmlWrapper->
widget();
2921 newWidgetInfo.labelText = elementDef->
name();
2922 newWidgetInfo.labelOnTop =
true;
2923 newWidgetInfo.showLabel = widgetDef->
showLabel();
2929 const QgsAttributeEditorHtmlElement *elementDef =
static_cast<const QgsAttributeEditorHtmlElement *
>( widgetDef );
2931 QgsHtmlWidgetWrapper *htmlWrapper =
new QgsHtmlWidgetWrapper( mLayer,
nullptr,
this );
2935 mWidgets.append( htmlWrapper );
2937 newWidgetInfo.widget = htmlWrapper->
widget();
2938 newWidgetInfo.labelText = elementDef->
name();
2939 newWidgetInfo.labelOnTop =
true;
2940 newWidgetInfo.showLabel = widgetDef->
showLabel();
2947 const QgsAttributeEditorTextElement *elementDef =
static_cast<const QgsAttributeEditorTextElement *
>( widgetDef );
2949 QgsTextWidgetWrapper *textWrapper =
new QgsTextWidgetWrapper( mLayer,
nullptr,
this );
2953 mWidgets.append( textWrapper );
2955 newWidgetInfo.widget = textWrapper->
widget();
2956 newWidgetInfo.labelText = elementDef->
name();
2957 newWidgetInfo.labelOnTop =
false;
2958 newWidgetInfo.showLabel = widgetDef->
showLabel();
2965 const QgsAttributeEditorSpacerElement *elementDef =
static_cast<const QgsAttributeEditorSpacerElement *
>( widgetDef );
2966 QgsSpacerWidgetWrapper *spacerWrapper =
new QgsSpacerWidgetWrapper( mLayer,
nullptr,
this );
2969 mWidgets.append( spacerWrapper );
2971 newWidgetInfo.widget = spacerWrapper->
widget();
2972 newWidgetInfo.labelOnTop =
false;
2973 newWidgetInfo.showLabel =
false;
2978 QgsDebugError( u
"Unknown attribute editor widget type encountered..."_s );
2982 return newWidgetInfo;
2985void QgsAttributeForm::createWrappers()
2987 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2988 const QList<QgsField> fields = mLayer->fields().
toList();
2990 const auto constMyWidgets = myWidgets;
2991 for ( QWidget *myWidget : constMyWidgets )
2994 QVariant vRel = myWidget->property(
"qgisRelation" );
2995 if ( vRel.isValid() )
2998 QgsRelation relation = relMgr->
relation( vRel.toString() );
3001 QgsRelationWidgetWrapper *rww =
new QgsRelationWidgetWrapper( mLayer, relation, myWidget,
this );
3002 rww->
setConfig( mLayer->editFormConfig().widgetConfig( relation.
id() ) );
3005 mWidgets.append( rww );
3010 const auto constFields = fields;
3011 for (
const QgsField &field : constFields )
3013 if ( field.name() == myWidget->objectName() )
3015 int idx = mLayer->fields().lookupField( field.name() );
3018 mWidgets.append( eww );
3025void QgsAttributeForm::afterWidgetInit()
3027 bool isFirstEww =
true;
3029 const auto constMWidgets = mWidgets;
3030 for ( QgsWidgetWrapper *ww : constMWidgets )
3032 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3038 setFocusProxy( eww->
widget() );
3047 QgsRelationWidgetWrapper *relationWidgetWrapper = qobject_cast<QgsRelationWidgetWrapper *>( ww );
3048 if ( relationWidgetWrapper )
3061 if ( e->type() == QEvent::KeyPress )
3063 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
3064 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
3075void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet<int> &mixedValueFields, QHash<int, QVariant> &fieldSharedValues )
const
3077 mixedValueFields.clear();
3078 fieldSharedValues.clear();
3084 for (
int i = 0; i < mLayer->
fields().count(); ++i )
3086 if ( mixedValueFields.contains( i ) )
3091 fieldSharedValues[i] = f.
attribute( i );
3095 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
3097 fieldSharedValues.remove( i );
3098 mixedValueFields.insert( i );
3104 if ( mixedValueFields.count() == mLayer->fields().count() )
3113void QgsAttributeForm::layerSelectionChanged()
3127 resetMultiEdit(
true );
3134 mIsSettingMultiEditFeatures =
true;
3135 mMultiEditFeatureIds = fids;
3137 if ( fids.isEmpty() )
3140 QMultiMap<int, QgsAttributeFormEditorWidget *>::const_iterator wIt = mFormEditorWidgets.constBegin();
3141 for ( ; wIt != mFormEditorWidgets.constEnd(); ++wIt )
3143 wIt.value()->initialize( QVariant() );
3145 mIsSettingMultiEditFeatures =
false;
3152 QSet<int> mixedValueFields;
3153 QHash<int, QVariant> fieldSharedValues;
3154 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
3157 fit = mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( *fids.constBegin() ) );
3163 if ( mCurrentFormFeature.id() != firstFeature.
id() )
3168 const auto constMixedValueFields = mixedValueFields;
3169 for (
int fieldIndex : std::as_const( mixedValueFields ) )
3171 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( fieldIndex );
3172 if ( formEditorWidgets.isEmpty() )
3175 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3176 QVariantList additionalFieldValues;
3177 for (
const QString &additionalField : additionalFields )
3178 additionalFieldValues << firstFeature.
attribute( additionalField );
3181 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
3183 QHash<int, QVariant>::const_iterator sharedValueIt = fieldSharedValues.constBegin();
3184 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
3186 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( sharedValueIt.key() );
3187 if ( formEditorWidgets.isEmpty() )
3191 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3192 for (
const QString &additionalField : additionalFields )
3194 int index = mLayer->fields().indexFromName( additionalField );
3195 if ( constMixedValueFields.contains( index ) )
3202 QVariantList additionalFieldValues;
3205 for (
const QString &additionalField : additionalFields )
3206 additionalFieldValues << firstFeature.
attribute( additionalField );
3208 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
3212 for (
const QString &additionalField : additionalFields )
3214 int index = mLayer->fields().indexFromName( additionalField );
3215 Q_ASSERT( fieldSharedValues.contains( index ) );
3216 additionalFieldValues << fieldSharedValues.value( index );
3219 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
3223 setMultiEditFeatureIdsRelations( fids );
3225 mIsSettingMultiEditFeatures =
false;
3230 if ( mOwnsMessageBar )
3232 mOwnsMessageBar =
false;
3233 mMessageBar = messageBar;
3243 QStringList filters;
3246 QString filter = widget->currentFilterExpression();
3247 if ( !filter.isNull() )
3248 filters <<
'(' + filter +
')';
3251 return filters.join(
" AND "_L1 );
3256 mExtraContextScope.reset( extraScope );
3261 const bool newVisibility = expression.
evaluate( expressionContext ).toBool();
3271 widget->setVisible( newVisibility );
3274 isVisible = newVisibility;
3277 const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
3279 if ( collapsedExpression.isValid() && !collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
3281 if ( QgsCollapsibleGroupBoxBasic * collapsibleGroupBox { qobject_cast<QgsCollapsibleGroupBoxBasic *>( widget ) } )
3283 collapsibleGroupBox->
setCollapsed( newCollapsedState );
3284 isCollapsed = newCollapsedState;
3289void QgsAttributeForm::updateJoinedFields(
const QgsEditorWidgetWrapper &eww )
3294 QgsFeature formFeature;
3298 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
3301 const QString hint = tr(
"No feature joined" );
3302 const auto constInfos = infos;
3303 for (
const QgsVectorLayerJoinInfo *info : constInfos )
3305 if ( !info->isDynamicFormEnabled() )
3308 QgsFeature joinFeature = mLayer->joinBuffer()->joinedFeatureOf( info, formFeature );
3310 mJoinedFeatures[info] = joinFeature;
3312 if ( info->hasSubset() )
3316 const auto constSubsetNames = subsetNames;
3317 for (
const QString &field : constSubsetNames )
3319 QString prefixedName = info->prefixedFieldName( field );
3321 QString hintText = hint;
3334 const QgsFields joinFields = joinFeature.
fields();
3335 for (
const QgsField &field : joinFields )
3337 QString prefixedName = info->prefixedFieldName( field );
3339 QString hintText = hint;
3353bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
3358void QgsAttributeForm::updateFieldDependencies()
3360 mDefaultValueDependencies.clear();
3361 mVirtualFieldsDependencies.clear();
3362 mRelatedLayerFieldsDependencies.clear();
3363 mParentDependencies.clear();
3366 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3368 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3372 updateFieldDependenciesParent( eww );
3373 updateFieldDependenciesDefaultValue( eww );
3374 updateFieldDependenciesVirtualFields( eww );
3375 updateRelatedLayerFieldsDependencies( eww );
3379void QgsAttributeForm::updateFieldDependenciesDefaultValue( QgsEditorWidgetWrapper *eww )
3383 if ( exp.needsGeometry() )
3384 mNeedsGeometry =
true;
3389 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3391 for (
const int id : allAttributeIds )
3393 mDefaultValueDependencies.insertMulti(
id, eww );
3399 const QSet<QString> referencedColumns = exp.referencedColumns();
3400 for (
const QString &referencedColumn : referencedColumns )
3402 mDefaultValueDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3407void QgsAttributeForm::updateFieldDependenciesVirtualFields( QgsEditorWidgetWrapper *eww )
3410 if ( expressionField.isEmpty() )
3413 QgsExpression exp( expressionField );
3415 if ( exp.needsGeometry() )
3416 mNeedsGeometry =
true;
3421 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3423 for (
const int id : allAttributeIds )
3425 mVirtualFieldsDependencies.insertMulti(
id, eww );
3431 const QSet<QString> referencedColumns = exp.referencedColumns();
3432 for (
const QString &referencedColumn : referencedColumns )
3434 mVirtualFieldsDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3439void QgsAttributeForm::updateRelatedLayerFieldsDependencies( QgsEditorWidgetWrapper *eww )
3444 if ( expressionField.contains( u
"relation_aggregate"_s )
3445 || expressionField.contains( u
"get_features"_s ) )
3446 mRelatedLayerFieldsDependencies.insert( eww );
3450 mRelatedLayerFieldsDependencies.clear();
3452 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3454 QgsEditorWidgetWrapper *editorWidgetWrapper = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3455 if ( !editorWidgetWrapper )
3458 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
3463void QgsAttributeForm::updateFieldDependenciesParent( QgsEditorWidgetWrapper *eww )
3468 const QSet<QString> referencedVariablesAndFunctions = expression.referencedVariables() + expression.referencedFunctions();
3469 for (
const QString &referenced : referencedVariablesAndFunctions )
3471 if ( referenced.startsWith(
"current_parent"_L1 ) )
3473 mParentDependencies.insert( eww );
3480void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
3482 for ( QgsAttributeFormWidget *formWidget : mFormWidgets )
3484 QgsAttributeFormRelationEditorWidget *relationEditorWidget =
dynamic_cast<QgsAttributeFormRelationEditorWidget *
>( formWidget );
3485 if ( !relationEditorWidget )
3492void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww )
3498 mIconMap[eww->
widget()]->hide();
3500 if ( !eww->
widget()->isEnabled() && mLayer->isEditable() )
3505 const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( eww->
fieldIdx(), mLayer->fields(), srcFieldIndex );
3512 const QString file = u
"/mIconJoinNotEditable.svg"_s;
3513 const QString tooltip = tr(
"Join settings do not allow editing" );
3514 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3518 const QString file = u
"mIconJoinHasNotUpsertOnEdit.svg"_s;
3519 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
3520 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3524 const QString file = u
"/mIconJoinedLayerNotEditable.svg"_s;
3525 const QString tooltip = tr(
"Joined layer is not toggled editable" );
3526 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3532void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
3535 sw->setToolTip( tooltip );
3542 QgsDebugMsgLevel( u
"reuseAllLastValues: %1"_s.arg( reuseAllLastValues ), 2 );
3546 for (
int idx = 0; idx < fields.
count(); ++idx )
3548 if ( attributes.contains( idx ) )
3550 initialAttributeValues.insert( idx, attributes.value( idx ) );
3554 const QVariant lastUsedValuesVariant =
layer->property(
"AttributeFormLastUsedValues" );
3556 if ( lastUsedValues.contains( idx ) &&
layer->dataProvider() &&
layer->dataProvider()->defaultValueClause( idx ) != lastUsedValues[idx] )
3558 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)