75int QgsAttributeForm::sFormCounter = 0;
80 , mOwnsMessageBar( true )
82 , mFormNr( sFormCounter++ )
84 , mPreventFeatureRefresh( false )
85 , mIsSettingMultiEditFeatures( false )
86 , mUnsavedMultiEditChanges( false )
87 , mEditCommandMessage( tr(
"Attributes changed" ) )
100 updateContainersVisibility();
102 updateEditableState();
109 qDeleteAll( mInterfaces );
136 mInterfaces.append( iface );
152 if ( mUnsavedMultiEditChanges )
155 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
156 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
157 if ( res == QMessageBox::Yes )
162 clearMultiEditMessages();
164 mUnsavedMultiEditChanges =
false;
216 w->setContext( newContext );
222 w->setVisible( relationWidgetsVisible );
229 mSearchButtonBox->setVisible(
false );
234 mSearchButtonBox->setVisible(
false );
239 mSearchButtonBox->setVisible(
false );
243 resetMultiEdit(
false );
245 mSearchButtonBox->setVisible(
false );
249 mSearchButtonBox->setVisible(
true );
255 mSearchButtonBox->setVisible(
false );
263 mSearchButtonBox->setVisible(
false );
272 const auto constMWidgets = mWidgets;
287 QVariant mainValue = eww->
value();
289 additionalFieldValues[index] = value;
290 eww->
setValues( mainValue, additionalFieldValues );
304 mIsSettingFeature =
true;
321 mIsSettingFeature =
false;
322 const auto constMInterfaces = mInterfaces;
325 iface->featureChanged();
341 mIsSettingFeature =
false;
344bool QgsAttributeForm::saveEdits( QString *error )
347 bool changedLayer =
false;
352 bool doUpdate =
false;
372 *error = tr(
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
375 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
376 QVariantList srcVars = QVariantList() << eww->
value();
377 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
381 for (
const QString &fieldName : additionalFields )
385 dstVars << dst.at( idx );
389 Q_ASSERT( dstVars.count() == srcVars.count() );
391 for (
int i = 0; i < dstVars.count(); i++ )
394 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
396 dst[fieldIndexes[i]] = srcVars[i];
406 const auto constMInterfaces = mInterfaces;
409 if ( !iface->acceptChanges( updatedFeature ) )
419 mFeature = updatedFeature;
425 bool res = mLayer->
addFeature( updatedFeature );
444 for (
int i = 0; i < dst.count(); ++i )
447 || !dst.at( i ).isValid()
448 || !fieldIsEditable( i ) )
454 QgsDebugMsgLevel( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
455 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( dst.at( i ) ) ).arg( dst.at( i ).isValid() ), 2 );
456 QgsDebugMsgLevel( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
457 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( src.at( i ) ) ).arg( src.at( i ).isValid() ), 2 );
459 newValues[i] = dst.at( i );
460 oldValues[i] = src.at( i );
467 if ( success && n > 0 )
494QgsFeature QgsAttributeForm::getUpdatedFeature()
const
506 QVariantList dstVars = QVariantList() << featureAttributes.at( eww->
fieldIdx() );
507 QVariantList srcVars = QVariantList() << eww->
value();
508 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
512 for (
const QString &fieldName : additionalFields )
516 dstVars << featureAttributes.at( idx );
520 Q_ASSERT( dstVars.count() == srcVars.count() );
522 for (
int i = 0; i < dstVars.count(); i++ )
524 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
525 featureAttributes[fieldIndexes[i]] = srcVars[i];
530 return updatedFeature;
533void QgsAttributeForm::updateValuesDependencies(
const int originIdx )
535 updateValuesDependenciesDefaultValues( originIdx );
536 updateValuesDependenciesVirtualFields( originIdx );
539void QgsAttributeForm::updateValuesDependenciesDefaultValues(
const int originIdx )
541 if ( !mDefaultValueDependencies.contains( originIdx ) )
549 QgsFeature updatedFeature = getUpdatedFeature();
552 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
569 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
580void QgsAttributeForm::updateValuesDependenciesVirtualFields(
const int originIdx )
582 if ( !mVirtualFieldsDependencies.contains( originIdx ) )
589 QgsFeature updatedFeature = getUpdatedFeature();
592 const QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
600 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
606 const QVariant value = exp.evaluate( &context );
612void QgsAttributeForm::updateRelatedLayerFields()
615 updateRelatedLayerFieldsDependencies();
617 if ( mRelatedLayerFieldsDependencies.isEmpty() )
624 QgsFeature updatedFeature = getUpdatedFeature();
627 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mRelatedLayerFieldsDependencies;
631 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
637 QVariant value = exp.evaluate( &context );
642void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
647 mUnsavedMultiEditChanges =
false;
651void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
653 clearMultiEditMessages();
654 resetMultiEdit( link == QLatin1String(
"#apply" ) );
657void QgsAttributeForm::filterTriggered()
659 QString filter = createFilterExpression();
665void QgsAttributeForm::searchZoomTo()
667 QString filter = createFilterExpression();
668 if ( filter.isEmpty() )
674void QgsAttributeForm::searchFlash()
676 QString filter = createFilterExpression();
677 if ( filter.isEmpty() )
683void QgsAttributeForm::filterAndTriggered()
685 QString filter = createFilterExpression();
686 if ( filter.isEmpty() )
694void QgsAttributeForm::filterOrTriggered()
696 QString filter = createFilterExpression();
697 if ( filter.isEmpty() )
705void QgsAttributeForm::pushSelectedFeaturesMessage()
711 tr(
"%n matching feature(s) selected",
"matching features", count ),
712 Qgis::MessageLevel::Info );
717 tr(
"No matching features found" ),
718 Qgis::MessageLevel::Info );
726 Qgis::MessageLevel::Warning );
731 QString filter = createFilterExpression();
732 if ( filter.isEmpty() )
736 pushSelectedFeaturesMessage();
741void QgsAttributeForm::searchSetSelection()
746void QgsAttributeForm::searchAddToSelection()
751void QgsAttributeForm::searchRemoveFromSelection()
756void QgsAttributeForm::searchIntersectSelection()
761bool QgsAttributeForm::saveMultiEdits()
765 const QList<int> fieldIndexes = mFormEditorWidgets.uniqueKeys();
766 mFormEditorWidgets.constBegin();
767 for (
int fieldIndex : fieldIndexes )
769 const QList<QgsAttributeFormEditorWidget *> widgets = mFormEditorWidgets.values( fieldIndex );
770 if ( !widgets.first()->hasChanged() )
773 if ( !widgets.first()->currentValue().isValid()
774 || !fieldIsEditable( fieldIndex ) )
781 widget->changesCommitted();
783 newAttributeValues.insert( fieldIndex, widgets.first()->currentValue() );
786 if ( newAttributeValues.isEmpty() )
794 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
795 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
796 if ( res != QMessageBox::Ok )
807 const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
810 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
811 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
817 clearMultiEditMessages();
822 mMultiEditMessageBarItem =
new QgsMessageBarItem( tr(
"Attribute changes for multiple features applied." ), Qgis::MessageLevel::Success, -1 );
827 mMultiEditMessageBarItem =
new QgsMessageBarItem( tr(
"Changes could not be applied." ), Qgis::MessageLevel::Warning, 0 );
830 if ( !mButtonBox->isVisible() )
831 mMessageBar->
pushItem( mMultiEditMessageBarItem );
857 wrapper->notifyAboutToSave();
897 success = saveEdits( error );
901 success = saveMultiEdits();
906 mUnsavedMultiEditChanges =
false;
915 mValuesInitialized =
false;
916 const auto constMWidgets = mWidgets;
919 ww->setFeature( mFeature );
923 updateFieldDependencies();
933 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
934 updateValuesDependencies( eww->
fieldIdx() );
935 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
938 mValuesInitialized =
true;
944 const auto widgets { findChildren< QgsAttributeFormEditorWidget * >() };
951void QgsAttributeForm::clearMultiEditMessages()
953 if ( mMultiEditUnsavedMessageBarItem )
955 if ( !mButtonBox->isVisible() )
956 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
957 mMultiEditUnsavedMessageBarItem =
nullptr;
959 if ( mMultiEditMessageBarItem )
961 if ( !mButtonBox->isVisible() )
962 mMessageBar->
popWidget( mMultiEditMessageBarItem );
963 mMultiEditMessageBarItem =
nullptr;
967QString QgsAttributeForm::createFilterExpression()
const
972 QString filter = w->currentFilterExpression();
973 if ( !filter.isEmpty() )
977 if ( filters.isEmpty() )
980 QString filter = filters.join( QLatin1String(
") AND (" ) ).prepend(
'(' ).append(
')' );
989 if ( mExtraContextScope )
996void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
1001 bool signalEmitted =
false;
1003 if ( mValuesInitialized )
1022 for (
int i = 0; i < additionalFields.count(); i++ )
1024 const QString fieldName = additionalFields.at( i );
1025 const QVariant value = additionalFieldValues.at( i );
1029 signalEmitted =
true;
1031 if ( mValuesInitialized )
1032 updateJoinedFields( *eww );
1038 if ( !mIsSettingMultiEditFeatures )
1040 mUnsavedMultiEditChanges =
true;
1042 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
1043 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
1044 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1045 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
1046 clearMultiEditMessages();
1048 mMultiEditUnsavedMessageBarItem =
new QgsMessageBarItem( msgLabel, Qgis::MessageLevel::Warning );
1049 if ( !mButtonBox->isVisible() )
1050 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
1053 signalEmitted =
true;
1063 updateConstraints( eww );
1066 if ( mValuesInitialized )
1069 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1070 updateValuesDependencies( eww->
fieldIdx() );
1071 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1076 updateEditableState();
1079 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1082 if ( formEditorWidget->editorWidget() == eww )
1084 formEditorWidget->editorWidget()->setValue( value );
1087 if ( !signalEmitted )
1092 bool attributeHasChanged = !mIsSettingFeature;
1094 attributeHasChanged &= !mIsSettingMultiEditFeatures;
1100void QgsAttributeForm::updateAllConstraints()
1102 const auto constMWidgets = mWidgets;
1107 updateConstraints( eww );
1115 if ( currentFormValuesFeature( ft ) )
1127 updateConstraint( ft, eww );
1130 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
1133 updateConstraint( ft, depsEww );
1141 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
1142 for ( ContainerInformation *info : infos )
1144 info->apply( &context );
1149void QgsAttributeForm::updateContainersVisibility()
1153 const QVector<ContainerInformation *> infos = mContainerVisibilityCollapsedInformation;
1155 for ( ContainerInformation *info : infos )
1157 info->apply( &context );
1165 if ( mMode != QgsAttributeEditorContext::Mode::MultiEditMode )
1167 updateAllConstraints();
1183 if ( mJoinedFeatures.contains( info ) )
1200void QgsAttributeForm::updateLabels()
1202 if ( ! mLabelDataDefinedProperties.isEmpty() )
1205 if ( currentFormValuesFeature( currentFeature ) )
1209 for (
auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
1211 QLabel *label { it.key() };
1213 const QString value { it->valueAsString( context, QString(), &ok ) };
1214 if ( ok && ! value.isEmpty() )
1216 label->setText( value );
1223void QgsAttributeForm::updateEditableState()
1225 if ( ! mEditableDataDefinedProperties.isEmpty() )
1228 if ( currentFormValuesFeature( currentFeature ) )
1232 for (
auto it = mEditableDataDefinedProperties.constBegin() ; it != mEditableDataDefinedProperties.constEnd(); ++it )
1234 QWidget *w { it.key() };
1236 const bool isEditable { it->valueAsBool( context,
true, &ok ) };
1246 w->setEnabled( isEditable );
1254bool QgsAttributeForm::currentFormValuesFeature(
QgsFeature &feature )
1267 if ( dst.count() > eww->
fieldIdx() )
1269 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1270 QVariantList srcVars = QVariantList() << eww->
value();
1271 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1275 for (
const QString &fieldName : additionalFields )
1278 fieldIndexes << idx;
1279 dstVars << dst.at( idx );
1283 Q_ASSERT( dstVars.count() == srcVars.count() );
1285 for (
int i = 0; i < dstVars.count(); i++ )
1291 dst[fieldIndexes[i]] = srcVars[i];
1308void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1310 mContainerVisibilityCollapsedInformation.append( info );
1312 const QSet<QString> referencedColumns = info->expression.referencedColumns().unite( info->collapsedExpression.referencedColumns() );
1314 for (
const QString &col : referencedColumns )
1316 mContainerInformationDependency[ col ].append( info );
1320bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1344bool QgsAttributeForm::currentFormValidHardConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1365void QgsAttributeForm::onAttributeAdded(
int idx )
1367 mPreventFeatureRefresh =
false;
1371 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
1379void QgsAttributeForm::onAttributeDeleted(
int idx )
1381 mPreventFeatureRefresh =
false;
1385 attrs.remove( idx );
1393void QgsAttributeForm::onRelatedFeaturesChanged()
1395 updateRelatedLayerFields();
1398void QgsAttributeForm::onUpdatedFields()
1400 mPreventFeatureRefresh =
false;
1417 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
1427void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1433 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1436 formEditorWidget->setConstraintStatus( constraint, description, err, result );
1441 QList<QgsEditorWidgetWrapper *> wDeps;
1453 if ( name != ewwName )
1460 for (
const QString &colName : referencedColumns )
1462 if ( name == colName )
1464 wDeps.append( eww );
1477 return setupRelationWidgetWrapper( QString(), rel, context );
1490void QgsAttributeForm::preventFeatureRefresh()
1492 mPreventFeatureRefresh =
true;
1523 return mNeedsGeometry;
1526void QgsAttributeForm::synchronizeState()
1528 bool isEditable = ( mFeature.
isValid()
1538 const QList<QgsAttributeFormEditorWidget *> formWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1541 formWidget->setConstraintResultVisible( isEditable );
1545 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1546 ww->setEnabled( enabled );
1552 ww->setEnabled( isEditable );
1560 if ( mMode == QgsAttributeEditorContext::Mode::MultiEditMode && mLayer->
selectedFeatureCount() == 0 )
1563 if ( mConstraintsFailMessageBarItem )
1565 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1567 mConstraintsFailMessageBarItem =
new QgsMessageBarItem( tr(
"Multi edit mode requires at least one selected feature." ), Qgis::MessageLevel::Info, -1 );
1568 mMessageBar->
pushItem( mConstraintsFailMessageBarItem );
1572 QStringList invalidFields, descriptions;
1573 mValidConstraints = currentFormValidHardConstraints( invalidFields, descriptions );
1577 if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
1579 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 );
1580 mMessageBar->
pushItem( mConstraintsFailMessageBarItem );
1582 else if ( mValidConstraints && mConstraintsFailMessageBarItem )
1584 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1585 mConstraintsFailMessageBarItem =
nullptr;
1588 else if ( mConstraintsFailMessageBarItem )
1590 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1591 mConstraintsFailMessageBarItem =
nullptr;
1594 isEditable = isEditable & mValidConstraints;
1599 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1601 okButton->setEnabled( isEditable );
1604void QgsAttributeForm::init()
1606 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1609 QWidget *formWidget =
nullptr;
1610 mNeedsGeometry =
false;
1612 bool buttonBoxVisible =
true;
1616 buttonBoxVisible = mButtonBox->isVisible();
1618 mButtonBox =
nullptr;
1621 if ( mSearchButtonBox )
1623 delete mSearchButtonBox;
1624 mSearchButtonBox =
nullptr;
1627 qDeleteAll( mWidgets );
1630 while ( QWidget *w = this->findChild<QWidget *>() )
1636 QVBoxLayout *vl =
new QVBoxLayout();
1637 vl->setContentsMargins( 0, 0, 0, 0 );
1639 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1640 vl->addWidget( mMessageBar );
1645 QGridLayout *layout =
new QGridLayout();
1646 QWidget *container =
new QWidget();
1647 container->setLayout( layout );
1648 vl->addWidget( container );
1650 mFormEditorWidgets.clear();
1651 mFormWidgets.clear();
1654 setContentsMargins( 0, 0, 0, 0 );
1663 if ( file && file->open( QFile::ReadOnly ) )
1667 QFileInfo fi( file->fileName() );
1668 loader.setWorkingDirectory( fi.dir() );
1669 formWidget = loader.load( file,
this );
1672 formWidget->setWindowFlags( Qt::Widget );
1673 layout->addWidget( formWidget );
1676 mButtonBox = findChild<QDialogButtonBox *>();
1679 formWidget->installEventFilter(
this );
1691 int columnCount = 1;
1692 bool hasRootFields =
false;
1693 bool addSpacer =
true;
1702 if ( !containerDef )
1707 tabWidget =
nullptr;
1708 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1709 if ( widgetInfo.labelStyle.overrideColor )
1711 if ( widgetInfo.labelStyle.color.isValid() )
1713 widgetInfo.widget->setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1716 if ( widgetInfo.labelStyle.overrideFont )
1718 widgetInfo.widget->setFont( widgetInfo.labelStyle.font );
1720 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1732 layout->addWidget( tabWidget, row, column, 1, 2 );
1736 QWidget *tabPage =
new QWidget( tabWidget );
1738 tabWidget->addTab( tabPage, widgDef->name() );
1739 tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
1743 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1745 QGridLayout *tabPageLayout =
new QGridLayout();
1746 tabPage->setLayout( tabPageLayout );
1748 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1749 tabPageLayout->addWidget( widgetInfo.widget );
1754 hasRootFields =
true;
1755 tabWidget =
nullptr;
1756 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1759 if ( widgetInfo.showLabel )
1761 if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
1763 collapsibleGroupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1766 if ( widgetInfo.labelStyle.overrideFont )
1768 collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
1771 collapsibleGroupBox->setTitle( widgetInfo.labelText );
1774 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1775 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
1776 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1778 QVBoxLayout *
c =
new QVBoxLayout();
1779 c->addWidget( collapsibleGroupBox );
1780 layout->addLayout(
c, row, column, 1, 2 );
1788 hasRootFields =
true;
1789 tabWidget =
nullptr;
1790 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1791 QLabel *label =
new QLabel( widgetInfo.labelText );
1793 if ( widgetInfo.labelStyle.overrideColor )
1795 if ( widgetInfo.labelStyle.color.isValid() )
1797 label->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1801 if ( widgetInfo.labelStyle.overrideFont )
1803 label->setFont( widgetInfo.labelStyle.font );
1806 label->setToolTip( widgetInfo.toolTip );
1807 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1809 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1812 label->setBuddy( widgetInfo.widget );
1815 if ( widgetInfo.widget
1816 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
1817 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
1818 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
1821 if ( !widgetInfo.showLabel )
1823 QVBoxLayout *
c =
new QVBoxLayout();
1824 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1825 c->addWidget( widgetInfo.widget );
1826 layout->addLayout(
c, row, column, 1, 2 );
1829 else if ( widgetInfo.labelOnTop )
1831 QVBoxLayout *
c =
new QVBoxLayout();
1832 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1833 c->addWidget( label );
1834 c->addWidget( widgetInfo.widget );
1835 layout->addLayout(
c, row, column, 1, 2 );
1840 layout->addWidget( label, row, column++ );
1841 layout->addWidget( widgetInfo.widget, row, column++ );
1845 if ( widgDef->type() == QgsAttributeEditorElement::AttributeEditorType::AeTypeField )
1848 const int fieldIdx = fieldElement->
idx();
1849 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
1851 const QString fieldName { mLayer->
fields().
at( fieldIdx ).
name() };
1855 if ( property.isActive() )
1857 mLabelDataDefinedProperties[ label ] = property;
1863 if ( property.isActive() )
1865 mEditableDataDefinedProperties[ widgetInfo.widget ] = property;
1872 if ( column >= columnCount * 2 )
1879 if ( hasRootFields && addSpacer )
1881 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1882 layout->addItem( spacerItem, row, 0 );
1883 layout->setRowStretch( row, 1 );
1886 formWidget = container;
1895 formWidget =
new QWidget(
this );
1896 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1897 formWidget->setLayout( gridLayout );
1903 scrollArea->setWidget( formWidget );
1904 scrollArea->setWidgetResizable(
true );
1905 scrollArea->setFrameShape( QFrame::NoFrame );
1906 scrollArea->setFrameShadow( QFrame::Plain );
1907 scrollArea->setFocusProxy(
this );
1908 layout->addWidget( scrollArea );
1912 layout->addWidget( formWidget );
1927 QString labelText = fieldName;
1928 labelText.replace(
'&', QLatin1String(
"&&" ) );
1932 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1938 QLabel *label =
new QLabel( labelText );
1940 QSvgWidget *i =
new QSvgWidget();
1941 i->setFixedSize( 18, 18 );
1946 if ( property.isActive() )
1948 mLabelDataDefinedProperties[ label ] = property;
1954 QWidget *w =
nullptr;
1959 mFormEditorWidgets.insert( idx, formWidget );
1960 mFormWidgets.append( formWidget );
1963 label->setBuddy( eww->
widget() );
1968 if ( property.isActive() )
1970 mEditableDataDefinedProperties[ formWidget ] = property;
1976 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() ) ) );
1985 mWidgets.append( eww );
1986 mIconMap[eww->
widget()] = i;
1991 gridLayout->addWidget( label, row++, 0, 1, 2 );
1992 gridLayout->addWidget( w, row++, 0, 1, 2 );
1993 gridLayout->addWidget( i, row++, 0, 1, 2 );
1997 gridLayout->addWidget( label, row, 0 );
1998 gridLayout->addWidget( w, row, 1 );
1999 gridLayout->addWidget( i, row++, 2 );
2013 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
2014 collapsibleGroupBoxLayout->addWidget( formWidget );
2015 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
2017 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
2019 mWidgets.append( rww );
2020 mFormWidgets.append( formWidget );
2025 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2026 gridLayout->addItem( spacerItem, row, 0 );
2027 gridLayout->setRowStretch( row, 1 );
2032 updateFieldDependencies();
2036 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
2037 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
2038 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2040 mButtonBox->setVisible( buttonBoxVisible );
2042 if ( !mSearchButtonBox )
2044 mSearchButtonBox =
new QWidget();
2045 QHBoxLayout *boxLayout =
new QHBoxLayout();
2046 boxLayout->setContentsMargins( 0, 0, 0, 0 );
2047 mSearchButtonBox->setLayout( boxLayout );
2048 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
2050 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
2052 boxLayout->addWidget( clearButton );
2053 boxLayout->addStretch( 1 );
2055 QPushButton *flashButton =
new QPushButton();
2056 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2057 flashButton->setText( tr(
"&Flash Features" ) );
2058 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
2059 boxLayout->addWidget( flashButton );
2061 QPushButton *openAttributeTableButton =
new QPushButton();
2062 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2063 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
2064 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
2065 connect( openAttributeTableButton, &QToolButton::clicked,
this, [ = ]
2069 boxLayout->addWidget( openAttributeTableButton );
2071 QPushButton *zoomButton =
new QPushButton();
2072 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2073 zoomButton->setText( tr(
"&Zoom to Features" ) );
2074 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
2075 boxLayout->addWidget( zoomButton );
2077 QToolButton *selectButton =
new QToolButton();
2078 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2079 selectButton->setText( tr(
"&Select Features" ) );
2081 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
2082 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
2083 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
2084 QMenu *selectMenu =
new QMenu( selectButton );
2085 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
2087 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
2088 selectMenu->addAction( selectAction );
2089 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
2091 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
2092 selectMenu->addAction( addSelectAction );
2093 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
2095 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
2096 selectMenu->addAction( deselectAction );
2097 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
2099 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
2100 selectMenu->addAction( filterSelectAction );
2101 selectButton->setMenu( selectMenu );
2102 boxLayout->addWidget( selectButton );
2106 QToolButton *filterButton =
new QToolButton();
2107 filterButton->setText( tr(
"Filter Features" ) );
2108 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
2109 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2110 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
2111 QMenu *filterMenu =
new QMenu( filterButton );
2112 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
2113 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
2114 filterMenu->addAction( filterAndAction );
2115 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
2116 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
2117 filterMenu->addAction( filterOrAction );
2118 filterButton->setMenu( filterMenu );
2119 boxLayout->addWidget( filterButton );
2123 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
2125 closeButton->setShortcut( Qt::Key_Escape );
2126 boxLayout->addWidget( closeButton );
2129 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2147 const auto constMInterfaces = mInterfaces;
2158 QApplication::restoreOverrideCursor();
2161void QgsAttributeForm::cleanPython()
2163 if ( !mPyFormVarName.isNull() )
2165 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
2170void QgsAttributeForm::initPython()
2187 if ( !initFilePath.isEmpty() )
2191 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2194 QTextStream inf( inputFile );
2195#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2196 inf.setCodec(
"UTF-8" );
2198 initCode = inf.readAll();
2203 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
2214 if ( initCode.isEmpty() )
2216 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
2227 if ( !initCode.isEmpty() )
2233 tr(
"Python macro could not be run due to missing permissions." ),
2234 Qgis::MessageLevel::Warning );
2241 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
2243 static int sFormId = 0;
2244 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
2246 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
2247 .arg( mPyFormVarName )
2248 .arg( ( quint64 )
this );
2252 QgsDebugMsg( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
2255 if ( numArgs == QLatin1String(
"3" ) )
2263 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 ) );
2266 QString expr = QString(
"%1(%2)" )
2267 .arg( mLayer->editFormInit() )
2268 .arg( mPyFormVarName );
2269 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2279 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 ) );
2287 WidgetInfo newWidgetInfo;
2289 newWidgetInfo.labelStyle = widgetDef->
labelStyle();
2291 switch ( widgetDef->
type() )
2303 mWidgets.append( actionWrapper );
2304 newWidgetInfo.widget = actionWrapper->
widget();
2305 newWidgetInfo.showLabel =
false;
2318 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2324 mFormEditorWidgets.insert( fldIdx, formWidget );
2325 mFormWidgets.append( formWidget );
2329 newWidgetInfo.widget = formWidget;
2330 mWidgets.append( eww );
2332 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2333 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
2338 newWidgetInfo.labelText.replace(
'&', QLatin1String(
"&&" ) );
2339 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
2340 newWidgetInfo.showLabel = widgetDef->
showLabel();
2361 mWidgets.append( rww );
2362 mFormWidgets.append( formWidget );
2364 newWidgetInfo.widget = formWidget;
2365 newWidgetInfo.showLabel = relDef->
showLabel();
2366 newWidgetInfo.labelText = relDef->
label();
2367 if ( newWidgetInfo.labelText.isEmpty() )
2369 newWidgetInfo.labelOnTop =
true;
2381 if ( columnCount <= 0 )
2385 QWidget *myContainer =
nullptr;
2389 widgetName = QStringLiteral(
"QGroupBox" );
2392 groupBox->setTitle( container->
name() );
2393 if ( newWidgetInfo.labelStyle.overrideColor )
2395 if ( newWidgetInfo.labelStyle.color.isValid() )
2397 groupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2400 if ( newWidgetInfo.labelStyle.overrideFont )
2402 groupBox->setFont( newWidgetInfo.labelStyle.font );
2405 myContainer = groupBox;
2406 newWidgetInfo.widget = myContainer;
2411 myContainer =
new QWidget();
2415 scrollArea->setWidget( myContainer );
2416 scrollArea->setWidgetResizable(
true );
2417 scrollArea->setFrameShape( QFrame::NoFrame );
2418 widgetName = QStringLiteral(
"QScrollArea QWidget" );
2420 newWidgetInfo.widget = scrollArea;
2425 QString style {QStringLiteral(
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
2426 newWidgetInfo.widget->setStyleSheet( style );
2429 QGridLayout *gbLayout =
new QGridLayout();
2430 myContainer->setLayout( gbLayout );
2434 bool addSpacer =
true;
2436 const QList<QgsAttributeEditorElement *> children = container->
children();
2440 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2451 if ( widgetInfo.labelText.isNull() || ! widgetInfo.showLabel )
2453 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2458 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2460 if ( widgetInfo.labelStyle.overrideColor )
2462 if ( widgetInfo.labelStyle.color.isValid() )
2464 mypLabel->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2468 if ( widgetInfo.labelStyle.overrideFont )
2470 mypLabel->setFont( widgetInfo.labelStyle.font );
2478 const int fldIdx = fieldDef->
idx();
2479 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2481 const QString fieldName { fields.
at( fldIdx ).
name() };
2485 if ( property.isActive() )
2487 mLabelDataDefinedProperties[ mypLabel ] = property;
2493 if ( property.isActive() )
2495 mEditableDataDefinedProperties[ widgetInfo.widget ] = property;
2501 mypLabel->setToolTip( widgetInfo.toolTip );
2502 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2504 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2507 mypLabel->setBuddy( widgetInfo.widget );
2509 if ( widgetInfo.labelOnTop )
2511 QVBoxLayout *
c =
new QVBoxLayout();
2512 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2513 c->layout()->addWidget( mypLabel );
2514 c->layout()->addWidget( widgetInfo.widget );
2515 gbLayout->addLayout(
c, row, column, 1, 2 );
2520 gbLayout->addWidget( mypLabel, row, column++ );
2521 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2525 if ( column >= columnCount * 2 )
2531 if ( widgetInfo.widget
2532 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2533 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2534 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2538 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
2544 QWidget *spacer =
new QWidget();
2545 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2546 gbLayout->addWidget( spacer, ++row, 0 );
2547 gbLayout->setRowStretch( row, 1 );
2550 newWidgetInfo.labelText = QString();
2551 newWidgetInfo.labelOnTop =
true;
2552 newWidgetInfo.showLabel = widgetDef->
showLabel();
2565 mWidgets.append( qmlWrapper );
2567 newWidgetInfo.widget = qmlWrapper->
widget();
2568 newWidgetInfo.labelText = elementDef->
name();
2569 newWidgetInfo.labelOnTop =
true;
2570 newWidgetInfo.showLabel = widgetDef->
showLabel();
2582 mWidgets.append( htmlWrapper );
2584 newWidgetInfo.widget = htmlWrapper->
widget();
2585 newWidgetInfo.labelText = elementDef->
name();
2586 newWidgetInfo.labelOnTop =
true;
2587 newWidgetInfo.showLabel = widgetDef->
showLabel();
2600 mWidgets.append( textWrapper );
2602 newWidgetInfo.widget = textWrapper->
widget();
2603 newWidgetInfo.labelText = elementDef->
name();
2604 newWidgetInfo.labelOnTop =
false;
2605 newWidgetInfo.showLabel = widgetDef->
showLabel();
2616 mWidgets.append( spacerWrapper );
2618 newWidgetInfo.widget = spacerWrapper->
widget();
2619 newWidgetInfo.labelOnTop =
false;
2620 newWidgetInfo.showLabel =
false;
2625 QgsDebugMsg( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
2629 return newWidgetInfo;
2632void QgsAttributeForm::createWrappers()
2634 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2635 const QList<QgsField> fields = mLayer->
fields().
toList();
2637 const auto constMyWidgets = myWidgets;
2638 for ( QWidget *myWidget : constMyWidgets )
2641 QVariant vRel = myWidget->property(
"qgisRelation" );
2642 if ( vRel.isValid() )
2652 mWidgets.append( rww );
2657 const auto constFields = fields;
2660 if (
field.
name() == myWidget->objectName() )
2665 mWidgets.append( eww );
2672void QgsAttributeForm::afterWidgetInit()
2674 bool isFirstEww =
true;
2676 const auto constMWidgets = mWidgets;
2685 setFocusProxy( eww->
widget() );
2695 if ( relationWidgetWrapper )
2708 if ( e->type() == QEvent::KeyPress )
2710 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
2711 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
2723 QSet< int > &mixedValueFields,
2724 QHash< int, QVariant > &fieldSharedValues )
const
2726 mixedValueFields.clear();
2727 fieldSharedValues.clear();
2733 for (
int i = 0; i < mLayer->
fields().count(); ++i )
2735 if ( mixedValueFields.contains( i ) )
2740 fieldSharedValues[i] = f.
attribute( i );
2744 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
2746 fieldSharedValues.remove( i );
2747 mixedValueFields.insert( i );
2753 if ( mixedValueFields.count() == mLayer->
fields().
count() )
2762void QgsAttributeForm::layerSelectionChanged()
2775 resetMultiEdit(
true );
2782 mIsSettingMultiEditFeatures =
true;
2783 mMultiEditFeatureIds = fids;
2785 if ( fids.isEmpty() )
2788 QMultiMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2789 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2791 wIt.value()->initialize( QVariant() );
2793 mIsSettingMultiEditFeatures =
false;
2800 QSet< int > mixedValueFields;
2801 QHash< int, QVariant > fieldSharedValues;
2802 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2811 if ( mCurrentFormFeature.
id() != firstFeature.
id( ) )
2816 const auto constMixedValueFields = mixedValueFields;
2817 for (
int fieldIndex : std::as_const( mixedValueFields ) )
2819 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( fieldIndex );
2820 if ( formEditorWidgets.isEmpty() )
2823 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
2824 QVariantList additionalFieldValues;
2825 for (
const QString &additionalField : additionalFields )
2826 additionalFieldValues << firstFeature.
attribute( additionalField );
2829 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
2831 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2832 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2834 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( sharedValueIt.key() );
2835 if ( formEditorWidgets.isEmpty() )
2839 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
2840 for (
const QString &additionalField : additionalFields )
2843 if ( constMixedValueFields.contains( index ) )
2850 QVariantList additionalFieldValues;
2853 for (
const QString &additionalField : additionalFields )
2854 additionalFieldValues << firstFeature.
attribute( additionalField );
2856 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
2860 for (
const QString &additionalField : additionalFields )
2863 Q_ASSERT( fieldSharedValues.contains( index ) );
2864 additionalFieldValues << fieldSharedValues.value( index );
2867 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
2871 setMultiEditFeatureIdsRelations( fids );
2873 mIsSettingMultiEditFeatures =
false;
2878 if ( mOwnsMessageBar )
2880 mOwnsMessageBar =
false;
2881 mMessageBar = messageBar;
2891 QStringList filters;
2894 QString filter = widget->currentFilterExpression();
2895 if ( !filter.isNull() )
2896 filters <<
'(' + filter +
')';
2899 return filters.join( QLatin1String(
" AND " ) );
2904 mExtraContextScope.reset( extraScope );
2910 const bool newVisibility = expression.evaluate( expressionContext ).toBool();
2912 if ( expression.isValid() && ! expression.hasEvalError() && newVisibility != isVisible )
2920 widget->setVisible( newVisibility );
2923 isVisible = newVisibility;
2926 const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
2928 if ( collapsedExpression.isValid() && ! collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
2933 collapsibleGroupBox->
setCollapsed( newCollapsedState );
2934 isCollapsed = newCollapsedState;
2948 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
2951 const QString hint = tr(
"No feature joined" );
2952 const auto constInfos = infos;
2955 if ( !info->isDynamicFormEnabled() )
2960 mJoinedFeatures[info] = joinFeature;
2962 if ( info->hasSubset() )
2966 const auto constSubsetNames = subsetNames;
2967 for (
const QString &
field : constSubsetNames )
2969 QString prefixedName = info->prefixedFieldName(
field );
2971 QString hintText = hint;
2987 QString prefixedName = info->prefixedFieldName(
field );
2989 QString hintText = hint;
3003bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
3008void QgsAttributeForm::updateFieldDependencies()
3010 mDefaultValueDependencies.clear();
3011 mVirtualFieldsDependencies.clear();
3012 mRelatedLayerFieldsDependencies.clear();
3021 updateFieldDependenciesDefaultValue( eww );
3022 updateFieldDependenciesVirtualFields( eww );
3023 updateRelatedLayerFieldsDependencies( eww );
3031 if ( exp.needsGeometry() )
3032 mNeedsGeometry =
true;
3034 const QSet<QString> referencedColumns = exp.referencedColumns();
3035 for (
const QString &referencedColumn : referencedColumns )
3041 for (
const int id : allAttributeIds )
3043 mDefaultValueDependencies.insertMulti(
id, eww );
3048 mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
3056 if ( expressionField.isEmpty() )
3061 if ( exp.needsGeometry() )
3062 mNeedsGeometry =
true;
3064 const QSet<QString> referencedColumns = exp.referencedColumns();
3065 for (
const QString &referencedColumn : referencedColumns )
3070 for (
const int id : allAttributeIds )
3072 mVirtualFieldsDependencies.insertMulti(
id, eww );
3077 mVirtualFieldsDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
3087 if ( expressionField.contains( QStringLiteral(
"relation_aggregate" ) )
3088 || expressionField.contains( QStringLiteral(
"get_features" ) ) )
3089 mRelatedLayerFieldsDependencies.insert( eww );
3093 mRelatedLayerFieldsDependencies.clear();
3098 if ( ! editorWidgetWrapper )
3101 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
3106void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
3111 if ( !relationEditorWidget )
3124 mIconMap[eww->
widget()]->hide();
3138 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
3139 const QString tooltip = tr(
"Join settings do not allow editing" );
3140 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3144 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
3145 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
3146 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3150 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
3151 const QString tooltip = tr(
"Joined layer is not toggled editable" );
3152 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3158void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
3161 sw->setToolTip( tooltip );
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.
This element will load a layer action onto the form.
const QgsAction & action(const QgsVectorLayer *layer) const
Returns the (possibly lazy loaded) action for the given layer.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QgsOptionalExpression visibilityExpression() const
The visibility expression is used in the attribute form to show or hide this container based on an ex...
virtual bool isGroupBox() const
Returns if this container is going to be a group box.
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 if this group box is collapsed.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
QColor backgroundColor() const
backgroundColor
int columnCount() const
Gets the number of columns in this group.
This class contains context information for attribute editor widgets.
FormMode formMode() const
Returns the form mode.
QString attributeFormModeString() const
Returns given attributeFormMode as string.
@ Embed
A form was embedded as a widget on another form.
bool allowCustomUi() const
Returns true if the attribute editor should permit use of custom UI forms.
@ 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.
This is an abstract base class for any elements of a drag and drop form.
LabelStyle labelStyle() const
Returns the label style.
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.
@ AeTypeTextElement
A text element (since QGIS 3.30)
@ AeTypeHtmlElement
A HTML element.
@ AeTypeSpacerElement
A spacer element (since QGIS 3.30)
@ AeTypeQmlElement
A QML element.
@ AeTypeContainer
A container.
@ AeTypeRelation
A relation.
@ AeTypeAction
A layer action element (since QGIS 3.22)
This element will load a field's widget onto the form.
int idx() const
Returns the index of the field.
An attribute editor widget that will represent arbitrary HTML code.
QString htmlCode() const
The Html code that will be represented within this widget.
An attribute editor widget that will represent arbitrary QML code.
QString qmlCode() const
The QML code that will be represented within this widget.
This element will load a relation editor onto the form.
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.
An attribute editor widget that will represent a spacer.
bool drawLine() const
Returns true if the spacer element will contain an horizontal line.
An attribute editor widget that will represent arbitrary text code.
QString text() const
The Text that will be represented within this widget.
A groupbox that collapses/expands when toggled.
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.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states.
Q_GADGET QString expression
Single scope for storing variables and functions for use within a QgsExpressionContext.
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.
Class for parsing and evaluation of expressions (formerly called "search strings").
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class 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...
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.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
void setValid(bool validity)
Sets the validity of the feature.
bool isValid() const
Returns the validity of this feature.
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
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,...
Encapsulate a field in an attribute table or data source.
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.
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
int count() const
Returns number of items.
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
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).
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 pythonMacroAllowed(void(*lambda)()=nullptr, QgsMessageBar *messageBar=nullptr)
Returns true if python macros are currently allowed to be run If the global option is to ask user,...
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.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
Represents an item shown within a QgsMessageBar widget.
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.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar, after hiding the currently visible one and putting it in a 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.
QgsRelationManager * relationManager
static QgsProject * instance()
Returns the QgsProject singleton instance.
bool hasProperty(int key) const override
Returns true if the collection contains a property with the specified key.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
A store for object properties.
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.
This class manages a set of relations between layers.
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.
bool isInvalidJSON()
Returns whether the text edit widget contains Invalid JSON.
Wraps a label widget to display text.
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)
Returns true if the specified variant should be considered a NULL value.
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.
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
Defines left outer join from our vector layer to some other vector layer.
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)
Tests whether a field is editable for a particular feature.
Represents a vector layer which manages a vector based data sets.
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else.
void beforeRemovingExpressionField(int idx)
Will be emitted, when an expression field is going to be deleted from this vector layer.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QString expressionField(int index) const
Returns the expression used for a given expression field.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE void selectByExpression(const QString &expression, Qgis::SelectBehavior behavior=Qgis::SelectBehavior::SetSelection, QgsExpressionContext *context=nullptr)
Selects matching features using an expression.
void endEditCommand()
Finish edit command and add it to undo/redo stack.
void destroyEditCommand()
Destroy active command and reverts all changes in it.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap(), bool skipDefaultValues=false)
Changes attributes' values for a feature (but does not immediately commit the changes).
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
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.
QVariant defaultValue(int index, const QgsFeature &feature=QgsFeature(), QgsExpressionContext *context=nullptr) const
Returns the calculated default value for the specified field index.
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
QgsEditFormConfig editFormConfig
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)