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 *zoomButton = 
new QPushButton();
 
 1911     zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1912     zoomButton->setText( tr( 
"&Zoom to Features" ) );
 
 1913     connect( zoomButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchZoomTo );
 
 1914     boxLayout->addWidget( zoomButton );
 
 1916     QToolButton *selectButton = 
new QToolButton();
 
 1917     selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1918     selectButton->setText( tr( 
"&Select Features" ) );
 
 1920     selectButton->setPopupMode( QToolButton::MenuButtonPopup );
 
 1921     selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
 
 1922     connect( selectButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchSetSelection );
 
 1923     QMenu *selectMenu = 
new QMenu( selectButton );
 
 1924     QAction *selectAction = 
new QAction( tr( 
"Select Features" ), selectMenu );
 
 1926     connect( selectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchSetSelection );
 
 1927     selectMenu->addAction( selectAction );
 
 1928     QAction *addSelectAction = 
new QAction( tr( 
"Add to Current Selection" ), selectMenu );
 
 1930     connect( addSelectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchAddToSelection );
 
 1931     selectMenu->addAction( addSelectAction );
 
 1932     QAction *deselectAction = 
new QAction( tr( 
"Remove from Current Selection" ), selectMenu );
 
 1934     connect( deselectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchRemoveFromSelection );
 
 1935     selectMenu->addAction( deselectAction );
 
 1936     QAction *filterSelectAction = 
new QAction( tr( 
"Filter Current Selection" ), selectMenu );
 
 1938     connect( filterSelectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchIntersectSelection );
 
 1939     selectMenu->addAction( filterSelectAction );
 
 1940     selectButton->setMenu( selectMenu );
 
 1941     boxLayout->addWidget( selectButton );
 
 1945       QToolButton *filterButton = 
new QToolButton();
 
 1946       filterButton->setText( tr( 
"Filter Features" ) );
 
 1947       filterButton->setPopupMode( QToolButton::MenuButtonPopup );
 
 1948       filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1949       connect( filterButton, &QToolButton::clicked, 
this, &QgsAttributeForm::filterTriggered );
 
 1950       QMenu *filterMenu = 
new QMenu( filterButton );
 
 1951       QAction *filterAndAction = 
new QAction( tr( 
"Filter Within (\"AND\")" ), filterMenu );
 
 1952       connect( filterAndAction, &QAction::triggered, 
this, &QgsAttributeForm::filterAndTriggered );
 
 1953       filterMenu->addAction( filterAndAction );
 
 1954       QAction *filterOrAction = 
new QAction( tr( 
"Extend Filter (\"OR\")" ), filterMenu );
 
 1955       connect( filterOrAction, &QAction::triggered, 
this, &QgsAttributeForm::filterOrTriggered );
 
 1956       filterMenu->addAction( filterOrAction );
 
 1957       filterButton->setMenu( filterMenu );
 
 1958       boxLayout->addWidget( filterButton );
 
 1962       QPushButton *closeButton = 
new QPushButton( tr( 
"Close" ), mSearchButtonBox );
 
 1964       closeButton->setShortcut( Qt::Key_Escape );
 
 1965       boxLayout->addWidget( closeButton );
 
 1968     layout->addWidget( mSearchButtonBox );
 
 1986   const auto constMInterfaces = mInterfaces;
 
 1997   QApplication::restoreOverrideCursor();
 
 2000 void QgsAttributeForm::cleanPython()
 
 2002   if ( !mPyFormVarName.isNull() )
 
 2004     QString expr = QStringLiteral( 
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
 
 2009 void QgsAttributeForm::initPython()
 
 2026         if ( !initFilePath.isEmpty() )
 
 2030           if ( inputFile && inputFile->open( QFile::ReadOnly ) )
 
 2033             QTextStream inf( inputFile );
 
 2034 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 
 2035             inf.setCodec( 
"UTF-8" );
 
 2037             initCode = inf.readAll();
 
 2042             QgsLogger::warning( QStringLiteral( 
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
 
 2053         if ( initCode.isEmpty() )
 
 2055           QgsLogger::warning( QStringLiteral( 
"The python code provided in the dialog is empty!" ) );
 
 2066     if ( !initCode.isEmpty() )
 
 2072                                   tr( 
"Python macro could not be run due to missing permissions." ),
 
 2073                                   Qgis::MessageLevel::Warning );
 
 2080     if ( 
QgsPythonRunner::eval( QStringLiteral( 
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
 
 2082       static int sFormId = 0;
 
 2083       mPyFormVarName = QStringLiteral( 
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
 
 2085       QString form = QStringLiteral( 
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
 
 2086                      .arg( mPyFormVarName )
 
 2087                      .arg( ( quint64 ) 
this );
 
 2091       QgsDebugMsg( QStringLiteral( 
"running featureForm init: %1" ).arg( mPyFormVarName ) );
 
 2094       if ( numArgs == QLatin1String( 
"3" ) )
 
 2102         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 ) );
 
 2105         QString expr = QString( 
"%1(%2)" )
 
 2106                        .arg( mLayer->editFormInit() )
 
 2107                        .arg( mPyFormVarName );
 
 2108         QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr, 
"QgsAttributeFormInterface" );
 
 2118       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 ) );
 
 2126   WidgetInfo newWidgetInfo;
 
 2128   switch ( widgetDef->
type() )
 
 2140       mWidgets.append( actionWrapper );
 
 2141       newWidgetInfo.widget = actionWrapper->
widget();
 
 2142       newWidgetInfo.showLabel = 
false;
 
 2155       if ( fldIdx < fields.
count() && fldIdx >= 0 )
 
 2161         mFormEditorWidgets.insert( fldIdx, formWidget );
 
 2162         mFormWidgets.append( formWidget );
 
 2166         newWidgetInfo.widget = formWidget;
 
 2167         addWidgetWrapper( eww );
 
 2169         newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
 
 2170         newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
 
 2175       newWidgetInfo.labelText.replace( 
'&', QLatin1String( 
"&&" ) ); 
 
 2176       newWidgetInfo.toolTip = QStringLiteral( 
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
 
 2177       newWidgetInfo.showLabel = widgetDef->
showLabel();
 
 2198       mWidgets.append( rww );
 
 2199       mFormWidgets.append( formWidget );
 
 2201       newWidgetInfo.widget = formWidget;
 
 2202       newWidgetInfo.showLabel = relDef->
showLabel();
 
 2203       newWidgetInfo.labelText = relDef->
label();
 
 2204       if ( newWidgetInfo.labelText.isEmpty() )
 
 2206       newWidgetInfo.labelOnTop = 
true;
 
 2218       if ( columnCount <= 0 )
 
 2222       QWidget *myContainer = 
nullptr;
 
 2225         QGroupBox *groupBox = 
new QGroupBox( parent );
 
 2226         widgetName = QStringLiteral( 
"QGroupBox" );
 
 2228           groupBox->setTitle( container->
name() );
 
 2229         myContainer = groupBox;
 
 2230         newWidgetInfo.widget = myContainer;
 
 2234         myContainer = 
new QWidget();
 
 2238         scrollArea->setWidget( myContainer );
 
 2239         scrollArea->setWidgetResizable( 
true );
 
 2240         scrollArea->setFrameShape( QFrame::NoFrame );
 
 2241         widgetName = QStringLiteral( 
"QScrollArea QWidget" );
 
 2243         newWidgetInfo.widget = scrollArea;
 
 2248         QString style {QStringLiteral( 
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
 
 2249         newWidgetInfo.widget->setStyleSheet( style );
 
 2252       QGridLayout *gbLayout = 
new QGridLayout();
 
 2253       myContainer->setLayout( gbLayout );
 
 2258       const QList<QgsAttributeEditorElement *> children = container->
children();
 
 2262         WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
 
 2269             registerContainerInformation( 
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
 
 2273         if ( widgetInfo.labelText.isNull() || ! widgetInfo.showLabel )
 
 2275           gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
 
 2280           QLabel *mypLabel = 
new QLabel( widgetInfo.labelText );
 
 2287             const int fldIdx = fieldDef->
idx();
 
 2288             if ( fldIdx < fields.
count() && fldIdx >= 0 )
 
 2290               const QString fieldName { fields.
at( fldIdx ).
name() };
 
 2294                 if ( property.isActive() && ! 
property.expressionString().isEmpty() )
 
 2296                   mLabelDataDefinedProperties[ mypLabel ] = property;
 
 2302           mypLabel->setToolTip( widgetInfo.toolTip );
 
 2303           if ( columnCount > 1 && !widgetInfo.labelOnTop )
 
 2305             mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
 
 2308           mypLabel->setBuddy( widgetInfo.widget );
 
 2310           if ( widgetInfo.labelOnTop )
 
 2312             QVBoxLayout *
c = 
new QVBoxLayout();
 
 2313             mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
 
 2314             c->layout()->addWidget( mypLabel );
 
 2315             c->layout()->addWidget( widgetInfo.widget );
 
 2316             gbLayout->addLayout( 
c, row, column, 1, 2 );
 
 2321             gbLayout->addWidget( mypLabel, row, column++ );
 
 2322             gbLayout->addWidget( widgetInfo.widget, row, column++ );
 
 2326         if ( column >= columnCount * 2 )
 
 2332       QWidget *spacer = 
new QWidget();
 
 2333       spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
 
 2334       gbLayout->addWidget( spacer, ++row, 0 );
 
 2335       gbLayout->setRowStretch( row, 1 );
 
 2337       newWidgetInfo.labelText = QString();
 
 2338       newWidgetInfo.labelOnTop = 
true;
 
 2339       newWidgetInfo.showLabel = widgetDef->
showLabel();
 
 2352       mWidgets.append( qmlWrapper );
 
 2354       newWidgetInfo.widget = qmlWrapper->
widget();
 
 2355       newWidgetInfo.labelText = elementDef->
name();
 
 2356       newWidgetInfo.labelOnTop = 
true;
 
 2357       newWidgetInfo.showLabel = widgetDef->
showLabel();
 
 2369       mWidgets.append( htmlWrapper );
 
 2371       newWidgetInfo.widget = htmlWrapper->
widget();
 
 2372       newWidgetInfo.labelText = elementDef->
name();
 
 2373       newWidgetInfo.labelOnTop = 
true;
 
 2374       newWidgetInfo.showLabel = widgetDef->
showLabel();
 
 2380       QgsDebugMsg( QStringLiteral( 
"Unknown attribute editor widget type encountered..." ) );
 
 2384   return newWidgetInfo;
 
 2405   mWidgets.append( eww );
 
 2408 void QgsAttributeForm::createWrappers()
 
 2410   QList<QWidget *> myWidgets = findChildren<QWidget *>();
 
 2411   const QList<QgsField> fields = mLayer->
fields().
toList();
 
 2413   const auto constMyWidgets = myWidgets;
 
 2414   for ( QWidget *myWidget : constMyWidgets )
 
 2417     QVariant vRel = myWidget->property( 
"qgisRelation" );
 
 2418     if ( vRel.isValid() )
 
 2428         mWidgets.append( rww );
 
 2433       const auto constFields = fields;
 
 2436         if ( 
field.
name() == myWidget->objectName() )
 
 2441           addWidgetWrapper( eww );
 
 2448 void QgsAttributeForm::afterWidgetInit()
 
 2450   bool isFirstEww = 
true;
 
 2452   const auto constMWidgets = mWidgets;
 
 2461         setFocusProxy( eww->
widget() );
 
 2471       if ( relationWidgetWrapper )
 
 2484   if ( e->type() == QEvent::KeyPress )
 
 2486     QKeyEvent *keyEvent = 
dynamic_cast<QKeyEvent *
>( e );
 
 2487     if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
 
 2499     QSet< int > &mixedValueFields,
 
 2500     QHash< int, QVariant > &fieldSharedValues )
 const 
 2502   mixedValueFields.clear();
 
 2503   fieldSharedValues.clear();
 
 2509     for ( 
int i = 0; i < mLayer->
fields().count(); ++i )
 
 2511       if ( mixedValueFields.contains( i ) )
 
 2516         fieldSharedValues[i] = f.
attribute( i );
 
 2520         if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
 
 2522           fieldSharedValues.remove( i );
 
 2523           mixedValueFields.insert( i );
 
 2529     if ( mixedValueFields.count() == mLayer->
fields().
count() )
 
 2538 void QgsAttributeForm::layerSelectionChanged()
 
 2551       resetMultiEdit( 
true );
 
 2558   mIsSettingMultiEditFeatures = 
true;
 
 2559   mMultiEditFeatureIds = fids;
 
 2561   if ( fids.isEmpty() )
 
 2564     QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
 
 2565     for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
 
 2567       wIt.value()->initialize( QVariant() );
 
 2569     mIsSettingMultiEditFeatures = 
false;
 
 2576   QSet< int > mixedValueFields;
 
 2577   QHash< int, QVariant > fieldSharedValues;
 
 2578   scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
 
 2585   const auto constMixedValueFields = mixedValueFields;
 
 2586   for ( 
int fieldIndex : std::as_const( mixedValueFields ) )
 
 2590       const QStringList additionalFields = w->editorWidget()->additionalFields();
 
 2591       QVariantList additionalFieldValues;
 
 2592       for ( 
const QString &additionalField : additionalFields )
 
 2593         additionalFieldValues << firstFeature.
attribute( additionalField );
 
 2594       w->initialize( firstFeature.
attribute( fieldIndex ), 
true, additionalFieldValues );
 
 2597   QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
 
 2598   for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
 
 2603       const QStringList additionalFields = w->editorWidget()->additionalFields();
 
 2604       for ( 
const QString &additionalField : additionalFields )
 
 2607         if ( constMixedValueFields.contains( index ) )
 
 2614       QVariantList additionalFieldValues;
 
 2617         for ( 
const QString &additionalField : additionalFields )
 
 2618           additionalFieldValues << firstFeature.
attribute( additionalField );
 
 2619         w->initialize( firstFeature.
attribute( sharedValueIt.key() ), 
true, additionalFieldValues );
 
 2623         for ( 
const QString &additionalField : additionalFields )
 
 2626           Q_ASSERT( fieldSharedValues.contains( index ) );
 
 2627           additionalFieldValues << fieldSharedValues.value( index );
 
 2629         w->initialize( sharedValueIt.value(), 
false, additionalFieldValues );
 
 2633   mIsSettingMultiEditFeatures = 
false;
 
 2638   if ( mOwnsMessageBar )
 
 2640   mOwnsMessageBar = 
false;
 
 2641   mMessageBar = messageBar;
 
 2651   QStringList filters;
 
 2654     QString filter = widget->currentFilterExpression();
 
 2655     if ( !filter.isNull() )
 
 2656       filters << 
'(' + filter + 
')';
 
 2659   return filters.join( QLatin1String( 
" AND " ) );
 
 2664   mExtraContextScope.reset( extraScope );
 
 2669   bool newVisibility = expression.evaluate( expressionContext ).toBool();
 
 2671   if ( newVisibility != isVisible )
 
 2679       widget->setVisible( newVisibility );
 
 2682     isVisible = newVisibility;
 
 2695   if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
 
 2698   const QString hint = tr( 
"No feature joined" );
 
 2699   const auto constInfos = infos;
 
 2702     if ( !info->isDynamicFormEnabled() )
 
 2707     mJoinedFeatures[info] = joinFeature;
 
 2709     if ( info->hasSubset() )
 
 2713       const auto constSubsetNames = subsetNames;
 
 2714       for ( 
const QString &
field : constSubsetNames )
 
 2716         QString prefixedName = info->prefixedFieldName( 
field );
 
 2718         QString hintText = hint;
 
 2734         QString prefixedName = info->prefixedFieldName( 
field );
 
 2736         QString hintText = hint;
 
 2750 bool QgsAttributeForm::fieldIsEditable( 
int fieldIndex )
 const 
 2755 void QgsAttributeForm::updateFieldDependencies()
 
 2757   mDefaultValueDependencies.clear();
 
 2758   mVirtualFieldsDependencies.clear();
 
 2759   mRelatedLayerFieldsDependencies.clear();
 
 2768     updateFieldDependenciesDefaultValue( eww );
 
 2769     updateFieldDependenciesVirtualFields( eww );
 
 2770     updateRelatedLayerFieldsDependencies( eww );
 
 2777   const QSet<QString> referencedColumns = exp.referencedColumns();
 
 2778   for ( 
const QString &referencedColumn : referencedColumns )
 
 2784       for ( 
const int id : allAttributeIds )
 
 2786         mDefaultValueDependencies.insertMulti( 
id, eww );
 
 2791       mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
 
 2799   if ( expressionField.isEmpty() )
 
 2803   const QSet<QString> referencedColumns = exp.referencedColumns();
 
 2804   for ( 
const QString &referencedColumn : referencedColumns )
 
 2809       for ( 
const int id : allAttributeIds )
 
 2811         mVirtualFieldsDependencies.insertMulti( 
id, eww );
 
 2816       mVirtualFieldsDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
 
 2826     if ( expressionField.contains( QStringLiteral( 
"relation_aggregate" ) )
 
 2827          || expressionField.contains( QStringLiteral( 
"get_features" ) ) )
 
 2828       mRelatedLayerFieldsDependencies.insert( eww );
 
 2832     mRelatedLayerFieldsDependencies.clear();
 
 2837       if ( ! editorWidgetWrapper )
 
 2840       updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
 
 2851   mIconMap[eww->
widget()]->hide();
 
 2865         const QString file = QStringLiteral( 
"/mIconJoinNotEditable.svg" );
 
 2866         const QString tooltip = tr( 
"Join settings do not allow editing" );
 
 2867         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
 
 2871         const QString file = QStringLiteral( 
"mIconJoinHasNotUpsertOnEdit.svg" );
 
 2872         const QString tooltip = tr( 
"Join settings do not allow upsert on edit" );
 
 2873         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
 
 2877         const QString file = QStringLiteral( 
"/mIconJoinedLayerNotEditable.svg" );
 
 2878         const QString tooltip = tr( 
"Joined layer is not toggled editable" );
 
 2879         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
 
 2885 void QgsAttributeForm::reloadIcon( 
const QString &file, 
const QString &tooltip, QSvgWidget *sw )
 
 2888   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 QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
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)