78#include "moc_qgsattributeform.cpp"
80int QgsAttributeForm::sFormCounter = 0;
86 , mFormNr( sFormCounter++ )
87 , mEditCommandMessage( tr(
"Attributes changed" ) )
99 updateContainersVisibility();
101 updateEditableState();
107 qDeleteAll( mInterfaces );
134 mInterfaces.append( iface );
139 return mFeature.isValid() && mLayer->isEditable();
150 if ( mUnsavedMultiEditChanges )
153 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ), tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
154 if ( res == QMessageBox::Yes )
159 clearMultiEditMessages();
161 mUnsavedMultiEditChanges =
false;
213 w->setContext( newContext );
219 w->setVisible( relationWidgetsVisible );
226 mSearchButtonBox->setVisible(
false );
231 mSearchButtonBox->setVisible(
false );
236 mSearchButtonBox->setVisible(
false );
240 resetMultiEdit(
false );
242 mSearchButtonBox->setVisible(
false );
246 mSearchButtonBox->setVisible(
true );
252 mSearchButtonBox->setVisible(
false );
260 mSearchButtonBox->setVisible(
false );
269 const auto constMWidgets = mWidgets;
284 QVariant mainValue = eww->
value();
286 additionalFieldValues[index] = value;
287 eww->
setValues( mainValue, additionalFieldValues );
296 mFeature.setGeometry( geometry );
301 mIsSettingFeature =
true;
318 mIsSettingFeature =
false;
319 const auto constMInterfaces = mInterfaces;
322 iface->featureChanged();
338 mIsSettingFeature =
false;
341bool QgsAttributeForm::saveEdits( QString *error )
344 bool changedLayer =
false;
349 bool doUpdate =
false;
358 QgsAttributes src = mFeature.attributes();
359 QgsAttributes dst = mFeature.attributes();
361 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
363 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
367 QgsTextEditWrapper *textEdit = qobject_cast<QgsTextEditWrapper *>( eww );
371 *error = tr(
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
374 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
375 QVariantList srcVars = QVariantList() << eww->
value();
376 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
380 for (
const QString &fieldName : additionalFields )
384 dstVars << dst.at( idx );
388 Q_ASSERT( dstVars.count() == srcVars.count() );
390 for (
int i = 0; i < dstVars.count(); i++ )
392 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
394 dst[fieldIndexes[i]] = srcVars[i];
404 const auto constMInterfaces = mInterfaces;
405 for ( QgsAttributeFormInterface *iface : constMInterfaces )
407 if ( !iface->acceptChanges( updatedFeature ) )
417 mFeature = updatedFeature;
421 mFeature.setValid(
true );
422 mLayer->beginEditCommand( mEditCommandMessage );
423 bool res = mLayer->addFeature( updatedFeature );
426 mFeature.setAttributes( updatedFeature.
attributes() );
427 mLayer->endEditCommand();
429 const QgsFields fields = mLayer->fields();
430 const QgsAttributes newValues = updatedFeature.
attributes();
431 const QVariant lastUsedValuesVariant = mLayer->property(
"AttributeFormLastUsedValues" );
433 for (
int idx = 0; idx < fields.
count(); ++idx )
438 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
439 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
440 if ( !rememberLastUsedValues.contains( idx ) )
443 rememberLastUsedValues[idx] = remember;
444 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
447 const QVariant newValue = rememberLastUsedValues[idx] ? newValues.at( idx ) : QVariant();
448 if ( !lastUsedValues.contains( idx ) || lastUsedValues[idx] != newValue )
450 lastUsedValues[idx] = newValue;
451 QgsDebugMsgLevel( QStringLiteral(
"Saving %1 for %2" ).arg( ( newValue.toString() ).arg( idx ) ), 2 );
455 mLayer->setProperty(
"AttributeFormLastUsedValues", QVariant::fromValue<QgsAttributeMap>( lastUsedValues ) );
461 mLayer->destroyEditCommand();
465 mLayer->beginEditCommand( mEditCommandMessage );
471 for (
int i = 0; i < dst.count(); ++i )
474 || !dst.at( i ).isValid()
475 || !fieldIsEditable( i ) )
481 QgsDebugMsgLevel( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" ).arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( dst.at( i ) ) ).arg( dst.at( i ).isValid() ), 2 );
482 QgsDebugMsgLevel( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" ).arg( src.at( i ).toString(), src.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( src.at( i ) ) ).arg( src.at( i ).isValid() ), 2 );
484 newValues[i] = dst.at( i );
485 oldValues[i] = src.at( i );
490 auto context = std::make_unique<QgsVectorLayerToolsContext>();
491 QgsExpressionContext expressionContext = createExpressionContext( updatedFeature );
492 context->setExpressionContext( &expressionContext );
493 success = mLayer->changeAttributeValues( mFeature.id(), newValues, oldValues,
false, context.get() );
495 if ( success && n > 0 )
497 mLayer->endEditCommand();
498 mFeature.setAttributes( dst );
503 mLayer->destroyEditCommand();
517 mLayer->triggerRepaint();
522QgsFeature QgsAttributeForm::getUpdatedFeature()
const
525 QgsFeature updatedFeature = QgsFeature( mFeature );
527 QgsAttributes featureAttributes = mFeature.attributes();
528 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
530 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
534 QVariantList dstVars = QVariantList() << featureAttributes.at( eww->
fieldIdx() );
535 QVariantList srcVars = QVariantList() << eww->
value();
536 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
540 for (
const QString &fieldName : additionalFields )
544 dstVars << featureAttributes.at( idx );
548 Q_ASSERT( dstVars.count() == srcVars.count() );
550 for (
int i = 0; i < dstVars.count(); i++ )
552 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
553 featureAttributes[fieldIndexes[i]] = srcVars[i];
558 return updatedFeature;
561void QgsAttributeForm::updateValuesDependencies(
const int originIdx )
563 updateValuesDependenciesDefaultValues( originIdx );
564 updateValuesDependenciesVirtualFields( originIdx );
567void QgsAttributeForm::updateValuesDependenciesDefaultValues(
const int originIdx )
569 if ( !mDefaultValueDependencies.contains( originIdx ) )
572 if ( !mFeature.isValid()
577 QgsFeature updatedFeature = getUpdatedFeature();
580 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
581 for ( QgsWidgetWrapper *ww : std::as_const( relevantWidgets ) )
583 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
597 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
600 QgsExpressionContext context = createExpressionContext( updatedFeature );
602 const QVariant value = mLayer->defaultValue( eww->
fieldIdx(), updatedFeature, &context );
604 mCurrentFormFeature.setAttribute( eww->
field().
name(), value );
609void QgsAttributeForm::updateValuesDependenciesVirtualFields(
const int originIdx )
611 if ( !mVirtualFieldsDependencies.contains( originIdx ) )
614 if ( !mFeature.isValid() )
618 QgsFeature updatedFeature = getUpdatedFeature();
621 const QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
622 for ( QgsWidgetWrapper *ww : relevantWidgets )
624 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
629 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
633 QgsExpressionContext context = createExpressionContext( updatedFeature );
634 QgsExpression exp( mLayer->expressionField( eww->
fieldIdx() ) );
635 const QVariant value = exp.evaluate( &context );
641void QgsAttributeForm::updateValuesDependenciesParent()
644 QgsFeature updatedFeature = getUpdatedFeature();
645 QList<int> updatedFields;
648 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mParentDependencies;
649 for ( QgsEditorWidgetWrapper *eww : relevantWidgets )
652 if ( updatedFields.contains( eww->
fieldIdx() ) )
657 QgsExpressionContext context = createExpressionContext( updatedFeature );
658 const QVariant value = mLayer->defaultValue( eww->
fieldIdx(), updatedFeature, &context );
663void QgsAttributeForm::updateRelatedLayerFields()
666 updateRelatedLayerFieldsDependencies();
668 if ( mRelatedLayerFieldsDependencies.isEmpty() )
671 if ( !mFeature.isValid() )
675 QgsFeature updatedFeature = getUpdatedFeature();
678 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mRelatedLayerFieldsDependencies;
679 for ( QgsEditorWidgetWrapper *eww : relevantWidgets )
682 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
686 QgsExpressionContext context = createExpressionContext( updatedFeature );
687 QgsExpression exp( mLayer->expressionField( eww->
fieldIdx() ) );
688 QVariant value = exp.evaluate( &context );
693void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
698 mUnsavedMultiEditChanges =
false;
702void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
704 clearMultiEditMessages();
705 resetMultiEdit( link == QLatin1String(
"#apply" ) );
708void QgsAttributeForm::filterTriggered()
710 QString filter = createFilterExpression();
716void QgsAttributeForm::searchZoomTo()
718 QString filter = createFilterExpression();
719 if ( filter.isEmpty() )
725void QgsAttributeForm::searchFlash()
727 QString filter = createFilterExpression();
728 if ( filter.isEmpty() )
734void QgsAttributeForm::filterAndTriggered()
736 QString filter = createFilterExpression();
737 if ( filter.isEmpty() )
745void QgsAttributeForm::filterOrTriggered()
747 QString filter = createFilterExpression();
748 if ( filter.isEmpty() )
756void QgsAttributeForm::pushSelectedFeaturesMessage()
758 int count = mLayer->selectedFeatureCount();
761 mMessageBar->pushMessage( QString(), tr(
"%n matching feature(s) selected",
"matching features", count ),
Qgis::MessageLevel::Info );
776 QString filter = createFilterExpression();
777 if ( filter.isEmpty() )
781 pushSelectedFeaturesMessage();
786void QgsAttributeForm::searchSetSelection()
791void QgsAttributeForm::searchAddToSelection()
796void QgsAttributeForm::searchRemoveFromSelection()
801void QgsAttributeForm::searchIntersectSelection()
806bool QgsAttributeForm::saveMultiEdits()
810 const QList<int> fieldIndexes = mFormEditorWidgets.uniqueKeys();
811 for (
int fieldIndex : fieldIndexes )
813 const QList<QgsAttributeFormEditorWidget *> widgets = mFormEditorWidgets.values( fieldIndex );
814 if ( !widgets.first()->hasChanged() )
817 if ( !widgets.first()->currentValue().isValid()
818 || !fieldIsEditable( fieldIndex ) )
824 for ( QgsAttributeFormEditorWidget *widget : widgets )
825 widget->changesCommitted();
827 newAttributeValues.insert( fieldIndex, widgets.first()->currentValue() );
830 if ( newAttributeValues.isEmpty() )
838 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
839 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
840 if ( res != QMessageBox::Ok )
847 mLayer->beginEditCommand( tr(
"Updated multiple feature attributes" ) );
851 const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
854 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
855 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
857 success &= mLayer->changeAttributeValue( fid, aIt.key(), aIt.value() );
861 clearMultiEditMessages();
864 mLayer->endEditCommand();
865 mLayer->triggerRepaint();
866 mMultiEditMessageBarItem =
new QgsMessageBarItem( tr(
"Attribute changes for multiple features applied." ),
Qgis::MessageLevel::Success, -1 );
870 mLayer->destroyEditCommand();
874 if ( !mButtonBox->isVisible() )
875 mMessageBar->pushItem( mMultiEditMessageBarItem );
901 wrapper->notifyAboutToSave();
941 success = saveEdits( error );
945 success = saveMultiEdits();
950 mUnsavedMultiEditChanges =
false;
959 mValuesInitialized =
false;
960 const auto constMWidgets = mWidgets;
963 ww->setFeature( mFeature );
974 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
975 updateValuesDependenciesVirtualFields( eww->
fieldIdx() );
976 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
979 mValuesInitialized =
true;
985 const auto widgets { findChildren<QgsAttributeFormEditorWidget *>() };
992void QgsAttributeForm::clearMultiEditMessages()
994 if ( mMultiEditUnsavedMessageBarItem )
996 if ( !mButtonBox->isVisible() )
997 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
998 mMultiEditUnsavedMessageBarItem =
nullptr;
1000 if ( mMultiEditMessageBarItem )
1002 if ( !mButtonBox->isVisible() )
1003 mMessageBar->popWidget( mMultiEditMessageBarItem );
1004 mMultiEditMessageBarItem =
nullptr;
1008QString QgsAttributeForm::createFilterExpression()
const
1010 QStringList filters;
1011 for ( QgsAttributeFormWidget *w : std::as_const( mFormWidgets ) )
1013 QString filter = w->currentFilterExpression();
1014 if ( !filter.isEmpty() )
1018 if ( filters.isEmpty() )
1021 QString filter = filters.join( QLatin1String(
") AND (" ) ).prepend(
'(' ).append(
')' );
1027 QgsExpressionContext context;
1030 if ( mExtraContextScope )
1032 context.
appendScope(
new QgsExpressionContextScope( *mExtraContextScope.get() ) );
1034 if ( mContext.parentFormFeature().isValid() )
1043void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
1045 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( sender() );
1048 bool signalEmitted =
false;
1050 if ( mValuesInitialized )
1053 mCurrentFormFeature.setAttribute( eww->
field().
name(), value );
1057 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1058 for ( QgsAttributeFormEditorWidget *formEditorWidget : std::as_const( formEditorWidgets ) )
1060 if ( formEditorWidget->editorWidget() == eww )
1064 formEditorWidget->editorWidget()->setValue( value );
1081 for (
int i = 0; i < additionalFields.count(); i++ )
1083 const QString fieldName = additionalFields.at( i );
1084 const QVariant value = additionalFieldValues.at( i );
1088 signalEmitted =
true;
1090 if ( mValuesInitialized )
1091 updateJoinedFields( *eww );
1097 if ( !mIsSettingMultiEditFeatures )
1099 mUnsavedMultiEditChanges =
true;
1101 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
1102 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
1103 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1104 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
1105 clearMultiEditMessages();
1108 if ( !mButtonBox->isVisible() )
1109 mMessageBar->pushItem( mMultiEditUnsavedMessageBarItem );
1112 signalEmitted =
true;
1122 updateConstraints( eww );
1125 if ( mValuesInitialized && !mIsSettingMultiEditFeatures )
1128 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1129 updateValuesDependencies( eww->
fieldIdx() );
1130 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1135 updateEditableState();
1137 if ( !signalEmitted )
1142 bool attributeHasChanged = !mIsSettingFeature;
1144 attributeHasChanged &= !mIsSettingMultiEditFeatures;
1150void QgsAttributeForm::updateAllConstraints()
1152 const auto constMWidgets = mWidgets;
1153 for ( QgsWidgetWrapper *ww : constMWidgets )
1155 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1157 updateConstraints( eww );
1165 if ( currentFormValuesFeature( ft ) )
1177 updateConstraint( ft, eww );
1180 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
1182 for ( QgsEditorWidgetWrapper *depsEww : deps )
1183 updateConstraint( ft, depsEww );
1188 QgsExpressionContext context = createExpressionContext( ft );
1191 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
1192 for ( ContainerInformation *info : infos )
1194 info->apply( &context );
1199void QgsAttributeForm::updateContainersVisibility()
1201 QgsExpressionContext context = createExpressionContext( mFeature );
1203 const QVector<ContainerInformation *> infos = mContainerVisibilityCollapsedInformation;
1205 for ( ContainerInformation *info : infos )
1207 info->apply( &context );
1217 updateAllConstraints();
1232 if ( mJoinedFeatures.contains( info ) )
1248void QgsAttributeForm::updateLabels()
1250 if ( !mLabelDataDefinedProperties.isEmpty() )
1252 QgsFeature currentFeature;
1253 if ( currentFormValuesFeature( currentFeature ) )
1255 QgsExpressionContext context = createExpressionContext( currentFeature );
1257 for (
auto it = mLabelDataDefinedProperties.constBegin(); it != mLabelDataDefinedProperties.constEnd(); ++it )
1259 QLabel *label { it.key() };
1261 const QString value { it->valueAsString( context, QString(), &ok ) };
1262 if ( ok && !value.isEmpty() )
1264 label->setText( value );
1271void QgsAttributeForm::updateEditableState()
1273 if ( !mEditableDataDefinedProperties.isEmpty() )
1275 QgsFeature currentFeature;
1276 if ( currentFormValuesFeature( currentFeature ) )
1278 QgsExpressionContext context = createExpressionContext( currentFeature );
1280 for (
auto it = mEditableDataDefinedProperties.constBegin(); it != mEditableDataDefinedProperties.constEnd(); ++it )
1282 QWidget *w { it.key() };
1284 const bool isEditable { it->valueAsBool( context,
true, &ok ) && mLayer && mLayer->isEditable() };
1287 QgsAttributeFormEditorWidget *editorWidget { qobject_cast<QgsAttributeFormEditorWidget *>( w ) };
1294 w->setEnabled( isEditable );
1302bool QgsAttributeForm::currentFormValuesFeature(
QgsFeature &feature )
1305 feature = QgsFeature( mFeature );
1306 QgsAttributes dst =
feature.attributes();
1308 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1310 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1315 if ( dst.count() > eww->
fieldIdx() )
1317 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1318 QVariantList srcVars = QVariantList() << eww->
value();
1319 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1323 for (
const QString &fieldName : additionalFields )
1326 fieldIndexes << idx;
1327 dstVars << dst.at( idx );
1331 Q_ASSERT( dstVars.count() == srcVars.count() );
1333 for (
int i = 0; i < dstVars.count(); i++ )
1339 dst[fieldIndexes[i]] = srcVars[i];
1356void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1358 mContainerVisibilityCollapsedInformation.append( info );
1362 for (
const QString &col : referencedColumns )
1364 mContainerInformationDependency[col].append( info );
1368bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1370 bool valid {
true };
1372 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1374 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1392bool QgsAttributeForm::currentFormValidHardConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1394 bool valid {
true };
1396 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1398 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1413void QgsAttributeForm::onAttributeAdded(
int idx )
1415 mPreventFeatureRefresh =
false;
1416 if ( mFeature.isValid() )
1418 QgsAttributes attrs = mFeature.attributes();
1420 mFeature.setFields(
layer()->fields() );
1421 mFeature.setAttributes( attrs );
1427void QgsAttributeForm::onAttributeDeleted(
int idx )
1429 mPreventFeatureRefresh =
false;
1430 if ( mFeature.isValid() )
1432 QgsAttributes attrs = mFeature.attributes();
1433 attrs.remove( idx );
1434 mFeature.setFields(
layer()->fields() );
1435 mFeature.setAttributes( attrs );
1441void QgsAttributeForm::onRelatedFeaturesChanged()
1443 updateRelatedLayerFields();
1446void QgsAttributeForm::onUpdatedFields()
1448 mPreventFeatureRefresh =
false;
1449 if ( mFeature.isValid() )
1451 QgsAttributes attrs(
layer()->fields().size() );
1454 int idx = mFeature.fields().indexFromName(
layer()->fields().at( i ).name() );
1457 attrs[i] = mFeature.attributes().at( idx );
1458 if ( mFeature.attributes().at( idx ).userType() !=
layer()->fields().at( i ).type() )
1460 attrs[i].convert(
layer()->fields().at( i ).type() );
1468 mFeature.setFields(
layer()->fields() );
1469 mFeature.setAttributes( attrs );
1477 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( sender() );
1480 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1482 for ( QgsAttributeFormEditorWidget *formEditorWidget : formEditorWidgets )
1484 formEditorWidget->setConstraintStatus( constraint, description, err, result );
1485 if ( formEditorWidget->editorWidget() != eww )
1487 formEditorWidget->editorWidget()->updateConstraint( result, err );
1494 QList<QgsEditorWidgetWrapper *> wDeps;
1498 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
1501 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1506 if ( name != ewwName )
1513 for (
const QString &colName : referencedColumns )
1515 if ( name.compare( colName, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
1517 wDeps.append( eww );
1530 return setupRelationWidgetWrapper( QString(), rel, context );
1535 QgsRelationWidgetWrapper *rww =
new QgsRelationWidgetWrapper( relationWidgetTypeId, mLayer, rel,
nullptr,
this );
1536 const QVariantMap config = mLayer->editFormConfig().widgetConfig( rel.
id() );
1543void QgsAttributeForm::preventFeatureRefresh()
1545 mPreventFeatureRefresh =
true;
1550 if ( mPreventFeatureRefresh || mLayer->isEditable() || !mFeature.isValid() )
1555 if ( !mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( mFeature ) )
1564 if ( mContext.parentFormFeature().isValid() )
1566 QgsFeature parentFormFeature = mContext.parentFormFeature();
1568 mContext.setParentFormFeature( parentFormFeature );
1571 updateValuesDependenciesParent();
1585 return mNeedsGeometry;
1588void QgsAttributeForm::synchronizeState()
1590 bool isEditable = ( mFeature.
isValid()
1600 const QList<QgsAttributeFormEditorWidget *> formWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1603 formWidget->setConstraintResultVisible( isEditable );
1607 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1608 ww->setEnabled( enabled );
1614 ww->setEnabled( isEditable );
1624 if ( mConstraintsFailMessageBarItem )
1626 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1628 mConstraintsFailMessageBarItem =
new QgsMessageBarItem( tr(
"Multi edit mode requires at least one selected feature." ),
Qgis::MessageLevel::Info, -1 );
1629 mMessageBar->pushItem( mConstraintsFailMessageBarItem );
1633 QStringList invalidFields, descriptions;
1634 mValidConstraints = currentFormValidHardConstraints( invalidFields, descriptions );
1638 if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
1640 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 );
1641 mMessageBar->pushItem( mConstraintsFailMessageBarItem );
1643 else if ( mValidConstraints && mConstraintsFailMessageBarItem )
1645 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1646 mConstraintsFailMessageBarItem =
nullptr;
1649 else if ( mConstraintsFailMessageBarItem )
1651 mMessageBar->popWidget( mConstraintsFailMessageBarItem );
1652 mConstraintsFailMessageBarItem =
nullptr;
1655 isEditable = isEditable & mValidConstraints;
1660 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1662 okButton->setEnabled( isEditable );
1665void QgsAttributeForm::init()
1667 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1670 QWidget *formWidget =
nullptr;
1671 mNeedsGeometry =
false;
1673 bool buttonBoxVisible =
true;
1677 buttonBoxVisible = mButtonBox->isVisible();
1679 mButtonBox =
nullptr;
1682 if ( mSearchButtonBox )
1684 delete mSearchButtonBox;
1685 mSearchButtonBox =
nullptr;
1688 qDeleteAll( mWidgets );
1691 while ( QWidget *w = this->findChild<QWidget *>() )
1697 QVBoxLayout *vl =
new QVBoxLayout();
1698 vl->setContentsMargins( 0, 0, 0, 0 );
1699 mMessageBar =
new QgsMessageBar(
this );
1700 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1701 vl->addWidget( mMessageBar );
1706 QGridLayout *layout =
new QGridLayout();
1707 QWidget *container =
new QWidget();
1708 container->setLayout( layout );
1709 vl->addWidget( container );
1711 mFormEditorWidgets.clear();
1712 mFormWidgets.clear();
1715 setContentsMargins( 0, 0, 0, 0 );
1720 QgsDebugMsgLevel( QStringLiteral(
"loading form: %1" ).arg( mLayer->editFormConfig().uiForm() ), 2 );
1721 const QString path = mLayer->editFormConfig().uiForm();
1723 if ( file && file->open( QFile::ReadOnly ) )
1727 QFileInfo fi( file->fileName() );
1728 loader.setWorkingDirectory( fi.dir() );
1729 formWidget = loader.load( file,
this );
1732 formWidget->setWindowFlags( Qt::Widget );
1733 layout->addWidget( formWidget );
1736 mButtonBox = findChild<QDialogButtonBox *>();
1739 formWidget->installEventFilter(
this );
1744 QgsTabWidget *tabWidget =
nullptr;
1751 int columnCount = 1;
1752 bool hasRootFields =
false;
1753 bool addSpacer =
true;
1755 const QList<QgsAttributeEditorElement *> tabs = mLayer->editFormConfig().tabs();
1757 for ( QgsAttributeEditorElement *widgDef : tabs )
1761 QgsAttributeEditorContainer *containerDef =
dynamic_cast<QgsAttributeEditorContainer *
>( widgDef );
1762 if ( !containerDef )
1765 switch ( containerDef->
type() )
1769 tabWidget =
nullptr;
1770 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1771 if ( widgetInfo.labelStyle.overrideColor )
1773 if ( widgetInfo.labelStyle.color.isValid() )
1775 widgetInfo.widget->setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1778 if ( widgetInfo.labelStyle.overrideFont )
1780 widgetInfo.widget->setFont( widgetInfo.labelStyle.font );
1783 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1784 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1786 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1788 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1790 layout->setRowStretch( row, widgDef->verticalStretch() );
1804 tabWidget =
nullptr;
1805 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1806 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1807 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1809 layout->setRowStretch( row, widgDef->verticalStretch() );
1812 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1814 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1829 tabWidget =
new QgsTabWidget();
1830 layout->addWidget( tabWidget, row, column, 1, 2 );
1834 QWidget *tabPage =
new QWidget( tabWidget );
1836 tabWidget->addTab( tabPage, widgDef->name() );
1837 tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
1841 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1843 QGridLayout *tabPageLayout =
new QGridLayout();
1844 tabPage->setLayout( tabPageLayout );
1846 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1847 tabPageLayout->addWidget( widgetInfo.widget );
1854 hasRootFields =
true;
1855 tabWidget =
nullptr;
1856 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1857 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox();
1859 if ( widgetInfo.showLabel )
1861 if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
1863 collapsibleGroupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1866 if ( widgetInfo.labelStyle.overrideFont )
1868 collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
1871 collapsibleGroupBox->setTitle( widgetInfo.labelText );
1874 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1875 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
1876 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1878 QVBoxLayout *
c =
new QVBoxLayout();
1879 c->addWidget( collapsibleGroupBox );
1880 layout->addLayout(
c, row, column, 1, 2 );
1882 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1883 layout->setRowStretch( row, widgDef->verticalStretch() );
1884 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1885 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1894 hasRootFields =
true;
1895 tabWidget =
nullptr;
1896 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1897 QLabel *label =
new QLabel( widgetInfo.labelText );
1899 if ( widgetInfo.labelStyle.overrideColor )
1901 if ( widgetInfo.labelStyle.color.isValid() )
1903 label->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1907 if ( widgetInfo.labelStyle.overrideFont )
1909 label->setFont( widgetInfo.labelStyle.font );
1912 label->setToolTip( widgetInfo.toolTip );
1913 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1915 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1918 label->setBuddy( widgetInfo.widget );
1921 if ( widgetInfo.widget
1922 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
1923 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
1924 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
1927 if ( !widgetInfo.showLabel )
1929 QVBoxLayout *
c =
new QVBoxLayout();
1930 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1931 c->addWidget( widgetInfo.widget );
1932 layout->addLayout(
c, row, column, 1, 2 );
1934 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1936 layout->setRowStretch( row, widgDef->verticalStretch() );
1939 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1941 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1946 else if ( widgetInfo.labelOnTop )
1948 QVBoxLayout *
c =
new QVBoxLayout();
1949 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1950 c->addWidget( label );
1951 c->addWidget( widgetInfo.widget );
1952 layout->addLayout(
c, row, column, 1, 2 );
1954 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1956 layout->setRowStretch( row, widgDef->verticalStretch() );
1959 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1961 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1968 const int widgetColumn = column + 1;
1969 layout->addWidget( label, row, column++ );
1970 layout->addWidget( widgetInfo.widget, row, column++ );
1972 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1974 layout->setRowStretch( row, widgDef->verticalStretch() );
1977 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( widgetColumn ) )
1979 layout->setColumnStretch( widgetColumn, widgDef->horizontalStretch() );
1986 const QgsAttributeEditorField *fieldElement {
static_cast<QgsAttributeEditorField *
>( widgDef ) };
1987 const int fieldIdx = fieldElement->
idx();
1988 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
1990 const QString fieldName { mLayer->fields().at( fieldIdx ).name() };
1994 if ( property.isActive() )
1996 mLabelDataDefinedProperties[label] = property;
2002 if ( property.isActive() )
2004 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2011 if ( column >= columnCount * 2 )
2018 if ( hasRootFields && addSpacer )
2020 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2021 layout->addItem( spacerItem, row, 0 );
2022 layout->setRowStretch( row, 1 );
2025 formWidget = container;
2034 formWidget =
new QWidget(
this );
2035 QGridLayout *gridLayout =
new QGridLayout( formWidget );
2036 formWidget->setLayout( gridLayout );
2041 QgsScrollArea *scrollArea =
new QgsScrollArea(
this );
2042 scrollArea->setWidget( formWidget );
2043 scrollArea->setWidgetResizable(
true );
2044 scrollArea->setFrameShape( QFrame::NoFrame );
2045 scrollArea->setFrameShadow( QFrame::Plain );
2046 scrollArea->setFocusProxy(
this );
2047 layout->addWidget( scrollArea );
2051 layout->addWidget( formWidget );
2056 const QgsFields fields = mLayer->fields();
2058 for (
const QgsField &field : fields )
2060 int idx = fields.lookupField( field.name() );
2065 QString fieldName = mLayer->attributeDisplayName( idx );
2066 QString labelText = fieldName;
2067 labelText.replace(
'&', QLatin1String(
"&&" ) );
2071 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
2074 bool labelOnTop = mLayer->editFormConfig().labelOnTop( idx );
2077 QLabel *label =
new QLabel( labelText );
2079 QSvgWidget *i =
new QSvgWidget();
2080 i->setFixedSize( 18, 18 );
2085 if ( property.isActive() )
2087 mLabelDataDefinedProperties[label] = property;
2093 QWidget *w =
nullptr;
2096 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2098 mFormEditorWidgets.insert( idx, formWidget );
2099 mFormWidgets.append( formWidget );
2105 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2106 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2107 if ( rememberLastUsedValues.contains( idx ) )
2109 remember = rememberLastUsedValues[idx];
2114 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2115 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2116 rememberLastUsedValues[idx] = remember;
2117 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2123 label->setBuddy( eww->
widget() );
2128 if ( property.isActive() )
2130 mEditableDataDefinedProperties[formWidget] = property;
2136 w =
new QLabel( QStringLiteral(
"<p style=\"color: red; font-style: italic;\">%1</p>" ).arg( tr(
"Failed to create widget with type '%1'" ).arg( widgetSetup.
type() ) ) );
2141 w->setObjectName( field.name() );
2145 mWidgets.append( eww );
2146 mIconMap[eww->
widget()] = i;
2151 gridLayout->addWidget( label, row++, 0, 1, 2 );
2152 gridLayout->addWidget( w, row++, 0, 1, 2 );
2153 gridLayout->addWidget( i, row++, 0, 1, 2 );
2157 gridLayout->addWidget( label, row, 0 );
2158 gridLayout->addWidget( w, row, 1 );
2159 gridLayout->addWidget( i, row++, 2 );
2164 for (
const QgsRelation &rel : relations )
2166 QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( QStringLiteral(
"relation_editor" ), rel, mContext );
2168 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2171 QgsCollapsibleGroupBox *collapsibleGroupBox =
new QgsCollapsibleGroupBox( rel.
name() );
2172 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
2173 collapsibleGroupBoxLayout->addWidget( formWidget );
2174 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
2176 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
2178 mWidgets.append( rww );
2179 mFormWidgets.append( formWidget );
2184 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2185 gridLayout->addItem( spacerItem, row, 0 );
2186 gridLayout->setRowStretch( row, 1 );
2192 updateFieldDependencies();
2196 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
2197 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
2198 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2200 mButtonBox->setVisible( buttonBoxVisible );
2202 if ( !mSearchButtonBox )
2204 mSearchButtonBox =
new QWidget();
2205 QHBoxLayout *boxLayout =
new QHBoxLayout();
2206 boxLayout->setContentsMargins( 0, 0, 0, 0 );
2207 mSearchButtonBox->setLayout( boxLayout );
2208 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
2210 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
2212 boxLayout->addWidget( clearButton );
2213 boxLayout->addStretch( 1 );
2215 QPushButton *flashButton =
new QPushButton();
2216 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2217 flashButton->setText( tr(
"&Flash Features" ) );
2218 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
2219 boxLayout->addWidget( flashButton );
2221 QPushButton *openAttributeTableButton =
new QPushButton();
2222 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2223 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
2224 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
2225 connect( openAttributeTableButton, &QToolButton::clicked,
this, [
this] {
2228 boxLayout->addWidget( openAttributeTableButton );
2230 QPushButton *zoomButton =
new QPushButton();
2231 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2232 zoomButton->setText( tr(
"&Zoom to Features" ) );
2233 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
2234 boxLayout->addWidget( zoomButton );
2236 QToolButton *selectButton =
new QToolButton();
2237 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2238 selectButton->setText( tr(
"&Select Features" ) );
2240 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
2241 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
2242 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
2243 QMenu *selectMenu =
new QMenu( selectButton );
2244 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
2246 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
2247 selectMenu->addAction( selectAction );
2248 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
2250 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
2251 selectMenu->addAction( addSelectAction );
2252 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
2254 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
2255 selectMenu->addAction( deselectAction );
2256 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
2258 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
2259 selectMenu->addAction( filterSelectAction );
2260 selectButton->setMenu( selectMenu );
2261 boxLayout->addWidget( selectButton );
2265 QToolButton *filterButton =
new QToolButton();
2266 filterButton->setText( tr(
"Filter Features" ) );
2267 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
2268 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2269 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
2270 QMenu *filterMenu =
new QMenu( filterButton );
2271 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
2272 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
2273 filterMenu->addAction( filterAndAction );
2274 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
2275 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
2276 filterMenu->addAction( filterOrAction );
2277 filterButton->setMenu( filterMenu );
2278 boxLayout->addWidget( filterButton );
2282 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
2284 closeButton->setShortcut( Qt::Key_Escape );
2285 boxLayout->addWidget( closeButton );
2288 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2306 const auto constMInterfaces = mInterfaces;
2307 for ( QgsAttributeFormInterface *iface : constMInterfaces )
2317 QApplication::restoreOverrideCursor();
2320void QgsAttributeForm::cleanPython()
2322 if ( !mPyFormVarName.isNull() )
2324 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
2329void QgsAttributeForm::initPython()
2335 if ( !mLayer->editFormConfig().initFunction().isEmpty()
2341 mMessageBar->pushMessage(
2342 tr(
"Security warning" ),
2343 tr(
"The attribute form contains an embedded script which has been denied execution." ),
2349 QString initFunction = mLayer->editFormConfig().initFunction();
2350 QString initFilePath = mLayer->editFormConfig().initFilePath();
2353 switch ( mLayer->editFormConfig().initCodeSource() )
2356 if ( !initFilePath.isEmpty() )
2360 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2363 QTextStream inf( inputFile );
2364#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
2365 inf.setCodec(
"UTF-8" );
2367 initCode = inf.readAll();
2372 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
2382 initCode = mLayer->editFormConfig().initCode();
2383 if ( initCode.isEmpty() )
2385 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
2396 if ( !initCode.isEmpty() )
2404 mMessageBar->pushMessage( QString(), tr(
"Python macro could not be run due to missing permissions." ),
Qgis::MessageLevel::Warning );
2412 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
2414 static int sFormId = 0;
2415 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
2417 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
2418 .arg( mPyFormVarName )
2419 .arg( ( quint64 )
this );
2423 QgsDebugMsgLevel( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ), 2 );
2426 if ( numArgs == QLatin1String(
"3" ) )
2428 addInterface(
new QgsAttributeFormLegacyInterface( initFunction, mPyFormVarName,
this ) );
2434 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 ) );
2437 QString expr = QString(
"%1(%2)" )
2438 .arg( mLayer->editFormInit() )
2439 .arg( mPyFormVarName );
2440 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2450 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 ) );
2458 WidgetInfo newWidgetInfo;
2460 newWidgetInfo.labelStyle = widgetDef->
labelStyle();
2462 switch ( widgetDef->
type() )
2466 const QgsAttributeEditorAction *elementDef =
dynamic_cast<const QgsAttributeEditorAction *
>( widgetDef );
2470 QgsActionWidgetWrapper *actionWrapper =
new QgsActionWidgetWrapper( mLayer,
nullptr,
this, mMessageBar );
2474 mWidgets.append( actionWrapper );
2475 newWidgetInfo.widget = actionWrapper->
widget();
2476 newWidgetInfo.showLabel =
false;
2483 const QgsAttributeEditorField *fieldDef =
dynamic_cast<const QgsAttributeEditorField *
>( widgetDef );
2487 const QgsFields fields = vl->
fields();
2489 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2494 QgsAttributeFormEditorWidget *formWidget =
new QgsAttributeFormEditorWidget( eww, widgetSetup.
type(),
this );
2495 mFormEditorWidgets.insert( fldIdx, formWidget );
2496 mFormWidgets.append( formWidget );
2502 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2503 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2504 if ( rememberLastUsedValues.contains( fldIdx ) )
2506 remember = rememberLastUsedValues[fldIdx];
2510 const QVariant rememberLastUsedValuesVariant = mLayer->property(
"AttributeFormRememberLastUsedValues" );
2511 QMap<int, bool> rememberLastUsedValues = rememberLastUsedValuesVariant.value<QMap<int, bool>>();
2512 rememberLastUsedValues[idx] = remember;
2513 mLayer->setProperty(
"AttributeFormRememberLastUsedValues", QVariant::fromValue<QMap<int, bool>>( rememberLastUsedValues ) );
2519 newWidgetInfo.widget = formWidget;
2520 mWidgets.append( eww );
2522 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2523 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
2526 newWidgetInfo.labelOnTop = mLayer->editFormConfig().labelOnTop( fldIdx );
2527 newWidgetInfo.labelText = mLayer->attributeDisplayName( fldIdx );
2528 newWidgetInfo.labelText.replace(
'&', QLatin1String(
"&&" ) );
2529 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->attributeDisplayName( fldIdx ), newWidgetInfo.hint );
2530 newWidgetInfo.showLabel = widgetDef->
showLabel();
2537 const QgsAttributeEditorRelation *relDef =
static_cast<const QgsAttributeEditorRelation *
>( widgetDef );
2541 QgsAttributeFormRelationEditorWidget *formWidget =
new QgsAttributeFormRelationEditorWidget( rww,
this );
2551 mWidgets.append( rww );
2552 mFormWidgets.append( formWidget );
2554 newWidgetInfo.widget = formWidget;
2555 newWidgetInfo.showLabel = relDef->
showLabel();
2556 newWidgetInfo.labelText = relDef->
label();
2557 if ( newWidgetInfo.labelText.isEmpty() )
2559 newWidgetInfo.labelOnTop =
true;
2565 const QgsAttributeEditorContainer *container =
dynamic_cast<const QgsAttributeEditorContainer *
>( widgetDef );
2571 if ( columnCount <= 0 )
2575 QWidget *myContainer =
nullptr;
2576 bool removeLayoutMargin =
false;
2577 switch ( container->
type() )
2581 QgsCollapsibleGroupBoxBasic *groupBox =
new QgsCollapsibleGroupBoxBasic();
2582 widgetName = QStringLiteral(
"QGroupBox" );
2585 groupBox->setTitle( container->
name() );
2586 if ( newWidgetInfo.labelStyle.overrideColor )
2588 if ( newWidgetInfo.labelStyle.color.isValid() )
2590 groupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2593 if ( newWidgetInfo.labelStyle.overrideFont )
2595 groupBox->setFont( newWidgetInfo.labelStyle.font );
2598 myContainer = groupBox;
2599 newWidgetInfo.widget = myContainer;
2606 QWidget *rowWidget =
new QWidget();
2607 widgetName = QStringLiteral(
"Row" );
2608 myContainer = rowWidget;
2609 newWidgetInfo.widget = myContainer;
2610 removeLayoutMargin =
true;
2611 columnCount = container->
children().size();
2617 myContainer =
new QWidget();
2619 QgsScrollArea *scrollArea =
new QgsScrollArea( parent );
2621 scrollArea->setWidget( myContainer );
2622 scrollArea->setWidgetResizable(
true );
2623 scrollArea->setFrameShape( QFrame::NoFrame );
2624 widgetName = QStringLiteral(
"QScrollArea QWidget" );
2626 newWidgetInfo.widget = scrollArea;
2633 QString style { QStringLiteral(
"background-color: %1;" ).arg( container->
backgroundColor().name() ) };
2634 newWidgetInfo.widget->setStyleSheet( style );
2637 QGridLayout *gbLayout =
new QGridLayout();
2638 if ( removeLayoutMargin )
2639 gbLayout->setContentsMargins( 0, 0, 0, 0 );
2640 myContainer->setLayout( gbLayout );
2644 bool addSpacer =
true;
2646 const QList<QgsAttributeEditorElement *> children = container->
children();
2648 for ( QgsAttributeEditorElement *childDef : children )
2650 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2654 QgsAttributeEditorContainer *containerDef =
static_cast<QgsAttributeEditorContainer *
>( childDef );
2662 int widgetColumn = column;
2664 if ( widgetInfo.labelText.isNull() || !widgetInfo.showLabel )
2666 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2667 widgetColumn = column + 1;
2672 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2674 if ( widgetInfo.labelStyle.overrideColor )
2676 if ( widgetInfo.labelStyle.color.isValid() )
2678 mypLabel->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2682 if ( widgetInfo.labelStyle.overrideFont )
2684 mypLabel->setFont( widgetInfo.labelStyle.font );
2690 const QgsAttributeEditorField *fieldDef {
static_cast<QgsAttributeEditorField *
>( childDef ) };
2691 const QgsFields fields = vl->
fields();
2692 const int fldIdx = fieldDef->
idx();
2693 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2695 const QString fieldName { fields.
at( fldIdx ).
name() };
2699 if ( property.isActive() )
2701 mLabelDataDefinedProperties[mypLabel] = property;
2707 if ( property.isActive() )
2709 mEditableDataDefinedProperties[widgetInfo.widget] = property;
2715 mypLabel->setToolTip( widgetInfo.toolTip );
2716 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2718 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2721 mypLabel->setBuddy( widgetInfo.widget );
2723 if ( widgetInfo.labelOnTop )
2725 widgetColumn = column + 1;
2726 QVBoxLayout *
c =
new QVBoxLayout();
2727 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2728 c->layout()->addWidget( mypLabel );
2729 c->layout()->addWidget( widgetInfo.widget );
2730 gbLayout->addLayout(
c, row, column, 1, 2 );
2735 widgetColumn = column + 1;
2736 gbLayout->addWidget( mypLabel, row, column++ );
2737 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2741 const int childHorizontalStretch = childDef->horizontalStretch();
2742 const int existingColumnStretch = gbLayout->columnStretch( widgetColumn );
2743 if ( childHorizontalStretch > 0 && childHorizontalStretch > existingColumnStretch )
2745 gbLayout->setColumnStretch( widgetColumn, childHorizontalStretch );
2748 if ( childDef->verticalStretch() > 0 && childDef->verticalStretch() > gbLayout->rowStretch( row ) )
2750 gbLayout->setRowStretch( row, childDef->verticalStretch() );
2753 if ( column >= columnCount * 2 )
2759 if ( widgetInfo.widget
2760 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2761 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2762 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2766 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
2772 QWidget *spacer =
new QWidget();
2773 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2774 gbLayout->addWidget( spacer, ++row, 0 );
2775 gbLayout->setRowStretch( row, 1 );
2778 newWidgetInfo.labelText = QString();
2779 newWidgetInfo.labelOnTop =
true;
2780 newWidgetInfo.showLabel = widgetDef->
showLabel();
2786 const QgsAttributeEditorQmlElement *elementDef =
static_cast<const QgsAttributeEditorQmlElement *
>( widgetDef );
2788 QgsQmlWidgetWrapper *qmlWrapper =
new QgsQmlWidgetWrapper( mLayer,
nullptr,
this );
2793 mWidgets.append( qmlWrapper );
2795 newWidgetInfo.widget = qmlWrapper->
widget();
2796 newWidgetInfo.labelText = elementDef->
name();
2797 newWidgetInfo.labelOnTop =
true;
2798 newWidgetInfo.showLabel = widgetDef->
showLabel();
2804 const QgsAttributeEditorHtmlElement *elementDef =
static_cast<const QgsAttributeEditorHtmlElement *
>( widgetDef );
2806 QgsHtmlWidgetWrapper *htmlWrapper =
new QgsHtmlWidgetWrapper( mLayer,
nullptr,
this );
2810 mWidgets.append( htmlWrapper );
2812 newWidgetInfo.widget = htmlWrapper->
widget();
2813 newWidgetInfo.labelText = elementDef->
name();
2814 newWidgetInfo.labelOnTop =
true;
2815 newWidgetInfo.showLabel = widgetDef->
showLabel();
2822 const QgsAttributeEditorTextElement *elementDef =
static_cast<const QgsAttributeEditorTextElement *
>( widgetDef );
2824 QgsTextWidgetWrapper *textWrapper =
new QgsTextWidgetWrapper( mLayer,
nullptr,
this );
2828 mWidgets.append( textWrapper );
2830 newWidgetInfo.widget = textWrapper->
widget();
2831 newWidgetInfo.labelText = elementDef->
name();
2832 newWidgetInfo.labelOnTop =
false;
2833 newWidgetInfo.showLabel = widgetDef->
showLabel();
2840 const QgsAttributeEditorSpacerElement *elementDef =
static_cast<const QgsAttributeEditorSpacerElement *
>( widgetDef );
2841 QgsSpacerWidgetWrapper *spacerWrapper =
new QgsSpacerWidgetWrapper( mLayer,
nullptr,
this );
2844 mWidgets.append( spacerWrapper );
2846 newWidgetInfo.widget = spacerWrapper->
widget();
2847 newWidgetInfo.labelOnTop =
false;
2848 newWidgetInfo.showLabel =
false;
2853 QgsDebugError( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
2857 return newWidgetInfo;
2860void QgsAttributeForm::createWrappers()
2862 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2863 const QList<QgsField> fields = mLayer->fields().
toList();
2865 const auto constMyWidgets = myWidgets;
2866 for ( QWidget *myWidget : constMyWidgets )
2869 QVariant vRel = myWidget->property(
"qgisRelation" );
2870 if ( vRel.isValid() )
2873 QgsRelation relation = relMgr->
relation( vRel.toString() );
2876 QgsRelationWidgetWrapper *rww =
new QgsRelationWidgetWrapper( mLayer, relation, myWidget,
this );
2877 rww->
setConfig( mLayer->editFormConfig().widgetConfig( relation.
id() ) );
2880 mWidgets.append( rww );
2885 const auto constFields = fields;
2886 for (
const QgsField &field : constFields )
2888 if ( field.name() == myWidget->objectName() )
2890 int idx = mLayer->fields().lookupField( field.name() );
2893 mWidgets.append( eww );
2900void QgsAttributeForm::afterWidgetInit()
2902 bool isFirstEww =
true;
2904 const auto constMWidgets = mWidgets;
2905 for ( QgsWidgetWrapper *ww : constMWidgets )
2907 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
2913 setFocusProxy( eww->
widget() );
2922 QgsRelationWidgetWrapper *relationWidgetWrapper = qobject_cast<QgsRelationWidgetWrapper *>( ww );
2923 if ( relationWidgetWrapper )
2936 if ( e->type() == QEvent::KeyPress )
2938 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
2939 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
2950void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet<int> &mixedValueFields, QHash<int, QVariant> &fieldSharedValues )
const
2952 mixedValueFields.clear();
2953 fieldSharedValues.clear();
2959 for (
int i = 0; i < mLayer->
fields().count(); ++i )
2961 if ( mixedValueFields.contains( i ) )
2966 fieldSharedValues[i] = f.
attribute( i );
2970 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
2972 fieldSharedValues.remove( i );
2973 mixedValueFields.insert( i );
2979 if ( mixedValueFields.count() == mLayer->fields().count() )
2988void QgsAttributeForm::layerSelectionChanged()
3001 resetMultiEdit(
true );
3008 mIsSettingMultiEditFeatures =
true;
3009 mMultiEditFeatureIds = fids;
3011 if ( fids.isEmpty() )
3014 QMultiMap<int, QgsAttributeFormEditorWidget *>::const_iterator wIt = mFormEditorWidgets.constBegin();
3015 for ( ; wIt != mFormEditorWidgets.constEnd(); ++wIt )
3017 wIt.value()->initialize( QVariant() );
3019 mIsSettingMultiEditFeatures =
false;
3026 QSet<int> mixedValueFields;
3027 QHash<int, QVariant> fieldSharedValues;
3028 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
3031 fit = mLayer->getFeatures(
QgsFeatureRequest().setFilterFid( *fids.constBegin() ) );
3037 if ( mCurrentFormFeature.id() != firstFeature.
id() )
3042 const auto constMixedValueFields = mixedValueFields;
3043 for (
int fieldIndex : std::as_const( mixedValueFields ) )
3045 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( fieldIndex );
3046 if ( formEditorWidgets.isEmpty() )
3049 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3050 QVariantList additionalFieldValues;
3051 for (
const QString &additionalField : additionalFields )
3052 additionalFieldValues << firstFeature.
attribute( additionalField );
3055 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
3057 QHash<int, QVariant>::const_iterator sharedValueIt = fieldSharedValues.constBegin();
3058 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
3060 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( sharedValueIt.key() );
3061 if ( formEditorWidgets.isEmpty() )
3065 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3066 for (
const QString &additionalField : additionalFields )
3068 int index = mLayer->fields().indexFromName( additionalField );
3069 if ( constMixedValueFields.contains( index ) )
3076 QVariantList additionalFieldValues;
3079 for (
const QString &additionalField : additionalFields )
3080 additionalFieldValues << firstFeature.
attribute( additionalField );
3082 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
3086 for (
const QString &additionalField : additionalFields )
3088 int index = mLayer->fields().indexFromName( additionalField );
3089 Q_ASSERT( fieldSharedValues.contains( index ) );
3090 additionalFieldValues << fieldSharedValues.value( index );
3093 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
3097 setMultiEditFeatureIdsRelations( fids );
3099 mIsSettingMultiEditFeatures =
false;
3104 if ( mOwnsMessageBar )
3106 mOwnsMessageBar =
false;
3107 mMessageBar = messageBar;
3117 QStringList filters;
3120 QString filter = widget->currentFilterExpression();
3121 if ( !filter.isNull() )
3122 filters <<
'(' + filter +
')';
3125 return filters.join( QLatin1String(
" AND " ) );
3130 mExtraContextScope.reset( extraScope );
3135 const bool newVisibility = expression.
evaluate( expressionContext ).toBool();
3145 widget->setVisible( newVisibility );
3148 isVisible = newVisibility;
3151 const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
3153 if ( collapsedExpression.isValid() && !collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
3155 if ( QgsCollapsibleGroupBoxBasic * collapsibleGroupBox { qobject_cast<QgsCollapsibleGroupBoxBasic *>( widget ) } )
3157 collapsibleGroupBox->
setCollapsed( newCollapsedState );
3158 isCollapsed = newCollapsedState;
3163void QgsAttributeForm::updateJoinedFields(
const QgsEditorWidgetWrapper &eww )
3168 QgsFeature formFeature;
3172 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
3175 const QString hint = tr(
"No feature joined" );
3176 const auto constInfos = infos;
3177 for (
const QgsVectorLayerJoinInfo *info : constInfos )
3179 if ( !info->isDynamicFormEnabled() )
3182 QgsFeature joinFeature = mLayer->joinBuffer()->joinedFeatureOf( info, formFeature );
3184 mJoinedFeatures[info] = joinFeature;
3186 if ( info->hasSubset() )
3190 const auto constSubsetNames = subsetNames;
3191 for (
const QString &field : constSubsetNames )
3193 QString prefixedName = info->prefixedFieldName( field );
3195 QString hintText = hint;
3208 const QgsFields joinFields = joinFeature.
fields();
3209 for (
const QgsField &field : joinFields )
3211 QString prefixedName = info->prefixedFieldName( field );
3213 QString hintText = hint;
3227bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
3232void QgsAttributeForm::updateFieldDependencies()
3234 mDefaultValueDependencies.clear();
3235 mVirtualFieldsDependencies.clear();
3236 mRelatedLayerFieldsDependencies.clear();
3237 mParentDependencies.clear();
3240 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3242 QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3246 updateFieldDependenciesParent( eww );
3247 updateFieldDependenciesDefaultValue( eww );
3248 updateFieldDependenciesVirtualFields( eww );
3249 updateRelatedLayerFieldsDependencies( eww );
3253void QgsAttributeForm::updateFieldDependenciesDefaultValue( QgsEditorWidgetWrapper *eww )
3257 if ( exp.needsGeometry() )
3258 mNeedsGeometry =
true;
3263 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3265 for (
const int id : allAttributeIds )
3267 mDefaultValueDependencies.insertMulti(
id, eww );
3273 const QSet<QString> referencedColumns = exp.referencedColumns();
3274 for (
const QString &referencedColumn : referencedColumns )
3276 mDefaultValueDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3281void QgsAttributeForm::updateFieldDependenciesVirtualFields( QgsEditorWidgetWrapper *eww )
3284 if ( expressionField.isEmpty() )
3287 QgsExpression exp( expressionField );
3289 if ( exp.needsGeometry() )
3290 mNeedsGeometry =
true;
3295 const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
3297 for (
const int id : allAttributeIds )
3299 mVirtualFieldsDependencies.insertMulti(
id, eww );
3305 const QSet<QString> referencedColumns = exp.referencedColumns();
3306 for (
const QString &referencedColumn : referencedColumns )
3308 mVirtualFieldsDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
3313void QgsAttributeForm::updateRelatedLayerFieldsDependencies( QgsEditorWidgetWrapper *eww )
3318 if ( expressionField.contains( QStringLiteral(
"relation_aggregate" ) )
3319 || expressionField.contains( QStringLiteral(
"get_features" ) ) )
3320 mRelatedLayerFieldsDependencies.insert( eww );
3324 mRelatedLayerFieldsDependencies.clear();
3326 for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
3328 QgsEditorWidgetWrapper *editorWidgetWrapper = qobject_cast<QgsEditorWidgetWrapper *>( ww );
3329 if ( !editorWidgetWrapper )
3332 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
3337void QgsAttributeForm::updateFieldDependenciesParent( QgsEditorWidgetWrapper *eww )
3342 const QSet<QString> referencedVariablesAndFunctions = expression.referencedVariables() + expression.referencedFunctions();
3343 for (
const QString &referenced : referencedVariablesAndFunctions )
3345 if ( referenced.startsWith( QLatin1String(
"current_parent" ) ) )
3347 mParentDependencies.insert( eww );
3354void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
3356 for ( QgsAttributeFormWidget *formWidget : mFormWidgets )
3358 QgsAttributeFormRelationEditorWidget *relationEditorWidget =
dynamic_cast<QgsAttributeFormRelationEditorWidget *
>( formWidget );
3359 if ( !relationEditorWidget )
3366void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww )
3372 mIconMap[eww->
widget()]->hide();
3374 if ( !eww->
widget()->isEnabled() && mLayer->isEditable() )
3379 const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( eww->
fieldIdx(), mLayer->fields(), srcFieldIndex );
3386 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
3387 const QString tooltip = tr(
"Join settings do not allow editing" );
3388 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3392 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
3393 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
3394 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3398 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
3399 const QString tooltip = tr(
"Joined layer is not toggled editable" );
3400 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3406void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
3409 sw->setToolTip( tooltip );
3416 QgsDebugMsgLevel( QStringLiteral(
"reuseAllLastValues: %1" ).arg( reuseAllLastValues ), 2 );
3420 for (
int idx = 0; idx < fields.
count(); ++idx )
3422 if ( attributes.contains( idx ) )
3424 initialAttributeValues.insert( idx, attributes.value( idx ) );
3428 const QVariant lastUsedValuesVariant =
layer->property(
"AttributeFormLastUsedValues" );
3430 if ( lastUsedValues.contains( idx ) &&
layer->dataProvider() &&
layer->dataProvider()->defaultValueClause( idx ) != lastUsedValues[idx] )
3432 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.
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 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.
static bool fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature)
Tests whether a field is editable for a particular feature.
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)