57 #include <QTextStream>
60 #include <QFormLayout>
61 #include <QGridLayout>
65 #include <QPushButton>
67 #include <QMessageBox>
68 #include <QToolButton>
72 int QgsAttributeForm::sFormCounter = 0;
77 , mOwnsMessageBar( true )
79 , mFormNr( sFormCounter++ )
81 , mPreventFeatureRefresh( false )
82 , mIsSettingMultiEditFeatures( false )
83 , mUnsavedMultiEditChanges( false )
84 , mEditCommandMessage( tr(
"Attributes changed" ) )
97 updateContainersVisibility();
105 qDeleteAll( mInterfaces );
132 mInterfaces.append( iface );
148 if ( mUnsavedMultiEditChanges )
151 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
152 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
153 if ( res == QMessageBox::Yes )
158 clearMultiEditMessages();
160 mUnsavedMultiEditChanges =
false;
212 w->setContext( newContext );
218 w->setVisible( relationWidgetsVisible );
225 mSearchButtonBox->setVisible(
false );
230 mSearchButtonBox->setVisible(
false );
235 mSearchButtonBox->setVisible(
false );
239 resetMultiEdit(
false );
241 mSearchButtonBox->setVisible(
false );
245 mSearchButtonBox->setVisible(
true );
251 mSearchButtonBox->setVisible(
false );
259 mSearchButtonBox->setVisible(
false );
268 const auto constMWidgets = mWidgets;
283 QVariant mainValue = eww->
value();
285 additionalFieldValues[index] = value;
286 eww->
setValues( mainValue, additionalFieldValues );
295 mIsSettingFeature =
true;
312 mIsSettingFeature =
false;
313 const auto constMInterfaces = mInterfaces;
316 iface->featureChanged();
332 mIsSettingFeature =
false;
335 bool QgsAttributeForm::saveEdits( QString *error )
338 bool changedLayer =
false;
343 bool doUpdate =
false;
363 *error = tr(
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
366 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
367 QVariantList srcVars = QVariantList() << eww->
value();
368 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
372 for (
const QString &fieldName : additionalFields )
376 dstVars << dst.at( idx );
380 Q_ASSERT( dstVars.count() == srcVars.count() );
382 for (
int i = 0; i < dstVars.count(); i++ )
385 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
387 dst[fieldIndexes[i]] = srcVars[i];
397 const auto constMInterfaces = mInterfaces;
400 if ( !iface->acceptChanges( updatedFeature ) )
410 mFeature = updatedFeature;
416 bool res = mLayer->
addFeature( updatedFeature );
435 for (
int i = 0; i < dst.count(); ++i )
438 || !dst.at( i ).isValid()
439 || !fieldIsEditable( i ) )
445 QgsDebugMsgLevel( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
446 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ), 2 );
447 QgsDebugMsgLevel( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
448 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ), 2 );
450 newValues[i] = dst.at( i );
451 oldValues[i] = src.at( i );
458 if ( success && n > 0 )
485 QgsFeature QgsAttributeForm::getUpdatedFeature()
const
497 QVariantList dstVars = QVariantList() << featureAttributes.at( eww->
fieldIdx() );
498 QVariantList srcVars = QVariantList() << eww->
value();
499 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
503 for (
const QString &fieldName : additionalFields )
507 dstVars << featureAttributes.at( idx );
511 Q_ASSERT( dstVars.count() == srcVars.count() );
513 for (
int i = 0; i < dstVars.count(); i++ )
515 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
516 featureAttributes[fieldIndexes[i]] = srcVars[i];
521 return updatedFeature;
524 void QgsAttributeForm::updateValuesDependencies(
const int originIdx )
526 updateFieldDependencies();
528 updateValuesDependenciesDefaultValues( originIdx );
529 updateValuesDependenciesVirtualFields( originIdx );
532 void QgsAttributeForm::updateValuesDependenciesDefaultValues(
const int originIdx )
534 if ( !mDefaultValueDependencies.contains( originIdx ) )
542 QgsFeature updatedFeature = getUpdatedFeature();
545 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
558 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
569 void QgsAttributeForm::updateValuesDependenciesVirtualFields(
const int originIdx )
571 if ( !mVirtualFieldsDependencies.contains( originIdx ) )
578 QgsFeature updatedFeature = getUpdatedFeature();
581 const QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
589 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
595 const QVariant value = exp.evaluate( &context );
601 void QgsAttributeForm::updateRelatedLayerFields()
604 updateRelatedLayerFieldsDependencies();
606 if ( mRelatedLayerFieldsDependencies.isEmpty() )
613 QgsFeature updatedFeature = getUpdatedFeature();
616 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mRelatedLayerFieldsDependencies;
620 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
626 QVariant value = exp.evaluate( &context );
631 void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
636 mUnsavedMultiEditChanges =
false;
640 void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
642 clearMultiEditMessages();
643 resetMultiEdit( link == QLatin1String(
"#apply" ) );
646 void QgsAttributeForm::filterTriggered()
648 QString filter = createFilterExpression();
654 void QgsAttributeForm::searchZoomTo()
656 QString filter = createFilterExpression();
657 if ( filter.isEmpty() )
663 void QgsAttributeForm::searchFlash()
665 QString filter = createFilterExpression();
666 if ( filter.isEmpty() )
672 void QgsAttributeForm::filterAndTriggered()
674 QString filter = createFilterExpression();
675 if ( filter.isEmpty() )
683 void QgsAttributeForm::filterOrTriggered()
685 QString filter = createFilterExpression();
686 if ( filter.isEmpty() )
694 void QgsAttributeForm::pushSelectedFeaturesMessage()
700 tr(
"%n matching feature(s) selected",
"matching features", count ),
701 Qgis::MessageLevel::Info );
706 tr(
"No matching features found" ),
707 Qgis::MessageLevel::Info );
715 Qgis::MessageLevel::Warning );
720 QString filter = createFilterExpression();
721 if ( filter.isEmpty() )
725 pushSelectedFeaturesMessage();
730 void QgsAttributeForm::searchSetSelection()
735 void QgsAttributeForm::searchAddToSelection()
740 void QgsAttributeForm::searchRemoveFromSelection()
745 void QgsAttributeForm::searchIntersectSelection()
750 bool QgsAttributeForm::saveMultiEdits()
754 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
755 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
762 || !fieldIsEditable( wIt.key() ) )
770 newAttributeValues.insert( wIt.key(), w->
currentValue() );
773 if ( newAttributeValues.isEmpty() )
781 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
782 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
783 if ( res != QMessageBox::Ok )
794 const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
797 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
798 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
804 clearMultiEditMessages();
809 mMultiEditMessageBarItem =
new QgsMessageBarItem( tr(
"Attribute changes for multiple features applied." ), Qgis::MessageLevel::Success, -1 );
814 mMultiEditMessageBarItem =
new QgsMessageBarItem( tr(
"Changes could not be applied." ), Qgis::MessageLevel::Warning, 0 );
817 if ( !mButtonBox->isVisible() )
818 mMessageBar->
pushItem( mMultiEditMessageBarItem );
844 wrapper->notifyAboutToSave();
884 success = saveEdits( error );
888 success = saveMultiEdits();
893 mUnsavedMultiEditChanges =
false;
902 mValuesInitialized =
false;
903 const auto constMWidgets = mWidgets;
906 ww->setFeature( mFeature );
908 mValuesInitialized =
true;
914 const auto widgets { findChildren< QgsAttributeFormEditorWidget * >() };
921 void QgsAttributeForm::clearMultiEditMessages()
923 if ( mMultiEditUnsavedMessageBarItem )
925 if ( !mButtonBox->isVisible() )
926 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
927 mMultiEditUnsavedMessageBarItem =
nullptr;
929 if ( mMultiEditMessageBarItem )
931 if ( !mButtonBox->isVisible() )
932 mMessageBar->
popWidget( mMultiEditMessageBarItem );
933 mMultiEditMessageBarItem =
nullptr;
937 QString QgsAttributeForm::createFilterExpression()
const
943 if ( !filter.isEmpty() )
947 if ( filters.isEmpty() )
950 QString filter = filters.join( QLatin1String(
") AND (" ) ).prepend(
'(' ).append(
')' );
959 if ( mExtraContextScope )
966 void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
971 bool signalEmitted =
false;
973 if ( mValuesInitialized )
992 for (
int i = 0; i < additionalFields.count(); i++ )
994 const QString fieldName = additionalFields.at( i );
995 const QVariant value = additionalFieldValues.at( i );
999 signalEmitted =
true;
1001 if ( mValuesInitialized )
1002 updateJoinedFields( *eww );
1008 if ( !mIsSettingMultiEditFeatures )
1010 mUnsavedMultiEditChanges =
true;
1012 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
1013 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
1014 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1015 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
1016 clearMultiEditMessages();
1018 mMultiEditUnsavedMessageBarItem =
new QgsMessageBarItem( msgLabel, Qgis::MessageLevel::Warning );
1019 if ( !mButtonBox->isVisible() )
1020 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
1032 updateConstraints( eww );
1035 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1036 updateValuesDependencies( eww->
fieldIdx() );
1037 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1042 if ( !signalEmitted )
1051 void QgsAttributeForm::updateAllConstraints()
1053 const auto constMWidgets = mWidgets;
1058 updateConstraints( eww );
1066 if ( currentFormValuesFeature( ft ) )
1078 updateConstraint( ft, eww );
1081 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
1084 updateConstraint( ft, depsEww );
1092 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
1093 for ( ContainerInformation *info : infos )
1095 info->apply( &context );
1100 void QgsAttributeForm::updateContainersVisibility()
1104 const QVector<ContainerInformation *> infos = mContainerVisibilityInformation;
1106 for ( ContainerInformation *info : infos )
1108 info->apply( &context );
1112 updateAllConstraints();
1118 if ( mContext.
attributeFormMode() != QgsAttributeEditorContext::Mode::MultiEditMode )
1130 if ( mJoinedFeatures.contains( info ) )
1148 void QgsAttributeForm::updateLabels()
1150 if ( ! mLabelDataDefinedProperties.isEmpty() )
1153 if ( currentFormValuesFeature( currentFeature ) )
1157 for (
auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
1159 QLabel *label { it.key() };
1161 const QString value { it->valueAsString( context, QString(), &ok ) };
1162 if ( ok && ! value.isEmpty() )
1164 label->setText( value );
1171 bool QgsAttributeForm::currentFormValuesFeature(
QgsFeature &feature )
1184 if ( dst.count() > eww->
fieldIdx() )
1186 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1187 QVariantList srcVars = QVariantList() << eww->
value();
1188 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1192 for (
const QString &fieldName : additionalFields )
1195 fieldIndexes << idx;
1196 dstVars << dst.at( idx );
1200 Q_ASSERT( dstVars.count() == srcVars.count() );
1202 for (
int i = 0; i < dstVars.count(); i++ )
1206 if ( ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) || dstVars[i].isNull() != srcVars[i].isNull() ) && srcVars[i].isValid() )
1208 dst[fieldIndexes[i]] = srcVars[i];
1225 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1227 mContainerVisibilityInformation.append( info );
1229 const QSet<QString> referencedColumns = info->expression.referencedColumns();
1231 for (
const QString &col : referencedColumns )
1233 mContainerInformationDependency[ col ].append( info );
1237 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1261 bool QgsAttributeForm::currentFormValidHardConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1282 void QgsAttributeForm::onAttributeAdded(
int idx )
1284 mPreventFeatureRefresh =
false;
1288 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
1296 void QgsAttributeForm::onAttributeDeleted(
int idx )
1298 mPreventFeatureRefresh =
false;
1302 attrs.remove( idx );
1310 void QgsAttributeForm::onRelatedFeaturesChanged()
1312 updateRelatedLayerFields();
1315 void QgsAttributeForm::onUpdatedFields()
1317 mPreventFeatureRefresh =
false;
1334 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
1344 void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1352 if ( formEditorWidget )
1358 QList<QgsEditorWidgetWrapper *> wDeps;
1370 if ( name != ewwName )
1377 for (
const QString &colName : referencedColumns )
1379 if ( name == colName )
1381 wDeps.append( eww );
1394 return setupRelationWidgetWrapper( QString(), rel, context );
1407 void QgsAttributeForm::preventFeatureRefresh()
1409 mPreventFeatureRefresh =
true;
1440 return mNeedsGeometry;
1443 void QgsAttributeForm::synchronizeState()
1445 bool isEditable = ( mFeature.
isValid()
1462 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1463 ww->setEnabled( enabled );
1469 ww->setEnabled( isEditable );
1477 QStringList invalidFields, descriptions;
1478 mValidConstraints = currentFormValidHardConstraints( invalidFields, descriptions );
1482 if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
1484 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 );
1485 mMessageBar->
pushItem( mConstraintsFailMessageBarItem );
1487 else if ( mValidConstraints && mConstraintsFailMessageBarItem )
1489 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1490 mConstraintsFailMessageBarItem =
nullptr;
1493 else if ( mConstraintsFailMessageBarItem )
1495 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1496 mConstraintsFailMessageBarItem =
nullptr;
1499 isEditable = isEditable & mValidConstraints;
1503 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1505 okButton->setEnabled( isEditable );
1508 void QgsAttributeForm::init()
1510 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1513 QWidget *formWidget =
nullptr;
1514 mNeedsGeometry =
false;
1516 bool buttonBoxVisible =
true;
1520 buttonBoxVisible = mButtonBox->isVisible();
1522 mButtonBox =
nullptr;
1525 if ( mSearchButtonBox )
1527 delete mSearchButtonBox;
1528 mSearchButtonBox =
nullptr;
1531 qDeleteAll( mWidgets );
1534 while ( QWidget *w = this->findChild<QWidget *>() )
1540 QVBoxLayout *vl =
new QVBoxLayout();
1541 vl->setContentsMargins( 0, 0, 0, 0 );
1543 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1544 vl->addWidget( mMessageBar );
1549 QGridLayout *layout =
new QGridLayout();
1550 QWidget *container =
new QWidget();
1551 container->setLayout( layout );
1552 vl->addWidget( container );
1554 mFormEditorWidgets.clear();
1555 mFormWidgets.clear();
1558 setContentsMargins( 0, 0, 0, 0 );
1567 if ( file && file->open( QFile::ReadOnly ) )
1571 QFileInfo fi( file->fileName() );
1572 loader.setWorkingDirectory( fi.dir() );
1573 formWidget = loader.load( file,
this );
1576 formWidget->setWindowFlags( Qt::Widget );
1577 layout->addWidget( formWidget );
1580 mButtonBox = findChild<QDialogButtonBox *>();
1583 formWidget->installEventFilter(
this );
1595 int columnCount = 1;
1596 bool hasRootFields =
false;
1597 bool addSpacer =
true;
1606 if ( !containerDef )
1611 tabWidget =
nullptr;
1612 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1613 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1616 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1625 layout->addWidget( tabWidget, row, column, 1, 2 );
1629 QWidget *tabPage =
new QWidget( tabWidget );
1631 tabWidget->addTab( tabPage, widgDef->name() );
1635 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1637 QGridLayout *tabPageLayout =
new QGridLayout();
1638 tabPage->setLayout( tabPageLayout );
1640 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1641 tabPageLayout->addWidget( widgetInfo.widget );
1646 hasRootFields =
true;
1647 tabWidget =
nullptr;
1648 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1651 if ( widgetInfo.showLabel )
1652 collapsibleGroupBox->setTitle( widgetInfo.labelText );
1654 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1655 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
1656 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1658 QVBoxLayout *
c =
new QVBoxLayout();
1659 c->addWidget( collapsibleGroupBox );
1660 layout->addLayout(
c, row, column, 1, 2 );
1668 hasRootFields =
true;
1669 tabWidget =
nullptr;
1670 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1671 QLabel *label =
new QLabel( widgetInfo.labelText );
1672 label->setToolTip( widgetInfo.toolTip );
1673 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1675 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1678 label->setBuddy( widgetInfo.widget );
1681 if ( widgetInfo.widget
1682 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
1683 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
1684 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
1687 if ( !widgetInfo.showLabel )
1689 QVBoxLayout *
c =
new QVBoxLayout();
1690 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1691 c->addWidget( widgetInfo.widget );
1692 layout->addLayout(
c, row, column, 1, 2 );
1695 else if ( widgetInfo.labelOnTop )
1697 QVBoxLayout *
c =
new QVBoxLayout();
1698 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1699 c->addWidget( label );
1700 c->addWidget( widgetInfo.widget );
1701 layout->addLayout(
c, row, column, 1, 2 );
1706 layout->addWidget( label, row, column++ );
1707 layout->addWidget( widgetInfo.widget, row, column++ );
1711 if ( widgDef->type() == QgsAttributeEditorElement::AttributeEditorType::AeTypeField )
1714 const int fieldIdx = fieldElement->
idx();
1715 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
1717 const QString fieldName { mLayer->
fields().
at( fieldIdx ).
name() };
1721 if ( property.isActive() && !
property.expressionString().isEmpty() )
1723 mLabelDataDefinedProperties[ label ] = property;
1730 if ( column >= columnCount * 2 )
1737 if ( hasRootFields && addSpacer )
1739 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1740 layout->addItem( spacerItem, row, 0 );
1741 layout->setRowStretch( row, 1 );
1744 formWidget = container;
1753 formWidget =
new QWidget(
this );
1754 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1755 formWidget->setLayout( gridLayout );
1761 scrollArea->setWidget( formWidget );
1762 scrollArea->setWidgetResizable(
true );
1763 scrollArea->setFrameShape( QFrame::NoFrame );
1764 scrollArea->setFrameShadow( QFrame::Plain );
1765 scrollArea->setFocusProxy(
this );
1766 layout->addWidget( scrollArea );
1770 layout->addWidget( formWidget );
1785 QString labelText = fieldName;
1786 labelText.replace(
'&', QLatin1String(
"&&" ) );
1790 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1796 QLabel *label =
new QLabel( labelText );
1798 QSvgWidget *i =
new QSvgWidget();
1799 i->setFixedSize( 18, 18 );
1804 if ( property.isActive() && ! property.expressionString().isEmpty() )
1806 mLabelDataDefinedProperties[ label ] = property;
1812 QWidget *w =
nullptr;
1817 mFormEditorWidgets.insert( idx, formWidget );
1818 mFormWidgets.append( formWidget );
1821 label->setBuddy( eww->
widget() );
1825 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() ) ) );
1834 addWidgetWrapper( eww );
1835 mIconMap[eww->
widget()] = i;
1840 gridLayout->addWidget( label, row++, 0, 1, 2 );
1841 gridLayout->addWidget( w, row++, 0, 1, 2 );
1842 gridLayout->addWidget( i, row++, 0, 1, 2 );
1846 gridLayout->addWidget( label, row, 0 );
1847 gridLayout->addWidget( w, row, 1 );
1848 gridLayout->addWidget( i, row++, 2 );
1862 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1863 collapsibleGroupBoxLayout->addWidget( formWidget );
1864 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1866 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
1868 mWidgets.append( rww );
1869 mFormWidgets.append( formWidget );
1874 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1875 gridLayout->addItem( spacerItem, row, 0 );
1876 gridLayout->setRowStretch( row, 1 );
1881 updateFieldDependencies();
1885 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1886 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1887 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1889 mButtonBox->setVisible( buttonBoxVisible );
1891 if ( !mSearchButtonBox )
1893 mSearchButtonBox =
new QWidget();
1894 QHBoxLayout *boxLayout =
new QHBoxLayout();
1895 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1896 mSearchButtonBox->setLayout( boxLayout );
1897 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1899 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
1901 boxLayout->addWidget( clearButton );
1902 boxLayout->addStretch( 1 );
1904 QPushButton *flashButton =
new QPushButton();
1905 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1906 flashButton->setText( tr(
"&Flash Features" ) );
1907 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1908 boxLayout->addWidget( flashButton );
1910 QPushButton *openAttributeTableButton =
new QPushButton();
1911 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1912 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
1913 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
1914 connect( openAttributeTableButton, &QToolButton::clicked,
this, [ = ]
1918 boxLayout->addWidget( openAttributeTableButton );
1920 QPushButton *zoomButton =
new QPushButton();
1921 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1922 zoomButton->setText( tr(
"&Zoom to Features" ) );
1923 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
1924 boxLayout->addWidget( zoomButton );
1926 QToolButton *selectButton =
new QToolButton();
1927 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1928 selectButton->setText( tr(
"&Select Features" ) );
1930 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1931 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1932 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
1933 QMenu *selectMenu =
new QMenu( selectButton );
1934 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
1936 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
1937 selectMenu->addAction( selectAction );
1938 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
1940 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
1941 selectMenu->addAction( addSelectAction );
1942 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
1944 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
1945 selectMenu->addAction( deselectAction );
1946 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
1948 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
1949 selectMenu->addAction( filterSelectAction );
1950 selectButton->setMenu( selectMenu );
1951 boxLayout->addWidget( selectButton );
1955 QToolButton *filterButton =
new QToolButton();
1956 filterButton->setText( tr(
"Filter Features" ) );
1957 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
1958 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1959 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
1960 QMenu *filterMenu =
new QMenu( filterButton );
1961 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
1962 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
1963 filterMenu->addAction( filterAndAction );
1964 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
1965 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
1966 filterMenu->addAction( filterOrAction );
1967 filterButton->setMenu( filterMenu );
1968 boxLayout->addWidget( filterButton );
1972 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
1974 closeButton->setShortcut( Qt::Key_Escape );
1975 boxLayout->addWidget( closeButton );
1978 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1996 const auto constMInterfaces = mInterfaces;
2007 QApplication::restoreOverrideCursor();
2010 void QgsAttributeForm::cleanPython()
2012 if ( !mPyFormVarName.isNull() )
2014 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
2019 void QgsAttributeForm::initPython()
2036 if ( !initFilePath.isEmpty() )
2040 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2043 QTextStream inf( inputFile );
2044 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2045 inf.setCodec(
"UTF-8" );
2047 initCode = inf.readAll();
2052 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
2063 if ( initCode.isEmpty() )
2065 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
2076 if ( !initCode.isEmpty() )
2082 tr(
"Python macro could not be run due to missing permissions." ),
2083 Qgis::MessageLevel::Warning );
2090 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
2092 static int sFormId = 0;
2093 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
2095 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
2096 .arg( mPyFormVarName )
2097 .arg( ( quint64 )
this );
2101 QgsDebugMsg( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
2104 if ( numArgs == QLatin1String(
"3" ) )
2112 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 ) );
2115 QString expr = QString(
"%1(%2)" )
2116 .arg( mLayer->editFormInit() )
2117 .arg( mPyFormVarName );
2118 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2128 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 ) );
2136 WidgetInfo newWidgetInfo;
2138 switch ( widgetDef->
type() )
2150 mWidgets.append( actionWrapper );
2151 newWidgetInfo.widget = actionWrapper->
widget();
2152 newWidgetInfo.showLabel =
false;
2165 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2171 mFormEditorWidgets.insert( fldIdx, formWidget );
2172 mFormWidgets.append( formWidget );
2176 newWidgetInfo.widget = formWidget;
2177 addWidgetWrapper( eww );
2179 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2180 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
2185 newWidgetInfo.labelText.replace(
'&', QLatin1String(
"&&" ) );
2186 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
2187 newWidgetInfo.showLabel = widgetDef->
showLabel();
2208 mWidgets.append( rww );
2209 mFormWidgets.append( formWidget );
2211 newWidgetInfo.widget = formWidget;
2212 newWidgetInfo.showLabel = relDef->
showLabel();
2213 newWidgetInfo.labelText = relDef->
label();
2214 if ( newWidgetInfo.labelText.isEmpty() )
2216 newWidgetInfo.labelOnTop =
true;
2228 if ( columnCount <= 0 )
2232 QWidget *myContainer =
nullptr;
2235 QGroupBox *groupBox =
new QGroupBox( parent );
2236 widgetName = QStringLiteral(
"QGroupBox" );
2238 groupBox->setTitle( container->
name() );
2239 myContainer = groupBox;
2240 newWidgetInfo.widget = myContainer;
2244 myContainer =
new QWidget();
2248 scrollArea->setWidget( myContainer );
2249 scrollArea->setWidgetResizable(
true );
2250 scrollArea->setFrameShape( QFrame::NoFrame );
2251 widgetName = QStringLiteral(
"QScrollArea QWidget" );
2253 newWidgetInfo.widget = scrollArea;
2258 QString style {QStringLiteral(
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
2259 newWidgetInfo.widget->setStyleSheet( style );
2262 QGridLayout *gbLayout =
new QGridLayout();
2263 myContainer->setLayout( gbLayout );
2267 bool addSpacer =
true;
2269 const QList<QgsAttributeEditorElement *> children = container->
children();
2273 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2280 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
2284 if ( widgetInfo.labelText.isNull() || ! widgetInfo.showLabel )
2286 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2291 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2298 const int fldIdx = fieldDef->
idx();
2299 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2301 const QString fieldName { fields.
at( fldIdx ).
name() };
2305 if ( property.isActive() && !
property.expressionString().isEmpty() )
2307 mLabelDataDefinedProperties[ mypLabel ] = property;
2313 mypLabel->setToolTip( widgetInfo.toolTip );
2314 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2316 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2319 mypLabel->setBuddy( widgetInfo.widget );
2321 if ( widgetInfo.labelOnTop )
2323 QVBoxLayout *
c =
new QVBoxLayout();
2324 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2325 c->layout()->addWidget( mypLabel );
2326 c->layout()->addWidget( widgetInfo.widget );
2327 gbLayout->addLayout(
c, row, column, 1, 2 );
2332 gbLayout->addWidget( mypLabel, row, column++ );
2333 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2337 if ( column >= columnCount * 2 )
2343 if ( widgetInfo.widget
2344 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2345 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2346 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2350 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
2356 QWidget *spacer =
new QWidget();
2357 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2358 gbLayout->addWidget( spacer, ++row, 0 );
2359 gbLayout->setRowStretch( row, 1 );
2362 newWidgetInfo.labelText = QString();
2363 newWidgetInfo.labelOnTop =
true;
2364 newWidgetInfo.showLabel = widgetDef->
showLabel();
2377 mWidgets.append( qmlWrapper );
2379 newWidgetInfo.widget = qmlWrapper->
widget();
2380 newWidgetInfo.labelText = elementDef->
name();
2381 newWidgetInfo.labelOnTop =
true;
2382 newWidgetInfo.showLabel = widgetDef->
showLabel();
2394 mWidgets.append( htmlWrapper );
2396 newWidgetInfo.widget = htmlWrapper->
widget();
2397 newWidgetInfo.labelText = elementDef->
name();
2398 newWidgetInfo.labelOnTop =
true;
2399 newWidgetInfo.showLabel = widgetDef->
showLabel();
2405 QgsDebugMsg( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
2409 return newWidgetInfo;
2430 mWidgets.append( eww );
2433 void QgsAttributeForm::createWrappers()
2435 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2436 const QList<QgsField> fields = mLayer->
fields().
toList();
2438 const auto constMyWidgets = myWidgets;
2439 for ( QWidget *myWidget : constMyWidgets )
2442 QVariant vRel = myWidget->property(
"qgisRelation" );
2443 if ( vRel.isValid() )
2453 mWidgets.append( rww );
2458 const auto constFields = fields;
2461 if (
field.
name() == myWidget->objectName() )
2466 addWidgetWrapper( eww );
2473 void QgsAttributeForm::afterWidgetInit()
2475 bool isFirstEww =
true;
2477 const auto constMWidgets = mWidgets;
2486 setFocusProxy( eww->
widget() );
2496 if ( relationWidgetWrapper )
2509 if ( e->type() == QEvent::KeyPress )
2511 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
2512 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
2524 QSet< int > &mixedValueFields,
2525 QHash< int, QVariant > &fieldSharedValues )
const
2527 mixedValueFields.clear();
2528 fieldSharedValues.clear();
2534 for (
int i = 0; i < mLayer->
fields().count(); ++i )
2536 if ( mixedValueFields.contains( i ) )
2541 fieldSharedValues[i] = f.
attribute( i );
2545 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
2547 fieldSharedValues.remove( i );
2548 mixedValueFields.insert( i );
2554 if ( mixedValueFields.count() == mLayer->
fields().
count() )
2563 void QgsAttributeForm::layerSelectionChanged()
2576 resetMultiEdit(
true );
2583 mIsSettingMultiEditFeatures =
true;
2584 mMultiEditFeatureIds = fids;
2586 if ( fids.isEmpty() )
2589 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2590 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2592 wIt.value()->initialize( QVariant() );
2594 mIsSettingMultiEditFeatures =
false;
2601 QSet< int > mixedValueFields;
2602 QHash< int, QVariant > fieldSharedValues;
2603 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2610 const auto constMixedValueFields = mixedValueFields;
2611 for (
int fieldIndex : std::as_const( mixedValueFields ) )
2615 const QStringList additionalFields = w->editorWidget()->additionalFields();
2616 QVariantList additionalFieldValues;
2617 for (
const QString &additionalField : additionalFields )
2618 additionalFieldValues << firstFeature.
attribute( additionalField );
2619 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
2622 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2623 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2628 const QStringList additionalFields = w->editorWidget()->additionalFields();
2629 for (
const QString &additionalField : additionalFields )
2632 if ( constMixedValueFields.contains( index ) )
2639 QVariantList additionalFieldValues;
2642 for (
const QString &additionalField : additionalFields )
2643 additionalFieldValues << firstFeature.
attribute( additionalField );
2644 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
2648 for (
const QString &additionalField : additionalFields )
2651 Q_ASSERT( fieldSharedValues.contains( index ) );
2652 additionalFieldValues << fieldSharedValues.value( index );
2654 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
2659 setMultiEditFeatureIdsRelations( fids );
2661 mIsSettingMultiEditFeatures =
false;
2666 if ( mOwnsMessageBar )
2668 mOwnsMessageBar =
false;
2669 mMessageBar = messageBar;
2679 QStringList filters;
2682 QString filter = widget->currentFilterExpression();
2683 if ( !filter.isNull() )
2684 filters <<
'(' + filter +
')';
2687 return filters.join( QLatin1String(
" AND " ) );
2692 mExtraContextScope.reset( extraScope );
2697 bool newVisibility = expression.evaluate( expressionContext ).toBool();
2699 if ( newVisibility != isVisible )
2707 widget->setVisible( newVisibility );
2710 isVisible = newVisibility;
2723 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
2726 const QString hint = tr(
"No feature joined" );
2727 const auto constInfos = infos;
2730 if ( !info->isDynamicFormEnabled() )
2735 mJoinedFeatures[info] = joinFeature;
2737 if ( info->hasSubset() )
2741 const auto constSubsetNames = subsetNames;
2742 for (
const QString &
field : constSubsetNames )
2744 QString prefixedName = info->prefixedFieldName(
field );
2746 QString hintText = hint;
2762 QString prefixedName = info->prefixedFieldName(
field );
2764 QString hintText = hint;
2778 bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
2783 void QgsAttributeForm::updateFieldDependencies()
2785 mDefaultValueDependencies.clear();
2786 mVirtualFieldsDependencies.clear();
2787 mRelatedLayerFieldsDependencies.clear();
2796 updateFieldDependenciesDefaultValue( eww );
2797 updateFieldDependenciesVirtualFields( eww );
2798 updateRelatedLayerFieldsDependencies( eww );
2805 const QSet<QString> referencedColumns = exp.referencedColumns();
2806 for (
const QString &referencedColumn : referencedColumns )
2812 for (
const int id : allAttributeIds )
2814 mDefaultValueDependencies.insertMulti(
id, eww );
2819 mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
2827 if ( expressionField.isEmpty() )
2831 const QSet<QString> referencedColumns = exp.referencedColumns();
2832 for (
const QString &referencedColumn : referencedColumns )
2837 for (
const int id : allAttributeIds )
2839 mVirtualFieldsDependencies.insertMulti(
id, eww );
2844 mVirtualFieldsDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
2854 if ( expressionField.contains( QStringLiteral(
"relation_aggregate" ) )
2855 || expressionField.contains( QStringLiteral(
"get_features" ) ) )
2856 mRelatedLayerFieldsDependencies.insert( eww );
2860 mRelatedLayerFieldsDependencies.clear();
2865 if ( ! editorWidgetWrapper )
2868 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
2873 void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
2878 if ( !relationEditorWidget )
2891 mIconMap[eww->
widget()]->hide();
2905 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
2906 const QString tooltip = tr(
"Join settings do not allow editing" );
2907 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2911 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
2912 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
2913 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2917 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
2918 const QString tooltip = tr(
"Joined layer is not toggled editable" );
2919 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2925 void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
2928 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 rendered as a group box.
QColor backgroundColor() const
backgroundColor
int columnCount() const
Gets the number of columns in this group.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
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.
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 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.
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.
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)
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
static bool fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature)
Tests whether a field is editable for a particular feature.
Represents a vector layer which manages a vector based 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.
void endEditCommand()
Finish edit command and add it to undo/redo stack.
void destroyEditCommand()
Destroy active command and reverts all changes in it.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
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.
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.
Q_INVOKABLE void selectByExpression(const QString &expression, Qgis::SelectBehavior behavior=Qgis::SelectBehavior::SetSelection)
Selects matching features using an expression.
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)