71int QgsAttributeForm::sFormCounter = 0;
76 , mOwnsMessageBar( true )
78 , mFormNr( sFormCounter++ )
80 , mPreventFeatureRefresh( false )
81 , mIsSettingMultiEditFeatures( false )
82 , mUnsavedMultiEditChanges( false )
83 , mEditCommandMessage( tr(
"Attributes changed" ) )
96 updateContainersVisibility();
104 qDeleteAll( mInterfaces );
131 mInterfaces.append( iface );
147 if ( mUnsavedMultiEditChanges )
150 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
151 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
152 if ( res == QMessageBox::Yes )
157 clearMultiEditMessages();
159 mUnsavedMultiEditChanges =
false;
211 w->setContext( newContext );
217 w->setVisible( relationWidgetsVisible );
224 mSearchButtonBox->setVisible(
false );
229 mSearchButtonBox->setVisible(
false );
234 mSearchButtonBox->setVisible(
false );
238 resetMultiEdit(
false );
240 mSearchButtonBox->setVisible(
false );
244 mSearchButtonBox->setVisible(
true );
250 mSearchButtonBox->setVisible(
false );
258 mSearchButtonBox->setVisible(
false );
267 const auto constMWidgets = mWidgets;
282 QVariant mainValue = eww->
value();
284 additionalFieldValues[index] = value;
285 eww->
setValues( mainValue, additionalFieldValues );
294 mIsSettingFeature =
true;
311 mIsSettingFeature =
false;
312 const auto constMInterfaces = mInterfaces;
315 iface->featureChanged();
331 mIsSettingFeature =
false;
334bool QgsAttributeForm::saveEdits( QString *error )
337 bool changedLayer =
false;
342 bool doUpdate =
false;
362 *error = tr(
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
365 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
366 QVariantList srcVars = QVariantList() << eww->
value();
367 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
371 for (
const QString &fieldName : additionalFields )
375 dstVars << dst.at( idx );
379 Q_ASSERT( dstVars.count() == srcVars.count() );
381 for (
int i = 0; i < dstVars.count(); i++ )
384 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
386 dst[fieldIndexes[i]] = srcVars[i];
396 const auto constMInterfaces = mInterfaces;
399 if ( !iface->acceptChanges( updatedFeature ) )
409 mFeature = updatedFeature;
415 bool res = mLayer->
addFeature( updatedFeature );
434 for (
int i = 0; i < dst.count(); ++i )
437 || !dst.at( i ).isValid()
438 || !fieldIsEditable( i ) )
444 QgsDebugMsgLevel( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
445 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( dst.at( i ) ) ).arg( dst.at( i ).isValid() ), 2 );
446 QgsDebugMsgLevel( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
447 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( src.at( i ) ) ).arg( src.at( i ).isValid() ), 2 );
449 newValues[i] = dst.at( i );
450 oldValues[i] = src.at( i );
457 if ( success && n > 0 )
484QgsFeature QgsAttributeForm::getUpdatedFeature()
const
496 QVariantList dstVars = QVariantList() << featureAttributes.at( eww->
fieldIdx() );
497 QVariantList srcVars = QVariantList() << eww->
value();
498 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
502 for (
const QString &fieldName : additionalFields )
506 dstVars << featureAttributes.at( idx );
510 Q_ASSERT( dstVars.count() == srcVars.count() );
512 for (
int i = 0; i < dstVars.count(); i++ )
514 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
515 featureAttributes[fieldIndexes[i]] = srcVars[i];
520 return updatedFeature;
523void QgsAttributeForm::updateValuesDependencies(
const int originIdx )
525 updateFieldDependencies();
527 updateValuesDependenciesDefaultValues( originIdx );
528 updateValuesDependenciesVirtualFields( originIdx );
531void QgsAttributeForm::updateValuesDependenciesDefaultValues(
const int originIdx )
533 if ( !mDefaultValueDependencies.contains( originIdx ) )
541 QgsFeature updatedFeature = getUpdatedFeature();
544 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
557 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
568void QgsAttributeForm::updateValuesDependenciesVirtualFields(
const int originIdx )
570 if ( !mVirtualFieldsDependencies.contains( originIdx ) )
577 QgsFeature updatedFeature = getUpdatedFeature();
580 const QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
588 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
594 const QVariant value = exp.evaluate( &context );
600void QgsAttributeForm::updateRelatedLayerFields()
603 updateRelatedLayerFieldsDependencies();
605 if ( mRelatedLayerFieldsDependencies.isEmpty() )
612 QgsFeature updatedFeature = getUpdatedFeature();
615 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mRelatedLayerFieldsDependencies;
619 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
625 QVariant value = exp.evaluate( &context );
630void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
635 mUnsavedMultiEditChanges =
false;
639void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
641 clearMultiEditMessages();
642 resetMultiEdit( link == QLatin1String(
"#apply" ) );
645void QgsAttributeForm::filterTriggered()
647 QString filter = createFilterExpression();
653void QgsAttributeForm::searchZoomTo()
655 QString filter = createFilterExpression();
656 if ( filter.isEmpty() )
662void QgsAttributeForm::searchFlash()
664 QString filter = createFilterExpression();
665 if ( filter.isEmpty() )
671void QgsAttributeForm::filterAndTriggered()
673 QString filter = createFilterExpression();
674 if ( filter.isEmpty() )
682void QgsAttributeForm::filterOrTriggered()
684 QString filter = createFilterExpression();
685 if ( filter.isEmpty() )
693void QgsAttributeForm::pushSelectedFeaturesMessage()
699 tr(
"%n matching feature(s) selected",
"matching features", count ),
700 Qgis::MessageLevel::Info );
705 tr(
"No matching features found" ),
706 Qgis::MessageLevel::Info );
714 Qgis::MessageLevel::Warning );
719 QString filter = createFilterExpression();
720 if ( filter.isEmpty() )
724 pushSelectedFeaturesMessage();
729void QgsAttributeForm::searchSetSelection()
734void QgsAttributeForm::searchAddToSelection()
739void QgsAttributeForm::searchRemoveFromSelection()
744void QgsAttributeForm::searchIntersectSelection()
749bool QgsAttributeForm::saveMultiEdits()
753 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
754 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
761 || !fieldIsEditable( wIt.key() ) )
769 newAttributeValues.insert( wIt.key(), w->
currentValue() );
772 if ( newAttributeValues.isEmpty() )
780 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
781 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
782 if ( res != QMessageBox::Ok )
793 const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
796 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
797 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
803 clearMultiEditMessages();
808 mMultiEditMessageBarItem =
new QgsMessageBarItem( tr(
"Attribute changes for multiple features applied." ), Qgis::MessageLevel::Success, -1 );
813 mMultiEditMessageBarItem =
new QgsMessageBarItem( tr(
"Changes could not be applied." ), Qgis::MessageLevel::Warning, 0 );
816 if ( !mButtonBox->isVisible() )
817 mMessageBar->
pushItem( mMultiEditMessageBarItem );
843 wrapper->notifyAboutToSave();
883 success = saveEdits( error );
887 success = saveMultiEdits();
892 mUnsavedMultiEditChanges =
false;
901 mValuesInitialized =
false;
902 const auto constMWidgets = mWidgets;
905 ww->setFeature( mFeature );
907 mValuesInitialized =
true;
913 const auto widgets { findChildren< QgsAttributeFormEditorWidget * >() };
920void QgsAttributeForm::clearMultiEditMessages()
922 if ( mMultiEditUnsavedMessageBarItem )
924 if ( !mButtonBox->isVisible() )
925 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
926 mMultiEditUnsavedMessageBarItem =
nullptr;
928 if ( mMultiEditMessageBarItem )
930 if ( !mButtonBox->isVisible() )
931 mMessageBar->
popWidget( mMultiEditMessageBarItem );
932 mMultiEditMessageBarItem =
nullptr;
936QString QgsAttributeForm::createFilterExpression()
const
942 if ( !filter.isEmpty() )
946 if ( filters.isEmpty() )
949 QString filter = filters.join( QLatin1String(
") AND (" ) ).prepend(
'(' ).append(
')' );
958 if ( mExtraContextScope )
965void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
970 bool signalEmitted =
false;
972 if ( mValuesInitialized )
991 for (
int i = 0; i < additionalFields.count(); i++ )
993 const QString fieldName = additionalFields.at( i );
994 const QVariant value = additionalFieldValues.at( i );
998 signalEmitted =
true;
1000 if ( mValuesInitialized )
1001 updateJoinedFields( *eww );
1007 if ( !mIsSettingMultiEditFeatures )
1009 mUnsavedMultiEditChanges =
true;
1011 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
1012 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
1013 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1014 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
1015 clearMultiEditMessages();
1017 mMultiEditUnsavedMessageBarItem =
new QgsMessageBarItem( msgLabel, Qgis::MessageLevel::Warning );
1018 if ( !mButtonBox->isVisible() )
1019 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
1022 signalEmitted =
true;
1032 updateConstraints( eww );
1035 if ( mValuesInitialized )
1038 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1039 updateValuesDependencies( eww->
fieldIdx() );
1040 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1046 if ( !signalEmitted )
1051 bool attributeHasChanged = !mIsSettingFeature;
1053 attributeHasChanged &= !mIsSettingMultiEditFeatures;
1059void QgsAttributeForm::updateAllConstraints()
1061 const auto constMWidgets = mWidgets;
1066 updateConstraints( eww );
1074 if ( currentFormValuesFeature( ft ) )
1086 updateConstraint( ft, eww );
1089 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
1092 updateConstraint( ft, depsEww );
1100 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
1101 for ( ContainerInformation *info : infos )
1103 info->apply( &context );
1108void QgsAttributeForm::updateContainersVisibility()
1112 const QVector<ContainerInformation *> infos = mContainerVisibilityCollapsedInformation;
1114 for ( ContainerInformation *info : infos )
1116 info->apply( &context );
1120 updateAllConstraints();
1126 if ( mContext.
attributeFormMode() != QgsAttributeEditorContext::Mode::MultiEditMode )
1138 if ( mJoinedFeatures.contains( info ) )
1156void QgsAttributeForm::updateLabels()
1158 if ( ! mLabelDataDefinedProperties.isEmpty() )
1161 if ( currentFormValuesFeature( currentFeature ) )
1165 for (
auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
1167 QLabel *label { it.key() };
1169 const QString value { it->valueAsString( context, QString(), &ok ) };
1170 if ( ok && ! value.isEmpty() )
1172 label->setText( value );
1179bool QgsAttributeForm::currentFormValuesFeature(
QgsFeature &feature )
1192 if ( dst.count() > eww->
fieldIdx() )
1194 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1195 QVariantList srcVars = QVariantList() << eww->
value();
1196 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1200 for (
const QString &fieldName : additionalFields )
1203 fieldIndexes << idx;
1204 dstVars << dst.at( idx );
1208 Q_ASSERT( dstVars.count() == srcVars.count() );
1210 for (
int i = 0; i < dstVars.count(); i++ )
1216 dst[fieldIndexes[i]] = srcVars[i];
1233void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1235 mContainerVisibilityCollapsedInformation.append( info );
1237 const QSet<QString> referencedColumns = info->expression.referencedColumns().unite( info->collapsedExpression.referencedColumns() );
1239 for (
const QString &col : referencedColumns )
1241 mContainerInformationDependency[ col ].append( info );
1245bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1269bool QgsAttributeForm::currentFormValidHardConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1290void QgsAttributeForm::onAttributeAdded(
int idx )
1292 mPreventFeatureRefresh =
false;
1296 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
1304void QgsAttributeForm::onAttributeDeleted(
int idx )
1306 mPreventFeatureRefresh =
false;
1310 attrs.remove( idx );
1318void QgsAttributeForm::onRelatedFeaturesChanged()
1320 updateRelatedLayerFields();
1323void QgsAttributeForm::onUpdatedFields()
1325 mPreventFeatureRefresh =
false;
1342 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
1352void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1360 if ( formEditorWidget )
1366 QList<QgsEditorWidgetWrapper *> wDeps;
1378 if ( name != ewwName )
1385 for (
const QString &colName : referencedColumns )
1387 if ( name == colName )
1389 wDeps.append( eww );
1402 return setupRelationWidgetWrapper( QString(), rel, context );
1415void QgsAttributeForm::preventFeatureRefresh()
1417 mPreventFeatureRefresh =
true;
1448 return mNeedsGeometry;
1451void QgsAttributeForm::synchronizeState()
1453 bool isEditable = ( mFeature.
isValid()
1470 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1471 ww->setEnabled( enabled );
1477 ww->setEnabled( isEditable );
1485 QStringList invalidFields, descriptions;
1486 mValidConstraints = currentFormValidHardConstraints( invalidFields, descriptions );
1490 if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
1492 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 );
1493 mMessageBar->
pushItem( mConstraintsFailMessageBarItem );
1495 else if ( mValidConstraints && mConstraintsFailMessageBarItem )
1497 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1498 mConstraintsFailMessageBarItem =
nullptr;
1501 else if ( mConstraintsFailMessageBarItem )
1503 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1504 mConstraintsFailMessageBarItem =
nullptr;
1507 isEditable = isEditable & mValidConstraints;
1511 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1513 okButton->setEnabled( isEditable );
1516void QgsAttributeForm::init()
1518 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1521 QWidget *formWidget =
nullptr;
1522 mNeedsGeometry =
false;
1524 bool buttonBoxVisible =
true;
1528 buttonBoxVisible = mButtonBox->isVisible();
1530 mButtonBox =
nullptr;
1533 if ( mSearchButtonBox )
1535 delete mSearchButtonBox;
1536 mSearchButtonBox =
nullptr;
1539 qDeleteAll( mWidgets );
1542 while ( QWidget *w = this->findChild<QWidget *>() )
1548 QVBoxLayout *vl =
new QVBoxLayout();
1549 vl->setContentsMargins( 0, 0, 0, 0 );
1551 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1552 vl->addWidget( mMessageBar );
1557 QGridLayout *layout =
new QGridLayout();
1558 QWidget *container =
new QWidget();
1559 container->setLayout( layout );
1560 vl->addWidget( container );
1562 mFormEditorWidgets.clear();
1563 mFormWidgets.clear();
1566 setContentsMargins( 0, 0, 0, 0 );
1575 if ( file && file->open( QFile::ReadOnly ) )
1579 QFileInfo fi( file->fileName() );
1580 loader.setWorkingDirectory( fi.dir() );
1581 formWidget = loader.load( file,
this );
1584 formWidget->setWindowFlags( Qt::Widget );
1585 layout->addWidget( formWidget );
1588 mButtonBox = findChild<QDialogButtonBox *>();
1591 formWidget->installEventFilter(
this );
1603 int columnCount = 1;
1604 bool hasRootFields =
false;
1605 bool addSpacer =
true;
1614 if ( !containerDef )
1619 tabWidget =
nullptr;
1620 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1621 if ( widgetInfo.labelStyle.overrideColor )
1623 if ( widgetInfo.labelStyle.color.isValid() )
1625 widgetInfo.widget->setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1628 if ( widgetInfo.labelStyle.overrideFont )
1630 widgetInfo.widget->setFont( widgetInfo.labelStyle.font );
1632 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1644 layout->addWidget( tabWidget, row, column, 1, 2 );
1648 QWidget *tabPage =
new QWidget( tabWidget );
1650 tabWidget->addTab( tabPage, widgDef->name() );
1651 tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
1655 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1657 QGridLayout *tabPageLayout =
new QGridLayout();
1658 tabPage->setLayout( tabPageLayout );
1660 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1661 tabPageLayout->addWidget( widgetInfo.widget );
1666 hasRootFields =
true;
1667 tabWidget =
nullptr;
1668 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1671 if ( widgetInfo.showLabel )
1673 if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
1675 collapsibleGroupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1678 if ( widgetInfo.labelStyle.overrideFont )
1680 collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
1683 collapsibleGroupBox->setTitle( widgetInfo.labelText );
1686 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1687 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
1688 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1690 QVBoxLayout *
c =
new QVBoxLayout();
1691 c->addWidget( collapsibleGroupBox );
1692 layout->addLayout(
c, row, column, 1, 2 );
1700 hasRootFields =
true;
1701 tabWidget =
nullptr;
1702 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1703 QLabel *label =
new QLabel( widgetInfo.labelText );
1705 if ( widgetInfo.labelStyle.overrideColor )
1707 if ( widgetInfo.labelStyle.color.isValid() )
1709 label->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1713 if ( widgetInfo.labelStyle.overrideFont )
1715 label->setFont( widgetInfo.labelStyle.font );
1718 label->setToolTip( widgetInfo.toolTip );
1719 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1721 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1724 label->setBuddy( widgetInfo.widget );
1727 if ( widgetInfo.widget
1728 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
1729 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
1730 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
1733 if ( !widgetInfo.showLabel )
1735 QVBoxLayout *
c =
new QVBoxLayout();
1736 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1737 c->addWidget( widgetInfo.widget );
1738 layout->addLayout(
c, row, column, 1, 2 );
1741 else if ( widgetInfo.labelOnTop )
1743 QVBoxLayout *
c =
new QVBoxLayout();
1744 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1745 c->addWidget( label );
1746 c->addWidget( widgetInfo.widget );
1747 layout->addLayout(
c, row, column, 1, 2 );
1752 layout->addWidget( label, row, column++ );
1753 layout->addWidget( widgetInfo.widget, row, column++ );
1757 if ( widgDef->type() == QgsAttributeEditorElement::AttributeEditorType::AeTypeField )
1760 const int fieldIdx = fieldElement->
idx();
1761 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
1763 const QString fieldName { mLayer->
fields().
at( fieldIdx ).
name() };
1767 if ( property.isActive() && !
property.expressionString().isEmpty() )
1769 mLabelDataDefinedProperties[ label ] = property;
1776 if ( column >= columnCount * 2 )
1783 if ( hasRootFields && addSpacer )
1785 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1786 layout->addItem( spacerItem, row, 0 );
1787 layout->setRowStretch( row, 1 );
1790 formWidget = container;
1799 formWidget =
new QWidget(
this );
1800 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1801 formWidget->setLayout( gridLayout );
1807 scrollArea->setWidget( formWidget );
1808 scrollArea->setWidgetResizable(
true );
1809 scrollArea->setFrameShape( QFrame::NoFrame );
1810 scrollArea->setFrameShadow( QFrame::Plain );
1811 scrollArea->setFocusProxy(
this );
1812 layout->addWidget( scrollArea );
1816 layout->addWidget( formWidget );
1831 QString labelText = fieldName;
1832 labelText.replace(
'&', QLatin1String(
"&&" ) );
1836 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1842 QLabel *label =
new QLabel( labelText );
1844 QSvgWidget *i =
new QSvgWidget();
1845 i->setFixedSize( 18, 18 );
1850 if ( property.isActive() && ! property.expressionString().isEmpty() )
1852 mLabelDataDefinedProperties[ label ] = property;
1858 QWidget *w =
nullptr;
1863 mFormEditorWidgets.insert( idx, formWidget );
1864 mFormWidgets.append( formWidget );
1867 label->setBuddy( eww->
widget() );
1871 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() ) ) );
1880 addWidgetWrapper( eww );
1881 mIconMap[eww->
widget()] = i;
1886 gridLayout->addWidget( label, row++, 0, 1, 2 );
1887 gridLayout->addWidget( w, row++, 0, 1, 2 );
1888 gridLayout->addWidget( i, row++, 0, 1, 2 );
1892 gridLayout->addWidget( label, row, 0 );
1893 gridLayout->addWidget( w, row, 1 );
1894 gridLayout->addWidget( i, row++, 2 );
1908 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1909 collapsibleGroupBoxLayout->addWidget( formWidget );
1910 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1912 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
1914 mWidgets.append( rww );
1915 mFormWidgets.append( formWidget );
1920 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1921 gridLayout->addItem( spacerItem, row, 0 );
1922 gridLayout->setRowStretch( row, 1 );
1927 updateFieldDependencies();
1931 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1932 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1933 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1935 mButtonBox->setVisible( buttonBoxVisible );
1937 if ( !mSearchButtonBox )
1939 mSearchButtonBox =
new QWidget();
1940 QHBoxLayout *boxLayout =
new QHBoxLayout();
1941 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1942 mSearchButtonBox->setLayout( boxLayout );
1943 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1945 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
1947 boxLayout->addWidget( clearButton );
1948 boxLayout->addStretch( 1 );
1950 QPushButton *flashButton =
new QPushButton();
1951 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1952 flashButton->setText( tr(
"&Flash Features" ) );
1953 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1954 boxLayout->addWidget( flashButton );
1956 QPushButton *openAttributeTableButton =
new QPushButton();
1957 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1958 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
1959 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
1960 connect( openAttributeTableButton, &QToolButton::clicked,
this, [ = ]
1964 boxLayout->addWidget( openAttributeTableButton );
1966 QPushButton *zoomButton =
new QPushButton();
1967 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1968 zoomButton->setText( tr(
"&Zoom to Features" ) );
1969 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
1970 boxLayout->addWidget( zoomButton );
1972 QToolButton *selectButton =
new QToolButton();
1973 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1974 selectButton->setText( tr(
"&Select Features" ) );
1976 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1977 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1978 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
1979 QMenu *selectMenu =
new QMenu( selectButton );
1980 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
1982 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
1983 selectMenu->addAction( selectAction );
1984 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
1986 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
1987 selectMenu->addAction( addSelectAction );
1988 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
1990 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
1991 selectMenu->addAction( deselectAction );
1992 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
1994 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
1995 selectMenu->addAction( filterSelectAction );
1996 selectButton->setMenu( selectMenu );
1997 boxLayout->addWidget( selectButton );
2001 QToolButton *filterButton =
new QToolButton();
2002 filterButton->setText( tr(
"Filter Features" ) );
2003 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
2004 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2005 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
2006 QMenu *filterMenu =
new QMenu( filterButton );
2007 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
2008 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
2009 filterMenu->addAction( filterAndAction );
2010 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
2011 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
2012 filterMenu->addAction( filterOrAction );
2013 filterButton->setMenu( filterMenu );
2014 boxLayout->addWidget( filterButton );
2018 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
2020 closeButton->setShortcut( Qt::Key_Escape );
2021 boxLayout->addWidget( closeButton );
2024 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2042 const auto constMInterfaces = mInterfaces;
2053 QApplication::restoreOverrideCursor();
2056void QgsAttributeForm::cleanPython()
2058 if ( !mPyFormVarName.isNull() )
2060 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
2065void QgsAttributeForm::initPython()
2082 if ( !initFilePath.isEmpty() )
2086 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2089 QTextStream inf( inputFile );
2090#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2091 inf.setCodec(
"UTF-8" );
2093 initCode = inf.readAll();
2098 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
2109 if ( initCode.isEmpty() )
2111 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
2122 if ( !initCode.isEmpty() )
2128 tr(
"Python macro could not be run due to missing permissions." ),
2129 Qgis::MessageLevel::Warning );
2136 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
2138 static int sFormId = 0;
2139 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
2141 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
2142 .arg( mPyFormVarName )
2143 .arg( ( quint64 )
this );
2147 QgsDebugMsg( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
2150 if ( numArgs == QLatin1String(
"3" ) )
2158 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 ) );
2161 QString expr = QString(
"%1(%2)" )
2162 .arg( mLayer->editFormInit() )
2163 .arg( mPyFormVarName );
2164 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2174 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 ) );
2182 WidgetInfo newWidgetInfo;
2184 newWidgetInfo.labelStyle = widgetDef->
labelStyle();
2186 switch ( widgetDef->
type() )
2198 mWidgets.append( actionWrapper );
2199 newWidgetInfo.widget = actionWrapper->
widget();
2200 newWidgetInfo.showLabel =
false;
2213 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2219 mFormEditorWidgets.insert( fldIdx, formWidget );
2220 mFormWidgets.append( formWidget );
2224 newWidgetInfo.widget = formWidget;
2225 addWidgetWrapper( eww );
2227 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2228 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
2233 newWidgetInfo.labelText.replace(
'&', QLatin1String(
"&&" ) );
2234 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
2235 newWidgetInfo.showLabel = widgetDef->
showLabel();
2256 mWidgets.append( rww );
2257 mFormWidgets.append( formWidget );
2259 newWidgetInfo.widget = formWidget;
2260 newWidgetInfo.showLabel = relDef->
showLabel();
2261 newWidgetInfo.labelText = relDef->
label();
2262 if ( newWidgetInfo.labelText.isEmpty() )
2264 newWidgetInfo.labelOnTop =
true;
2276 if ( columnCount <= 0 )
2280 QWidget *myContainer =
nullptr;
2284 widgetName = QStringLiteral(
"QGroupBox" );
2287 groupBox->setTitle( container->
name() );
2288 if ( newWidgetInfo.labelStyle.overrideColor )
2290 if ( newWidgetInfo.labelStyle.color.isValid() )
2292 groupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2295 if ( newWidgetInfo.labelStyle.overrideFont )
2297 groupBox->setFont( newWidgetInfo.labelStyle.font );
2300 myContainer = groupBox;
2301 newWidgetInfo.widget = myContainer;
2306 myContainer =
new QWidget();
2310 scrollArea->setWidget( myContainer );
2311 scrollArea->setWidgetResizable(
true );
2312 scrollArea->setFrameShape( QFrame::NoFrame );
2313 widgetName = QStringLiteral(
"QScrollArea QWidget" );
2315 newWidgetInfo.widget = scrollArea;
2320 QString style {QStringLiteral(
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
2321 newWidgetInfo.widget->setStyleSheet( style );
2324 QGridLayout *gbLayout =
new QGridLayout();
2325 myContainer->setLayout( gbLayout );
2329 bool addSpacer =
true;
2331 const QList<QgsAttributeEditorElement *> children = container->
children();
2335 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2346 if ( widgetInfo.labelText.isNull() || ! widgetInfo.showLabel )
2348 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2353 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2355 if ( widgetInfo.labelStyle.overrideColor )
2357 if ( widgetInfo.labelStyle.color.isValid() )
2359 mypLabel->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2363 if ( widgetInfo.labelStyle.overrideFont )
2365 mypLabel->setFont( widgetInfo.labelStyle.font );
2373 const int fldIdx = fieldDef->
idx();
2374 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2376 const QString fieldName { fields.
at( fldIdx ).
name() };
2380 if ( property.isActive() && !
property.expressionString().isEmpty() )
2382 mLabelDataDefinedProperties[ mypLabel ] = property;
2388 mypLabel->setToolTip( widgetInfo.toolTip );
2389 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2391 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2394 mypLabel->setBuddy( widgetInfo.widget );
2396 if ( widgetInfo.labelOnTop )
2398 QVBoxLayout *
c =
new QVBoxLayout();
2399 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2400 c->layout()->addWidget( mypLabel );
2401 c->layout()->addWidget( widgetInfo.widget );
2402 gbLayout->addLayout(
c, row, column, 1, 2 );
2407 gbLayout->addWidget( mypLabel, row, column++ );
2408 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2412 if ( column >= columnCount * 2 )
2418 if ( widgetInfo.widget
2419 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2420 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2421 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2425 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
2431 QWidget *spacer =
new QWidget();
2432 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2433 gbLayout->addWidget( spacer, ++row, 0 );
2434 gbLayout->setRowStretch( row, 1 );
2437 newWidgetInfo.labelText = QString();
2438 newWidgetInfo.labelOnTop =
true;
2439 newWidgetInfo.showLabel = widgetDef->
showLabel();
2452 mWidgets.append( qmlWrapper );
2454 newWidgetInfo.widget = qmlWrapper->
widget();
2455 newWidgetInfo.labelText = elementDef->
name();
2456 newWidgetInfo.labelOnTop =
true;
2457 newWidgetInfo.showLabel = widgetDef->
showLabel();
2469 mWidgets.append( htmlWrapper );
2471 newWidgetInfo.widget = htmlWrapper->
widget();
2472 newWidgetInfo.labelText = elementDef->
name();
2473 newWidgetInfo.labelOnTop =
true;
2474 newWidgetInfo.showLabel = widgetDef->
showLabel();
2480 QgsDebugMsg( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
2484 return newWidgetInfo;
2505 mWidgets.append( eww );
2508void QgsAttributeForm::createWrappers()
2510 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2511 const QList<QgsField> fields = mLayer->
fields().
toList();
2513 const auto constMyWidgets = myWidgets;
2514 for ( QWidget *myWidget : constMyWidgets )
2517 QVariant vRel = myWidget->property(
"qgisRelation" );
2518 if ( vRel.isValid() )
2528 mWidgets.append( rww );
2533 const auto constFields = fields;
2536 if (
field.
name() == myWidget->objectName() )
2541 addWidgetWrapper( eww );
2548void QgsAttributeForm::afterWidgetInit()
2550 bool isFirstEww =
true;
2552 const auto constMWidgets = mWidgets;
2561 setFocusProxy( eww->
widget() );
2571 if ( relationWidgetWrapper )
2584 if ( e->type() == QEvent::KeyPress )
2586 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
2587 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
2599 QSet< int > &mixedValueFields,
2600 QHash< int, QVariant > &fieldSharedValues )
const
2602 mixedValueFields.clear();
2603 fieldSharedValues.clear();
2609 for (
int i = 0; i < mLayer->
fields().count(); ++i )
2611 if ( mixedValueFields.contains( i ) )
2616 fieldSharedValues[i] = f.
attribute( i );
2620 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
2622 fieldSharedValues.remove( i );
2623 mixedValueFields.insert( i );
2629 if ( mixedValueFields.count() == mLayer->
fields().
count() )
2638void QgsAttributeForm::layerSelectionChanged()
2651 resetMultiEdit(
true );
2658 mIsSettingMultiEditFeatures =
true;
2659 mMultiEditFeatureIds = fids;
2661 if ( fids.isEmpty() )
2664 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2665 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2667 wIt.value()->initialize( QVariant() );
2669 mIsSettingMultiEditFeatures =
false;
2676 QSet< int > mixedValueFields;
2677 QHash< int, QVariant > fieldSharedValues;
2678 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2685 const auto constMixedValueFields = mixedValueFields;
2686 for (
int fieldIndex : std::as_const( mixedValueFields ) )
2690 const QStringList additionalFields = w->editorWidget()->additionalFields();
2691 QVariantList additionalFieldValues;
2692 for (
const QString &additionalField : additionalFields )
2693 additionalFieldValues << firstFeature.
attribute( additionalField );
2694 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
2697 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2698 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2703 const QStringList additionalFields = w->editorWidget()->additionalFields();
2704 for (
const QString &additionalField : additionalFields )
2707 if ( constMixedValueFields.contains( index ) )
2714 QVariantList additionalFieldValues;
2717 for (
const QString &additionalField : additionalFields )
2718 additionalFieldValues << firstFeature.
attribute( additionalField );
2719 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
2723 for (
const QString &additionalField : additionalFields )
2726 Q_ASSERT( fieldSharedValues.contains( index ) );
2727 additionalFieldValues << fieldSharedValues.value( index );
2729 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
2734 setMultiEditFeatureIdsRelations( fids );
2736 mIsSettingMultiEditFeatures =
false;
2741 if ( mOwnsMessageBar )
2743 mOwnsMessageBar =
false;
2744 mMessageBar = messageBar;
2754 QStringList filters;
2757 QString filter = widget->currentFilterExpression();
2758 if ( !filter.isNull() )
2759 filters <<
'(' + filter +
')';
2762 return filters.join( QLatin1String(
" AND " ) );
2767 mExtraContextScope.reset( extraScope );
2773 const bool newVisibility = expression.evaluate( expressionContext ).toBool();
2775 if ( expression.isValid() && ! expression.hasEvalError() && newVisibility != isVisible )
2783 widget->setVisible( newVisibility );
2786 isVisible = newVisibility;
2789 const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
2791 if ( collapsedExpression.isValid() && ! collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
2796 collapsibleGroupBox->
setCollapsed( newCollapsedState );
2797 isCollapsed = newCollapsedState;
2811 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
2814 const QString hint = tr(
"No feature joined" );
2815 const auto constInfos = infos;
2818 if ( !info->isDynamicFormEnabled() )
2823 mJoinedFeatures[info] = joinFeature;
2825 if ( info->hasSubset() )
2829 const auto constSubsetNames = subsetNames;
2830 for (
const QString &
field : constSubsetNames )
2832 QString prefixedName = info->prefixedFieldName(
field );
2834 QString hintText = hint;
2850 QString prefixedName = info->prefixedFieldName(
field );
2852 QString hintText = hint;
2866bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
2871void QgsAttributeForm::updateFieldDependencies()
2873 mDefaultValueDependencies.clear();
2874 mVirtualFieldsDependencies.clear();
2875 mRelatedLayerFieldsDependencies.clear();
2884 updateFieldDependenciesDefaultValue( eww );
2885 updateFieldDependenciesVirtualFields( eww );
2886 updateRelatedLayerFieldsDependencies( eww );
2894 if ( exp.needsGeometry() )
2895 mNeedsGeometry =
true;
2897 const QSet<QString> referencedColumns = exp.referencedColumns();
2898 for (
const QString &referencedColumn : referencedColumns )
2904 for (
const int id : allAttributeIds )
2906 mDefaultValueDependencies.insertMulti(
id, eww );
2911 mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
2919 if ( expressionField.isEmpty() )
2924 if ( exp.needsGeometry() )
2925 mNeedsGeometry =
true;
2927 const QSet<QString> referencedColumns = exp.referencedColumns();
2928 for (
const QString &referencedColumn : referencedColumns )
2933 for (
const int id : allAttributeIds )
2935 mVirtualFieldsDependencies.insertMulti(
id, eww );
2940 mVirtualFieldsDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
2950 if ( expressionField.contains( QStringLiteral(
"relation_aggregate" ) )
2951 || expressionField.contains( QStringLiteral(
"get_features" ) ) )
2952 mRelatedLayerFieldsDependencies.insert( eww );
2956 mRelatedLayerFieldsDependencies.clear();
2961 if ( ! editorWidgetWrapper )
2964 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
2969void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
2974 if ( !relationEditorWidget )
2987 mIconMap[eww->
widget()]->hide();
3001 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
3002 const QString tooltip = tr(
"Join settings do not allow editing" );
3003 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3007 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
3008 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
3009 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3013 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
3014 const QString tooltip = tr(
"Joined layer is not toggled editable" );
3015 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3021void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
3024 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.
Mode attributeFormMode() const
Returns current attributeFormMode.
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.
@ AeTypeHtmlElement
A HTML element.
@ 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.
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.
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.
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.
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)