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() );
1867 tabWidget =
nullptr;
1868 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1869 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1870 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1872 layout->setRowStretch( row, widgDef->verticalStretch() );
1875 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1877 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1892 tabWidget =
new QgsTabWidget();
1893 layout->addWidget( tabWidget, row, column, 1, 2 );
1897 QWidget *tabPage =
new QWidget( tabWidget );
1899 tabWidget->addTab( tabPage, widgDef->name() );
1900 tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
1904 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1906 QGridLayout *tabPageLayout =
new QGridLayout();
1907 tabPage->setLayout( tabPageLayout );
1909 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1910 tabPageLayout->addWidget( widgetInfo.widget );
1917 hasRootFields =
true;
1918 tabWidget =
nullptr;
1919 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1920 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox();
1922 if ( widgetInfo.showLabel )
1924 if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
1926 collapsibleGroupBox->
setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1929 if ( widgetInfo.labelStyle.overrideFont )
1931 collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
1934 collapsibleGroupBox->setTitle( widgetInfo.labelText );
1937 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1938 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
1939 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1941 QVBoxLayout *
c =
new QVBoxLayout();
1942 c->addWidget( collapsibleGroupBox );
1943 layout->addLayout(
c, row, column, 1, 2 );
1945 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1946 layout->setRowStretch( row, widgDef->verticalStretch() );
1947 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1948 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1957 hasRootFields =
true;
1958 tabWidget =
nullptr;
1959 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1960 QLabel *label =
new QLabel( widgetInfo.labelText );
1962 if ( widgetInfo.labelStyle.overrideColor )
1964 if ( widgetInfo.labelStyle.color.isValid() )
1966 label->setStyleSheet( u
"QLabel { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1970 if ( widgetInfo.labelStyle.overrideFont )
1972 label->setFont( widgetInfo.labelStyle.font );
1975 label->setToolTip( widgetInfo.toolTip );
1976 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1978 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1981 label->setBuddy( widgetInfo.widget );
1984 if ( widgetInfo.widget
1985 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
1986 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
1987 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
1990 if ( !widgetInfo.showLabel )
1992 QVBoxLayout *
c =
new QVBoxLayout();
1993 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1994 c->addWidget( widgetInfo.widget );
1995 layout->addLayout(
c, row, column, 1, 2 );
1997 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1999 layout->setRowStretch( row, widgDef->verticalStretch() );
2002 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
2004 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
2009 else if ( widgetInfo.labelOnTop )
2011 QVBoxLayout *
c =
new QVBoxLayout();
2012 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2013 c->addWidget( label );
2014 c->addWidget( widgetInfo.widget );
2015 layout->addLayout(
c, row, column, 1, 2 );
2017 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2019 layout->setRowStretch( row, widgDef->verticalStretch() );
2022 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
2024 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
2031 const int widgetColumn = column + 1;
2032 layout->addWidget( label, row, column++ );
2033 layout->addWidget( widgetInfo.widget, row, column++ );
2035 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
2037 layout->setRowStretch( row, widgDef->verticalStretch() );
2040 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( widgetColumn ) )
2042 layout->setColumnStretch( widgetColumn, widgDef->horizontalStretch() );
2049 const QgsAttributeEditorField *fieldElement {
static_cast<QgsAttributeEditorField *
>( widgDef ) };
2050 const int fieldIdx = fieldElement->
idx();
2051 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
2053 const QString fieldName { mLayer->fields().at( fieldIdx ).name() };
2057 if ( property.isActive() )
2059 mLabelDataDefinedProperties[label] = property;
2065 if ( property.isActive() )
2067 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2074 if ( column >= columnCount * 2 )
2081 if ( hasRootFields && addSpacer )
2083 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2084 layout->addItem( spacerItem, row, 0 );
2085 layout->setRowStretch( row, 1 );
2088 formWidget = container;
2097 formWidget =
new QWidget(
this );
2098 QGridLayout *gridLayout =
new QGridLayout( formWidget );
2099 formWidget->setLayout( gridLayout );
2104 QgsScrollArea *scrollArea =
new QgsScrollArea(
this );
2105 scrollArea->setWidget( formWidget );
2106 scrollArea->setWidgetResizable(
true );
2107 scrollArea->setFrameShape( QFrame::NoFrame );
2108 scrollArea->setFrameShadow( QFrame::Plain );
2109 scrollArea->setFocusProxy(
this );
2110 layout->addWidget( scrollArea );
2114 layout->addWidget( formWidget );
2119 const QgsFields fields = mLayer->fields();
2121 for (
const QgsField &field : fields )
2123 int idx = fields.lookupField( field.name() );
2128 QString fieldName = mLayer->attributeDisplayName( idx );
2129 QString labelText = fieldName;
2130 labelText.replace(
'&',
"&&"_L1 );
2134 if ( widgetSetup.
type() ==
"Hidden"_L1 )
2137 bool labelOnTop = mLayer->editFormConfig().labelOnTop( idx );
2140 QLabel *label =
new QLabel( labelText );
2142 QSvgWidget *i =
new QSvgWidget();
2143 i->setFixedSize( 18, 18 );
2148 if ( property.isActive() )
2150 mLabelDataDefinedProperties[label] = property;
2156 QWidget *w =
nullptr;
2159 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2161 mFormEditorWidgets.insert( idx, formWidget );
2162 mFormWidgets.append( formWidget );
2168 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2169 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2170 if ( rememberLastUsedValues.contains( idx ) )
2172 remember = rememberLastUsedValues[idx];
2177 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2178 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2179 rememberLastUsedValues[idx] = remember;
2180 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2186 label->setBuddy( eww->
widget() );
2191 if ( property.isActive() )
2193 mEditableDataDefinedProperties[formWidget] = property;
2199 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() ) ) );
2204 w->setObjectName( field.name() );
2208 mWidgets.append( eww );
2209 mIconMap[eww->
widget()] = i;
2214 gridLayout->addWidget( label, row++, 0, 1, 2 );
2215 gridLayout->addWidget( w, row++, 0, 1, 2 );
2216 gridLayout->addWidget( i, row++, 0, 1, 2 );
2220 gridLayout->addWidget( label, row, 0 );
2221 gridLayout->addWidget( w, row, 1 );
2222 gridLayout->addWidget( i, row++, 2 );
2227 for (
const QgsRelation &rel : relations )
2229 QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( u
"relation_editor"_s, rel, mContext );
2231 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2234 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox( rel.
name() );
2235 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
2236 collapsibleGroupBoxLayout->addWidget( formWidget );
2237 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
2239 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
2241 mWidgets.append( rww );
2242 mFormWidgets.append( formWidget );
2247 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2248 gridLayout->addItem( spacerItem, row, 0 );
2249 gridLayout->setRowStretch( row, 1 );
2255 updateFieldDependencies();
2259 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
2260 mButtonBox->setObjectName( u
"buttonBox"_s );
2261 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2263 mButtonBox->setVisible( buttonBoxVisible );
2265 if ( !mSearchButtonBox )
2267 mSearchButtonBox =
new QWidget();
2268 QHBoxLayout *boxLayout =
new QHBoxLayout();
2269 boxLayout->setContentsMargins( 0, 0, 0, 0 );
2270 mSearchButtonBox->setLayout( boxLayout );
2271 mSearchButtonBox->setObjectName( u
"searchButtonBox"_s );
2273 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
2275 boxLayout->addWidget( clearButton );
2276 boxLayout->addStretch( 1 );
2278 QPushButton *flashButton =
new QPushButton();
2279 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2280 flashButton->setText( tr(
"&Flash Features" ) );
2281 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
2282 boxLayout->addWidget( flashButton );
2284 QPushButton *openAttributeTableButton =
new QPushButton();
2285 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2286 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
2287 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
2288 connect( openAttributeTableButton, &QToolButton::clicked,
this, [
this] {
2291 boxLayout->addWidget( openAttributeTableButton );
2293 QPushButton *zoomButton =
new QPushButton();
2294 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2295 zoomButton->setText( tr(
"&Zoom to Features" ) );
2296 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
2297 boxLayout->addWidget( zoomButton );
2299 QToolButton *selectButton =
new QToolButton();
2300 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2301 selectButton->setText( tr(
"&Select Features" ) );
2303 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
2304 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
2305 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
2306 QMenu *selectMenu =
new QMenu( selectButton );
2307 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
2309 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
2310 selectMenu->addAction( selectAction );
2311 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
2313 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
2314 selectMenu->addAction( addSelectAction );
2315 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
2317 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
2318 selectMenu->addAction( deselectAction );
2319 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
2321 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
2322 selectMenu->addAction( filterSelectAction );
2323 selectButton->setMenu( selectMenu );
2324 boxLayout->addWidget( selectButton );
2328 QToolButton *filterButton =
new QToolButton();
2329 filterButton->setText( tr(
"Filter Features" ) );
2330 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
2331 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2332 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
2333 QMenu *filterMenu =
new QMenu( filterButton );
2334 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
2335 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
2336 filterMenu->addAction( filterAndAction );
2337 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
2338 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
2339 filterMenu->addAction( filterOrAction );
2340 filterButton->setMenu( filterMenu );
2341 boxLayout->addWidget( filterButton );
2345 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
2347 closeButton->setShortcut( Qt::Key_Escape );
2348 boxLayout->addWidget( closeButton );
2351 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2369 const auto constMInterfaces = mInterfaces;
2370 for ( QgsAttributeFormInterface *iface : constMInterfaces )
2380 QApplication::restoreOverrideCursor();
2383void QgsAttributeForm::cleanPython()
2385 if ( !mPyFormVarName.isNull() )
2387 QString expr = u
"if '%1' in locals(): del %1\n"_s.arg( mPyFormVarName );
2392void QgsAttributeForm::initPython()
2398 if ( !mLayer->editFormConfig().initFunction().isEmpty()
2404 mMessageBar->pushMessage(
2405 tr(
"Security warning" ),
2406 tr(
"The attribute form contains an embedded script which has been denied execution." ),
2412 QString initFunction = mLayer->editFormConfig().initFunction();
2413 QString initFilePath = mLayer->editFormConfig().initFilePath();
2416 switch ( mLayer->editFormConfig().initCodeSource() )
2419 if ( !initFilePath.isEmpty() )
2423 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2426 QTextStream inf( inputFile );
2427 initCode = inf.readAll();
2432 QgsLogger::warning( u
"The external python file path %1 could not be opened!"_s.arg( initFilePath ) );
2442 initCode = mLayer->editFormConfig().initCode();
2443 if ( initCode.isEmpty() )
2456 if ( !initCode.isEmpty() )
2464 mMessageBar->pushMessage( QString(), tr(
"Python macro could not be run due to missing permissions." ),
Qgis::MessageLevel::Warning );
2472 if (
QgsPythonRunner::eval( u
"len(inspect.getfullargspec(%1)[0])"_s.arg( initFunction ), numArgs ) )
2474 static int sFormId = 0;
2475 mPyFormVarName = u
"_qgis_featureform_%1_%2"_s.arg( mFormNr ).arg( sFormId++ );
2477 QString form = u
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )"_s
2478 .arg( mPyFormVarName )
2479 .arg( ( quint64 )
this );
2483 QgsDebugMsgLevel( u
"running featureForm init: %1"_s.arg( mPyFormVarName ), 2 );
2486 if ( numArgs ==
"3"_L1 )
2488 addInterface(
new QgsAttributeFormLegacyInterface( initFunction, mPyFormVarName,
this ) );
2494 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 ) );
2497 QString expr = QString(
"%1(%2)" )
2498 .arg( mLayer->editFormInit() )
2499 .arg( mPyFormVarName );
2500 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2510 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 ) );
2518 WidgetInfo newWidgetInfo;
2520 newWidgetInfo.labelStyle = widgetDef->
labelStyle();
2522 switch ( widgetDef->
type() )
2526 const QgsAttributeEditorAction *elementDef =
dynamic_cast<const QgsAttributeEditorAction *
>( widgetDef );
2530 QgsActionWidgetWrapper *actionWrapper =
new QgsActionWidgetWrapper( mLayer,
nullptr,
this, mMessageBar );
2534 mWidgets.append( actionWrapper );
2535 newWidgetInfo.widget = actionWrapper->
widget();
2536 newWidgetInfo.showLabel =
false;
2543 const QgsAttributeEditorField *fieldDef =
dynamic_cast<const QgsAttributeEditorField *
>( widgetDef );
2547 const QgsFields fields = vl->
fields();
2549 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2554 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2555 mFormEditorWidgets.insert( fldIdx, formWidget );
2556 mFormWidgets.append( formWidget );
2562 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2563 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2564 if ( rememberLastUsedValues.contains( fldIdx ) )
2566 remember = rememberLastUsedValues[fldIdx];
2570 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2571 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2572 rememberLastUsedValues[idx] = remember;
2573 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2579 newWidgetInfo.widget = formWidget;
2580 mWidgets.append( eww );
2582 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2583 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
2586 newWidgetInfo.labelOnTop = mLayer->editFormConfig().labelOnTop( fldIdx );
2587 newWidgetInfo.labelText = mLayer->attributeDisplayName( fldIdx );
2588 newWidgetInfo.labelText.replace(
'&',
"&&"_L1 );
2589 newWidgetInfo.toolTip = u
"<b>%1</b><p>%2</p>"_s.arg( mLayer->attributeDisplayName( fldIdx ), newWidgetInfo.hint );
2590 newWidgetInfo.showLabel = widgetDef->
showLabel();
2597 const QgsAttributeEditorRelation *relDef =
static_cast<const QgsAttributeEditorRelation *
>( widgetDef );
2601 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2611 mWidgets.append( rww );
2612 mFormWidgets.append( formWidget );
2614 newWidgetInfo.widget = formWidget;
2615 newWidgetInfo.showLabel = relDef->
showLabel();
2616 newWidgetInfo.labelText = relDef->
label();
2617 if ( newWidgetInfo.labelText.isEmpty() )
2619 newWidgetInfo.labelOnTop =
true;
2625 const QgsAttributeEditorContainer *container =
dynamic_cast<const QgsAttributeEditorContainer *
>( widgetDef );
2631 if ( columnCount <= 0 )
2635 QWidget *myContainer =
nullptr;
2636 bool removeLayoutMargin =
false;
2637 switch ( container->
type() )
2641 QgsCollapsibleGroupBoxBasic *groupBox =
new QgsCollapsibleGroupBoxBasic();
2642 widgetName = u
"QGroupBox"_s;
2645 groupBox->setTitle( container->
name() );
2646 if ( newWidgetInfo.labelStyle.overrideColor )
2648 if ( newWidgetInfo.labelStyle.color.isValid() )
2650 groupBox->
setStyleSheet( u
"QGroupBox::title { color: %1; }"_s.arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2653 if ( newWidgetInfo.labelStyle.overrideFont )
2655 groupBox->setFont( newWidgetInfo.labelStyle.font );
2658 myContainer = groupBox;
2659 newWidgetInfo.widget = myContainer;
2666 QWidget *rowWidget =
new QWidget();
2667 widgetName = u
"Row"_s;
2668 myContainer = rowWidget;
2669 newWidgetInfo.widget = myContainer;
2670 removeLayoutMargin =
true;
2671 columnCount = container->
children().size();
2677 myContainer =
new QWidget();
2679 QgsScrollArea *scrollArea =
new QgsScrollArea( parent );
2681 scrollArea->setWidget( myContainer );
2682 scrollArea->setWidgetResizable(
true );
2683 scrollArea->setFrameShape( QFrame::NoFrame );
2684 widgetName = u
"QScrollArea QWidget"_s;
2686 newWidgetInfo.widget = scrollArea;
2693 QString style { u
"background-color: %1;"_s.arg( container->
backgroundColor().name() ) };
2694 newWidgetInfo.widget->setStyleSheet( style );
2697 QGridLayout *gbLayout =
new QGridLayout();
2698 if ( removeLayoutMargin )
2699 gbLayout->setContentsMargins( 0, 0, 0, 0 );
2700 myContainer->setLayout( gbLayout );
2704 bool addSpacer =
true;
2706 const QList<QgsAttributeEditorElement *> children = container->
children();
2708 for ( QgsAttributeEditorElement *childDef : children )
2710 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2714 QgsAttributeEditorContainer *containerDef =
static_cast<QgsAttributeEditorContainer *
>( childDef );
2722 int widgetColumn = column;
2724 if ( widgetInfo.labelText.isNull() || !widgetInfo.showLabel )
2726 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2727 widgetColumn = column + 1;
2732 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2734 if ( widgetInfo.labelStyle.overrideColor )
2736 if ( widgetInfo.labelStyle.color.isValid() )
2738 mypLabel->setStyleSheet( u
"QLabel { color: %1; }"_s.arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2742 if ( widgetInfo.labelStyle.overrideFont )
2744 mypLabel->setFont( widgetInfo.labelStyle.font );
2750 const QgsAttributeEditorField *fieldDef {
static_cast<QgsAttributeEditorField *
>( childDef ) };
2751 const QgsFields fields = vl->
fields();
2752 const int fldIdx = fieldDef->
idx();
2753 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2755 const QString fieldName { fields.
at( fldIdx ).
name() };
2759 if ( property.isActive() )
2761 mLabelDataDefinedProperties[mypLabel] = property;
2767 if ( property.isActive() )
2769 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2775 mypLabel->setToolTip( widgetInfo.toolTip );
2776 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2778 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2781 mypLabel->setBuddy( widgetInfo.widget );
2783 if ( widgetInfo.labelOnTop )
2785 widgetColumn = column + 1;
2786 QVBoxLayout *
c =
new QVBoxLayout();
2787 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2788 c->layout()->addWidget( mypLabel );
2789 c->layout()->addWidget( widgetInfo.widget );
2790 gbLayout->addLayout(
c, row, column, 1, 2 );
2795 widgetColumn = column + 1;
2796 gbLayout->addWidget( mypLabel, row, column++ );
2797 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2801 const int childHorizontalStretch = childDef->horizontalStretch();
2802 const int existingColumnStretch = gbLayout->columnStretch( widgetColumn );
2803 if ( childHorizontalStretch > 0 && childHorizontalStretch > existingColumnStretch )
2805 gbLayout->setColumnStretch( widgetColumn, childHorizontalStretch );
2808 if ( childDef->verticalStretch() > 0 && childDef->verticalStretch() > gbLayout->rowStretch( row ) )
2810 gbLayout->setRowStretch( row, childDef->verticalStretch() );
2813 if ( column >= columnCount * 2 )
2819 if ( widgetInfo.widget
2820 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2821 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2822 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2826 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
2832 QWidget *spacer =
new QWidget();
2833 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2834 gbLayout->addWidget( spacer, ++row, 0 );
2835 gbLayout->setRowStretch( row, 1 );
2838 newWidgetInfo.labelText = QString();
2839 newWidgetInfo.labelOnTop =
true;
2840 newWidgetInfo.showLabel = widgetDef->
showLabel();
2846 const QgsAttributeEditorQmlElement *elementDef =
static_cast<const QgsAttributeEditorQmlElement *
>( widgetDef );
2848 QgsQmlWidgetWrapper *qmlWrapper =
new QgsQmlWidgetWrapper( mLayer,
nullptr,
this );
2853 mWidgets.append( qmlWrapper );
2855 newWidgetInfo.widget = qmlWrapper->
widget();
2856 newWidgetInfo.labelText = elementDef->
name();
2857 newWidgetInfo.labelOnTop =
true;
2858 newWidgetInfo.showLabel = widgetDef->
showLabel();
2864 const QgsAttributeEditorHtmlElement *elementDef =
static_cast<const QgsAttributeEditorHtmlElement *
>( widgetDef );
2866 QgsHtmlWidgetWrapper *htmlWrapper =
new QgsHtmlWidgetWrapper( mLayer,
nullptr,
this );
2870 mWidgets.append( htmlWrapper );
2872 newWidgetInfo.widget = htmlWrapper->
widget();
2873 newWidgetInfo.labelText = elementDef->
name();
2874 newWidgetInfo.labelOnTop =
true;
2875 newWidgetInfo.showLabel = widgetDef->
showLabel();
2882 const QgsAttributeEditorTextElement *elementDef =
static_cast<const QgsAttributeEditorTextElement *
>( widgetDef );
2884 QgsTextWidgetWrapper *textWrapper =
new QgsTextWidgetWrapper( mLayer,
nullptr,
this );
2888 mWidgets.append( textWrapper );
2890 newWidgetInfo.widget = textWrapper->
widget();
2891 newWidgetInfo.labelText = elementDef->
name();
2892 newWidgetInfo.labelOnTop =
false;
2893 newWidgetInfo.showLabel = widgetDef->
showLabel();
2900 const QgsAttributeEditorSpacerElement *elementDef =
static_cast<const QgsAttributeEditorSpacerElement *
>( widgetDef );
2901 QgsSpacerWidgetWrapper *spacerWrapper =
new QgsSpacerWidgetWrapper( mLayer,
nullptr,
this );
2904 mWidgets.append( spacerWrapper );
2906 newWidgetInfo.widget = spacerWrapper->
widget();
2907 newWidgetInfo.labelOnTop =
false;
2908 newWidgetInfo.showLabel =
false;
2913 QgsDebugError( u
"Unknown attribute editor widget type encountered..."_s );
2917 return newWidgetInfo;
2920void QgsAttributeForm::createWrappers()
2922 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2923 const QList<QgsField> fields = mLayer->fields().
toList();
2925 const auto constMyWidgets = myWidgets;
2926 for ( QWidget *myWidget : constMyWidgets )
2929 QVariant vRel = myWidget->property(
"qgisRelation" );
2930 if ( vRel.isValid() )
2933 QgsRelation relation = relMgr->
relation( vRel.toString() );
2936 QgsRelationWidgetWrapper *rww =
new QgsRelationWidgetWrapper( mLayer, relation, myWidget,
this );
2937 rww->
setConfig( mLayer->editFormConfig().widgetConfig( relation.
id() ) );
2940 mWidgets.append( rww );
2945 const auto constFields = fields;
2946 for (
const QgsField &field : constFields )
2948 if ( field.name() == myWidget->objectName() )
2950 int idx = mLayer->fields().lookupField( field.name() );
2953 mWidgets.append( eww );
2960void QgsAttributeForm::afterWidgetInit()
2962 bool isFirstEww =
true;
2964 const auto constMWidgets = mWidgets;
2965 for ( QgsWidgetWrapper *ww : constMWidgets )
2967 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
2973 setFocusProxy( eww->
widget() );
2982 QgsRelationWidgetWrapper *relationWidgetWrapper = qobject_cast<QgsRelationWidgetWrapper *>( ww );
2983 if ( relationWidgetWrapper )
2996 if ( e->type() == QEvent::KeyPress )
2998 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
2999 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
3010void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet<int> &mixedValueFields, QHash<int, QVariant> &fieldSharedValues )
const
3012 mixedValueFields.clear();
3013 fieldSharedValues.clear();
3019 for (
int i = 0; i < mLayer->
fields().count(); ++i )
3021 if ( mixedValueFields.contains( i ) )
3026 fieldSharedValues[i] = f.
attribute( i );
3030 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
3032 fieldSharedValues.remove( i );
3033 mixedValueFields.insert( i );
3039 if ( mixedValueFields.count() == mLayer->fields().count() )
3048void QgsAttributeForm::layerSelectionChanged()
3062 resetMultiEdit(
true );
3069 mIsSettingMultiEditFeatures =
true;
3070 mMultiEditFeatureIds = fids;
3072 if ( fids.isEmpty() )
3075 QMultiMap<int, QgsAttributeFormEditorWidget *>::const_iterator wIt = mFormEditorWidgets.constBegin();
3076 for ( ; wIt != mFormEditorWidgets.constEnd(); ++wIt )
3078 wIt.value()->initialize( QVariant() );
3080 mIsSettingMultiEditFeatures =
false;
3087 QSet<int> mixedValueFields;
3088 QHash<int, QVariant> fieldSharedValues;
3089 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
3092 fit = mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( *fids.constBegin() ) );
3098 if ( mCurrentFormFeature.id() != firstFeature.
id() )
3103 const auto constMixedValueFields = mixedValueFields;
3104 for (
int fieldIndex : std::as_const( mixedValueFields ) )
3106 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( fieldIndex );
3107 if ( formEditorWidgets.isEmpty() )
3110 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3111 QVariantList additionalFieldValues;
3112 for (
const QString &additionalField : additionalFields )
3113 additionalFieldValues << firstFeature.
attribute( additionalField );
3116 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
3118 QHash<int, QVariant>::const_iterator sharedValueIt = fieldSharedValues.constBegin();
3119 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
3121 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( sharedValueIt.key() );
3122 if ( formEditorWidgets.isEmpty() )
3126 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3127 for (
const QString &additionalField : additionalFields )
3129 int index = mLayer->fields().indexFromName( additionalField );
3130 if ( constMixedValueFields.contains( index ) )
3137 QVariantList additionalFieldValues;
3140 for (
const QString &additionalField : additionalFields )
3141 additionalFieldValues << firstFeature.
attribute( additionalField );
3143 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
3147 for (
const QString &additionalField : additionalFields )
3149 int index = mLayer->fields().indexFromName( additionalField );
3150 Q_ASSERT( fieldSharedValues.contains( index ) );
3151 additionalFieldValues << fieldSharedValues.value( index );
3154 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
3158 setMultiEditFeatureIdsRelations( fids );
3160 mIsSettingMultiEditFeatures =
false;
3165 if ( mOwnsMessageBar )
3167 mOwnsMessageBar =
false;
3168 mMessageBar = messageBar;
3178 QStringList filters;
3181 QString filter = widget->currentFilterExpression();
3182 if ( !filter.isNull() )
3183 filters <<
'(' + filter +
')';
3186 return filters.join(
" AND "_L1 );
3191 mExtraContextScope.reset( extraScope );
3196 const bool newVisibility = expression.
evaluate( expressionContext ).toBool();
3206 widget->setVisible( newVisibility );
3209 isVisible = newVisibility;
3212 const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
3214 if ( collapsedExpression.isValid() && !collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
3216 if ( QgsCollapsibleGroupBoxBasic * collapsibleGroupBox { qobject_cast<QgsCollapsibleGroupBoxBasic *>( widget ) } )
3218 collapsibleGroupBox->
setCollapsed( newCollapsedState );
3219 isCollapsed = newCollapsedState;
3224void QgsAttributeForm::updateJoinedFields(
const QgsEditorWidgetWrapper &eww )
3229 QgsFeature formFeature;
3233 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
3236 const QString hint = tr(
"No feature joined" );
3237 const auto constInfos = infos;
3238 for (
const QgsVectorLayerJoinInfo *info : constInfos )
3240 if ( !info->isDynamicFormEnabled() )
3243 QgsFeature joinFeature = mLayer->joinBuffer()->joinedFeatureOf( info, formFeature );
3245 mJoinedFeatures[info] = joinFeature;
3247 if ( info->hasSubset() )
3251 const auto constSubsetNames = subsetNames;
3252 for (
const QString &field : constSubsetNames )
3254 QString prefixedName = info->prefixedFieldName( field );
3256 QString hintText = hint;
3269 const QgsFields joinFields = joinFeature.
fields();
3270 for (
const QgsField &field : joinFields )
3272 QString prefixedName = info->prefixedFieldName( field );
3274 QString hintText = hint;
3288bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
3293void QgsAttributeForm::updateFieldDependencies()
3295 mDefaultValueDependencies.clear();
3296 mVirtualFieldsDependencies.clear();
3297 mRelatedLayerFieldsDependencies.clear();
3298 mParentDependencies.clear();
3301 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3303 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3307 updateFieldDependenciesParent( eww );
3308 updateFieldDependenciesDefaultValue( eww );
3309 updateFieldDependenciesVirtualFields( eww );
3310 updateRelatedLayerFieldsDependencies( eww );
3314void QgsAttributeForm::updateFieldDependenciesDefaultValue( QgsEditorWidgetWrapper *eww )
3318 if ( exp.needsGeometry() )
3319 mNeedsGeometry =
true;
3324 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3326 for (
const int id : allAttributeIds )
3328 mDefaultValueDependencies.insertMulti(
id, eww );
3334 const QSet<QString> referencedColumns = exp.referencedColumns();
3335 for (
const QString &referencedColumn : referencedColumns )
3337 mDefaultValueDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3342void QgsAttributeForm::updateFieldDependenciesVirtualFields( QgsEditorWidgetWrapper *eww )
3345 if ( expressionField.isEmpty() )
3348 QgsExpression exp( expressionField );
3350 if ( exp.needsGeometry() )
3351 mNeedsGeometry =
true;
3356 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3358 for (
const int id : allAttributeIds )
3360 mVirtualFieldsDependencies.insertMulti(
id, eww );
3366 const QSet<QString> referencedColumns = exp.referencedColumns();
3367 for (
const QString &referencedColumn : referencedColumns )
3369 mVirtualFieldsDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3374void QgsAttributeForm::updateRelatedLayerFieldsDependencies( QgsEditorWidgetWrapper *eww )
3379 if ( expressionField.contains( u
"relation_aggregate"_s )
3380 || expressionField.contains( u
"get_features"_s ) )
3381 mRelatedLayerFieldsDependencies.insert( eww );
3385 mRelatedLayerFieldsDependencies.clear();
3387 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3389 QgsEditorWidgetWrapper *editorWidgetWrapper = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3390 if ( !editorWidgetWrapper )
3393 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
3398void QgsAttributeForm::updateFieldDependenciesParent( QgsEditorWidgetWrapper *eww )
3403 const QSet<QString> referencedVariablesAndFunctions = expression.referencedVariables() + expression.referencedFunctions();
3404 for (
const QString &referenced : referencedVariablesAndFunctions )
3406 if ( referenced.startsWith(
"current_parent"_L1 ) )
3408 mParentDependencies.insert( eww );
3415void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
3417 for ( QgsAttributeFormWidget *formWidget : mFormWidgets )
3419 QgsAttributeFormRelationEditorWidget *relationEditorWidget =
dynamic_cast<QgsAttributeFormRelationEditorWidget *
>( formWidget );
3420 if ( !relationEditorWidget )
3427void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww )
3433 mIconMap[eww->
widget()]->hide();
3435 if ( !eww->
widget()->isEnabled() && mLayer->isEditable() )
3440 const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( eww->
fieldIdx(), mLayer->fields(), srcFieldIndex );
3447 const QString file = u
"/mIconJoinNotEditable.svg"_s;
3448 const QString tooltip = tr(
"Join settings do not allow editing" );
3449 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3453 const QString file = u
"mIconJoinHasNotUpsertOnEdit.svg"_s;
3454 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
3455 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3459 const QString file = u
"/mIconJoinedLayerNotEditable.svg"_s;
3460 const QString tooltip = tr(
"Joined layer is not toggled editable" );
3461 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3467void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
3470 sw->setToolTip( tooltip );
3477 QgsDebugMsgLevel( u
"reuseAllLastValues: %1"_s.arg( reuseAllLastValues ), 2 );
3481 for (
int idx = 0; idx < fields.
count(); ++idx )
3483 if ( attributes.contains( idx ) )
3485 initialAttributeValues.insert( idx, attributes.value( idx ) );
3489 const QVariant lastUsedValuesVariant =
layer->property(
"AttributeFormLastUsedValues" );
3491 if ( lastUsedValues.contains( idx ) &&
layer->dataProvider() &&
layer->dataProvider()->defaultValueClause( idx ) != lastUsedValues[idx] )
3493 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.
@ 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.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a relationship between two vector layers.
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)