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)