21 #include "qgsattributeeditorcontainer.h" 
   22 #include "qgsattributeeditorfield.h" 
   23 #include "qgsattributeeditorrelation.h" 
   24 #include "qgsattributeeditorqmlelement.h" 
   25 #include "qgsattributeeditorhtmlelement.h" 
   41 #include "qgssettings.h" 
   55 #include <QTextStream> 
   58 #include <QFormLayout> 
   59 #include <QGridLayout> 
   63 #include <QPushButton> 
   65 #include <QMessageBox> 
   66 #include <QToolButton> 
   69 int QgsAttributeForm::sFormCounter = 0;
 
   74   , mOwnsMessageBar( true )
 
   76   , mFormNr( sFormCounter++ )
 
   78   , mPreventFeatureRefresh( false )
 
   79   , mIsSettingMultiEditFeatures( false )
 
   80   , mUnsavedMultiEditChanges( false )
 
   81   , mEditCommandMessage( tr( 
"Attributes changed" ) )
 
   94   updateContainersVisibility();
 
  102   qDeleteAll( mInterfaces );
 
  129   mInterfaces.append( iface );
 
  145     if ( mUnsavedMultiEditChanges )
 
  148       int res = QMessageBox::question( 
this, tr( 
"Multiedit Attributes" ),
 
  149                                        tr( 
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
 
  150       if ( res == QMessageBox::Yes )
 
  155     clearMultiEditMessages();
 
  157   mUnsavedMultiEditChanges = 
false;
 
  209     w->setContext( newContext );
 
  215     w->setVisible( relationWidgetsVisible );
 
  222       mSearchButtonBox->setVisible( 
false );
 
  227       mSearchButtonBox->setVisible( 
false );
 
  232       mSearchButtonBox->setVisible( 
false );
 
  236       resetMultiEdit( 
false );
 
  238       mSearchButtonBox->setVisible( 
false );
 
  242       mSearchButtonBox->setVisible( 
true );
 
  248       mSearchButtonBox->setVisible( 
false );
 
  256       mSearchButtonBox->setVisible( 
false );
 
  265   const auto constMWidgets = mWidgets;
 
  280         QVariant mainValue = eww->
value();
 
  282         additionalFieldValues[index] = value;
 
  283         eww->
setValues( mainValue, additionalFieldValues );
 
  292   mIsSettingFeature = 
true;
 
  309       mIsSettingFeature = 
false;
 
  310       const auto constMInterfaces = mInterfaces;
 
  313         iface->featureChanged();
 
  329   mIsSettingFeature = 
false;
 
  332 bool QgsAttributeForm::saveEdits( QString *error )
 
  335   bool changedLayer = 
false;
 
  340     bool doUpdate = 
false;
 
  360             *error = tr( 
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
 
  363         QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
 
  364         QVariantList srcVars = QVariantList() << eww->
value();
 
  365         QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
 
  369         for ( 
const QString &fieldName : additionalFields )
 
  373           dstVars << dst.at( idx );
 
  377         Q_ASSERT( dstVars.count() == srcVars.count() );
 
  379         for ( 
int i = 0; i < dstVars.count(); i++ )
 
  382           if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
 
  384             dst[fieldIndexes[i]] = srcVars[i];
 
  394     const auto constMInterfaces = mInterfaces;
 
  397       if ( !iface->acceptChanges( updatedFeature ) )
 
  407         mFeature = updatedFeature;
 
  413         bool res = mLayer->
addFeature( updatedFeature );
 
  432         for ( 
int i = 0; i < dst.count(); ++i )
 
  435                || !dst.at( i ).isValid()                 
 
  436                || !fieldIsEditable( i ) )                
 
  442           QgsDebugMsgLevel( QStringLiteral( 
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
 
  443                             .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ), 2 );
 
  444           QgsDebugMsgLevel( QStringLiteral( 
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
 
  445                             .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ), 2 );
 
  447           newValues[i] = dst.at( i );
 
  448           oldValues[i] = src.at( i );
 
  455         if ( success && n > 0 )
 
  482 bool QgsAttributeForm::updateDefaultValues( 
const int originIdx )
 
  486   updateDefaultValueDependencies();
 
  488   if ( !mDefaultValueDependencies.contains( originIdx ) )
 
  501         QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
 
  502         QVariantList srcVars = QVariantList() << eww->
value();
 
  503         QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
 
  507         for ( 
const QString &fieldName : additionalFields )
 
  511           dstVars << dst.at( idx );
 
  515         Q_ASSERT( dstVars.count() == srcVars.count() );
 
  517         for ( 
int i = 0; i < dstVars.count(); i++ )
 
  520           if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() && fieldIsEditable( fieldIndexes[i] ) )
 
  522             dst[fieldIndexes[i]] = srcVars[i];
 
  530     QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
 
  543         if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
 
  555 void QgsAttributeForm::resetMultiEdit( 
bool promptToSave )
 
  560   mUnsavedMultiEditChanges = 
false;
 
  564 void QgsAttributeForm::multiEditMessageClicked( 
const QString &link )
 
  566   clearMultiEditMessages();
 
  567   resetMultiEdit( link == QLatin1String( 
"#apply" ) );
 
  570 void QgsAttributeForm::filterTriggered()
 
  572   QString filter = createFilterExpression();
 
  578 void QgsAttributeForm::searchZoomTo()
 
  580   QString filter = createFilterExpression();
 
  581   if ( filter.isEmpty() )
 
  587 void QgsAttributeForm::searchFlash()
 
  589   QString filter = createFilterExpression();
 
  590   if ( filter.isEmpty() )
 
  596 void QgsAttributeForm::filterAndTriggered()
 
  598   QString filter = createFilterExpression();
 
  599   if ( filter.isEmpty() )
 
  607 void QgsAttributeForm::filterOrTriggered()
 
  609   QString filter = createFilterExpression();
 
  610   if ( filter.isEmpty() )
 
  618 void QgsAttributeForm::pushSelectedFeaturesMessage()
 
  624                               tr( 
"%n matching feature(s) selected", 
"matching features", count ),
 
  625                               Qgis::MessageLevel::Info );
 
  630                               tr( 
"No matching features found" ),
 
  631                               Qgis::MessageLevel::Info );
 
  639                             Qgis::MessageLevel::Warning );
 
  644   QString filter = createFilterExpression();
 
  645   if ( filter.isEmpty() )
 
  649   pushSelectedFeaturesMessage();
 
  654 void QgsAttributeForm::searchSetSelection()
 
  659 void QgsAttributeForm::searchAddToSelection()
 
  664 void QgsAttributeForm::searchRemoveFromSelection()
 
  669 void QgsAttributeForm::searchIntersectSelection()
 
  674 bool QgsAttributeForm::saveMultiEdits()
 
  678   QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
 
  679   for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
 
  686          || !fieldIsEditable( wIt.key() ) ) 
 
  694     newAttributeValues.insert( wIt.key(), w->
currentValue() );
 
  697   if ( newAttributeValues.isEmpty() )
 
  705   int res = QMessageBox::information( 
this, tr( 
"Multiedit Attributes" ),
 
  706                                       tr( 
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
 
  707   if ( res != QMessageBox::Ok )
 
  718   const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
 
  721     QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
 
  722     for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
 
  728   clearMultiEditMessages();
 
  733     mMultiEditMessageBarItem = 
new QgsMessageBarItem( tr( 
"Attribute changes for multiple features applied." ), Qgis::MessageLevel::Success, -1 );
 
  738     mMultiEditMessageBarItem = 
new QgsMessageBarItem( tr( 
"Changes could not be applied." ), Qgis::MessageLevel::Warning, 0 );
 
  741   if ( !mButtonBox->isVisible() )
 
  742     mMessageBar->
pushItem( mMultiEditMessageBarItem );
 
  768     wrapper->notifyAboutToSave();
 
  808       success = saveEdits( error );
 
  812       success = saveMultiEdits();
 
  817   mUnsavedMultiEditChanges = 
false;
 
  826   mValuesInitialized = 
false;
 
  827   const auto constMWidgets = mWidgets;
 
  830     ww->setFeature( mFeature );
 
  832   mValuesInitialized = 
true;
 
  838   const auto widgets { findChildren<  QgsAttributeFormEditorWidget * >() };
 
  845 void QgsAttributeForm::clearMultiEditMessages()
 
  847   if ( mMultiEditUnsavedMessageBarItem )
 
  849     if ( !mButtonBox->isVisible() )
 
  850       mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
 
  851     mMultiEditUnsavedMessageBarItem = 
nullptr;
 
  853   if ( mMultiEditMessageBarItem )
 
  855     if ( !mButtonBox->isVisible() )
 
  856       mMessageBar->
popWidget( mMultiEditMessageBarItem );
 
  857     mMultiEditMessageBarItem = 
nullptr;
 
  861 QString QgsAttributeForm::createFilterExpression()
 const 
  867     if ( !filter.isEmpty() )
 
  871   if ( filters.isEmpty() )
 
  874   QString filter = filters.join( QLatin1String( 
") AND (" ) ).prepend( 
'(' ).append( 
')' );
 
  883   if ( mExtraContextScope )
 
  890 void QgsAttributeForm::onAttributeChanged( 
const QVariant &value, 
const QVariantList &additionalFieldValues )
 
  895   bool signalEmitted = 
false;
 
  897   if ( mValuesInitialized )
 
  916       for ( 
int i = 0; i < additionalFields.count(); i++ )
 
  918         const QString fieldName = additionalFields.at( i );
 
  919         const QVariant value = additionalFieldValues.at( i );
 
  923       signalEmitted = 
true;
 
  925       updateJoinedFields( *eww );
 
  931       if ( !mIsSettingMultiEditFeatures )
 
  933         mUnsavedMultiEditChanges = 
true;
 
  935         QLabel *msgLabel = 
new QLabel( tr( 
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
 
  936         msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
 
  937         msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
 
  938         connect( msgLabel, &QLabel::linkActivated, 
this, &QgsAttributeForm::multiEditMessageClicked );
 
  939         clearMultiEditMessages();
 
  941         mMultiEditUnsavedMessageBarItem = 
new QgsMessageBarItem( msgLabel, Qgis::MessageLevel::Warning );
 
  942         if ( !mButtonBox->isVisible() )
 
  943           mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
 
  955   updateConstraints( eww );
 
  958   mAlreadyUpdatedFields.append( eww->
fieldIdx() );
 
  959   updateDefaultValues( eww->
fieldIdx() );
 
  960   mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
 
  965   if ( !signalEmitted )
 
  974 void QgsAttributeForm::updateAllConstraints()
 
  976   const auto constMWidgets = mWidgets;
 
  981       updateConstraints( eww );
 
  989   if ( currentFormValuesFeature( ft ) )
 
 1001     updateConstraint( ft, eww );
 
 1004     const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
 
 1007       updateConstraint( ft, depsEww );
 
 1015     const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
 
 1016     for ( ContainerInformation *info : infos )
 
 1018       info->apply( &context );
 
 1023 void QgsAttributeForm::updateContainersVisibility()
 
 1027   const QVector<ContainerInformation *> infos = mContainerVisibilityInformation;
 
 1029   for ( ContainerInformation *info : infos )
 
 1031     info->apply( &context );
 
 1035   updateAllConstraints();
 
 1041   if ( mContext.
attributeFormMode() != QgsAttributeEditorContext::Mode::MultiEditMode )
 
 1053         if ( mJoinedFeatures.contains( info ) )
 
 1071 void QgsAttributeForm::updateLabels()
 
 1073   if ( ! mLabelDataDefinedProperties.isEmpty() )
 
 1076     if ( currentFormValuesFeature( currentFeature ) )
 
 1080       for ( 
auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
 
 1082         QLabel *label { it.key() };
 
 1084         const QString value { it->valueAsString( context, QString(), &ok ) };
 
 1085         if ( ok && ! value.isEmpty() )
 
 1087           label->setText( value );
 
 1094 bool QgsAttributeForm::currentFormValuesFeature( 
QgsFeature &feature )
 
 1107     if ( dst.count() > eww->
fieldIdx() )
 
 1109       QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
 
 1110       QVariantList srcVars = QVariantList() << eww->
value();
 
 1111       QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
 
 1115       for ( 
const QString &fieldName : additionalFields )
 
 1118         fieldIndexes << idx;
 
 1119         dstVars << dst.at( idx );
 
 1123       Q_ASSERT( dstVars.count() == srcVars.count() );
 
 1125       for ( 
int i = 0; i < dstVars.count(); i++ )
 
 1129         if ( ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) || dstVars[i].isNull() != srcVars[i].isNull() ) && srcVars[i].isValid() )
 
 1131           dst[fieldIndexes[i]] = srcVars[i];
 
 1148 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
 
 1150   mContainerVisibilityInformation.append( info );
 
 1152   const QSet<QString> referencedColumns = info->expression.referencedColumns();
 
 1154   for ( 
const QString &col : referencedColumns )
 
 1156     mContainerInformationDependency[ col ].append( info );
 
 1160 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
 
 1184 void QgsAttributeForm::onAttributeAdded( 
int idx )
 
 1186   mPreventFeatureRefresh = 
false;
 
 1190     attrs.insert( idx, QVariant( 
layer()->fields().at( idx ).type() ) );
 
 1198 void QgsAttributeForm::onAttributeDeleted( 
int idx )
 
 1200   mPreventFeatureRefresh = 
false;
 
 1204     attrs.remove( idx );
 
 1212 void QgsAttributeForm::onUpdatedFields()
 
 1214   mPreventFeatureRefresh = 
false;
 
 1231         attrs[i] = QVariant( 
layer()->fields().at( i ).type() );
 
 1241 void QgsAttributeForm::onConstraintStatusChanged( 
const QString &constraint,
 
 1249   if ( formEditorWidget )
 
 1255   QList<QgsEditorWidgetWrapper *> wDeps;
 
 1267       if ( name != ewwName )
 
 1274         for ( 
const QString &colName : referencedColumns )
 
 1276           if ( name == colName )
 
 1278             wDeps.append( eww );
 
 1291   return setupRelationWidgetWrapper( QString(), rel, context );
 
 1297   const QVariantMap config = mLayer->
editFormConfig().widgetConfig( rel.
id() );
 
 1304 void QgsAttributeForm::preventFeatureRefresh()
 
 1306   mPreventFeatureRefresh = 
true;
 
 1335 void QgsAttributeForm::synchronizeState()
 
 1337   bool isEditable = ( mFeature.
isValid()
 
 1353       bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
 
 1354       ww->setEnabled( enabled );
 
 1362     QStringList invalidFields, descriptions;
 
 1363     mValidConstraints = currentFormValidConstraints( invalidFields, descriptions );
 
 1367       if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
 
 1369         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 );
 
 1370         mMessageBar->
pushItem( mConstraintsFailMessageBarItem );
 
 1372       else if ( mValidConstraints && mConstraintsFailMessageBarItem )
 
 1374         mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
 
 1375         mConstraintsFailMessageBarItem = 
nullptr;
 
 1378     else if ( mConstraintsFailMessageBarItem )
 
 1380       mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
 
 1381       mConstraintsFailMessageBarItem = 
nullptr;
 
 1384     isEditable = isEditable & mValidConstraints;
 
 1388   QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
 
 1390     okButton->setEnabled( isEditable );
 
 1393 void QgsAttributeForm::init()
 
 1395   QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
 
 1398   QWidget *formWidget = 
nullptr;
 
 1400   bool buttonBoxVisible = 
true;
 
 1404     buttonBoxVisible = mButtonBox->isVisible();
 
 1406     mButtonBox = 
nullptr;
 
 1409   if ( mSearchButtonBox )
 
 1411     delete mSearchButtonBox;
 
 1412     mSearchButtonBox = 
nullptr;
 
 1415   qDeleteAll( mWidgets );
 
 1418   while ( QWidget *w = this->findChild<QWidget *>() )
 
 1424   QVBoxLayout *vl = 
new QVBoxLayout();
 
 1425   vl->setContentsMargins( 0, 0, 0, 0 );
 
 1427   mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
 
 1428   vl->addWidget( mMessageBar );
 
 1433   QGridLayout *layout = 
new QGridLayout();
 
 1434   QWidget *container = 
new QWidget();
 
 1435   container->setLayout( layout );
 
 1436   vl->addWidget( container );
 
 1438   mFormEditorWidgets.clear();
 
 1439   mFormWidgets.clear();
 
 1442   setContentsMargins( 0, 0, 0, 0 );
 
 1451     if ( file && file->open( QFile::ReadOnly ) )
 
 1455       QFileInfo fi( file->fileName() );
 
 1456       loader.setWorkingDirectory( fi.dir() );
 
 1457       formWidget = loader.load( file, 
this );
 
 1460         formWidget->setWindowFlags( Qt::Widget );
 
 1461         layout->addWidget( formWidget );
 
 1464         mButtonBox = findChild<QDialogButtonBox *>();
 
 1467         formWidget->installEventFilter( 
this );
 
 1475   if ( !formWidget && mLayer->
editFormConfig().layout() == QgsEditFormConfig::TabLayout )
 
 1479     int columnCount = 1;
 
 1480     bool hasRootFields = 
false;
 
 1482     const QList<QgsAttributeEditorElement *> tabs = mLayer->
editFormConfig().tabs();
 
 1484     for ( QgsAttributeEditorElement *widgDef : tabs )
 
 1486       if ( widgDef->type() == QgsAttributeEditorElement::AeTypeContainer )
 
 1488         QgsAttributeEditorContainer *containerDef = 
dynamic_cast<QgsAttributeEditorContainer *
>( widgDef );
 
 1489         if ( !containerDef )
 
 1492         if ( containerDef->isGroupBox() )
 
 1494           tabWidget = 
nullptr;
 
 1495           WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
 
 1496           layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
 
 1497           if ( containerDef->visibilityExpression().enabled() )
 
 1499             registerContainerInformation( 
new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
 
 1508             layout->addWidget( tabWidget, row, column, 1, 2 );
 
 1512           QWidget *tabPage = 
new QWidget( tabWidget );
 
 1514           tabWidget->addTab( tabPage, widgDef->name() );
 
 1516           if ( containerDef->visibilityExpression().enabled() )
 
 1518             registerContainerInformation( 
new ContainerInformation( tabWidget, tabPage, containerDef->visibilityExpression().data() ) );
 
 1520           QGridLayout *tabPageLayout = 
new QGridLayout();
 
 1521           tabPage->setLayout( tabPageLayout );
 
 1523           WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
 
 1524           tabPageLayout->addWidget( widgetInfo.widget );
 
 1527       else if ( widgDef->type() == QgsAttributeEditorElement::AeTypeRelation )
 
 1529         hasRootFields = 
true;
 
 1530         tabWidget = 
nullptr;
 
 1531         WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
 
 1534         if ( widgetInfo.showLabel )
 
 1535           collapsibleGroupBox->setTitle( widgetInfo.labelText );
 
 1537         QVBoxLayout *collapsibleGroupBoxLayout = 
new QVBoxLayout();
 
 1538         collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
 
 1539         collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
 
 1541         QVBoxLayout *
c = 
new QVBoxLayout();
 
 1542         c->addWidget( collapsibleGroupBox );
 
 1543         layout->addLayout( 
c, row, column, 1, 2 );
 
 1548         hasRootFields = 
true;
 
 1549         tabWidget = 
nullptr;
 
 1550         WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
 
 1551         QLabel *label = 
new QLabel( widgetInfo.labelText );
 
 1552         label->setToolTip( widgetInfo.toolTip );
 
 1553         if ( columnCount > 1 && !widgetInfo.labelOnTop )
 
 1555           label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
 
 1558         label->setBuddy( widgetInfo.widget );
 
 1560         if ( !widgetInfo.showLabel )
 
 1562           QVBoxLayout *
c = 
new QVBoxLayout();
 
 1563           label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
 
 1564           c->addWidget( widgetInfo.widget );
 
 1565           layout->addLayout( 
c, row, column, 1, 2 );
 
 1568         else if ( widgetInfo.labelOnTop )
 
 1570           QVBoxLayout *
c = 
new QVBoxLayout();
 
 1571           label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
 
 1572           c->addWidget( label );
 
 1573           c->addWidget( widgetInfo.widget );
 
 1574           layout->addLayout( 
c, row, column, 1, 2 );
 
 1579           layout->addWidget( label, row, column++ );
 
 1580           layout->addWidget( widgetInfo.widget, row, column++ );
 
 1584         if ( widgDef->type() == QgsAttributeEditorElement::AttributeEditorType::AeTypeField )
 
 1586           const QgsAttributeEditorField *fieldElement { 
static_cast<QgsAttributeEditorField *
>( widgDef ) };
 
 1587           const int fieldIdx = fieldElement->idx();
 
 1588           if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
 
 1590             const QString fieldName { mLayer->
fields().
at( fieldIdx ).
name() };
 
 1591             if ( mLayer->
editFormConfig().dataDefinedFieldProperties( fieldName ).hasProperty( QgsEditFormConfig::DataDefinedProperty::Alias ) )
 
 1593               const QgsProperty property { mLayer->
editFormConfig().dataDefinedFieldProperties( fieldName ).property( QgsEditFormConfig::DataDefinedProperty::Alias ) };
 
 1594               if ( property.isActive() && ! 
property.expressionString().isEmpty() )
 
 1596                 mLabelDataDefinedProperties[ label ] = property;
 
 1603       if ( column >= columnCount * 2 )
 
 1610     if ( hasRootFields )
 
 1612       QSpacerItem *spacerItem = 
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
 
 1613       layout->addItem( spacerItem, row, 0 );
 
 1614       layout->setRowStretch( row, 1 );
 
 1617     formWidget = container;
 
 1626     formWidget = 
new QWidget( 
this );
 
 1627     QGridLayout *gridLayout = 
new QGridLayout( formWidget );
 
 1628     formWidget->setLayout( gridLayout );
 
 1634       scrollArea->setWidget( formWidget );
 
 1635       scrollArea->setWidgetResizable( 
true );
 
 1636       scrollArea->setFrameShape( QFrame::NoFrame );
 
 1637       scrollArea->setFrameShadow( QFrame::Plain );
 
 1638       scrollArea->setFocusProxy( 
this );
 
 1639       layout->addWidget( scrollArea );
 
 1643       layout->addWidget( formWidget );
 
 1658       QString labelText = fieldName;
 
 1659       labelText.replace( 
'&', QLatin1String( 
"&&" ) ); 
 
 1663       if ( widgetSetup.
type() == QLatin1String( 
"Hidden" ) )
 
 1669       QLabel *label = 
new QLabel( labelText );
 
 1671       QSvgWidget *i = 
new QSvgWidget();
 
 1672       i->setFixedSize( 18, 18 );
 
 1674       if ( mLayer->
editFormConfig().dataDefinedFieldProperties( fieldName ).hasProperty( QgsEditFormConfig::DataDefinedProperty::Alias ) )
 
 1676         const QgsProperty property { mLayer->
editFormConfig().dataDefinedFieldProperties( fieldName ).property( QgsEditFormConfig::DataDefinedProperty::Alias ) };
 
 1677         if ( property.isActive() && ! property.expressionString().isEmpty() )
 
 1679           mLabelDataDefinedProperties[ label ] = property;
 
 1685       QWidget *w = 
nullptr;
 
 1690         mFormEditorWidgets.insert( idx, formWidget );
 
 1691         mFormWidgets.append( formWidget );
 
 1694         label->setBuddy( eww->
widget() );
 
 1698         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() ) ) );
 
 1707         addWidgetWrapper( eww );
 
 1708         mIconMap[eww->
widget()] = i;
 
 1713         gridLayout->addWidget( label, row++, 0, 1, 2 );
 
 1714         gridLayout->addWidget( w, row++, 0, 1, 2 );
 
 1715         gridLayout->addWidget( i, row++, 0, 1, 2 );
 
 1719         gridLayout->addWidget( label, row, 0 );
 
 1720         gridLayout->addWidget( w, row, 1 );
 
 1721         gridLayout->addWidget( i, row++, 2 );
 
 1735       QVBoxLayout *collapsibleGroupBoxLayout = 
new QVBoxLayout();
 
 1736       collapsibleGroupBoxLayout->addWidget( formWidget );
 
 1737       collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
 
 1739       gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
 
 1741       mWidgets.append( rww );
 
 1742       mFormWidgets.append( formWidget );
 
 1747       QSpacerItem *spacerItem = 
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
 
 1748       gridLayout->addItem( spacerItem, row, 0 );
 
 1749       gridLayout->setRowStretch( row, 1 );
 
 1754   updateDefaultValueDependencies();
 
 1758     mButtonBox = 
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
 
 1759     mButtonBox->setObjectName( QStringLiteral( 
"buttonBox" ) );
 
 1760     layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
 
 1762   mButtonBox->setVisible( buttonBoxVisible );
 
 1764   if ( !mSearchButtonBox )
 
 1766     mSearchButtonBox = 
new QWidget();
 
 1767     QHBoxLayout *boxLayout = 
new QHBoxLayout();
 
 1768     boxLayout->setContentsMargins( 0, 0, 0, 0 );
 
 1769     mSearchButtonBox->setLayout( boxLayout );
 
 1770     mSearchButtonBox->setObjectName( QStringLiteral( 
"searchButtonBox" ) );
 
 1772     QPushButton *clearButton = 
new QPushButton( tr( 
"&Reset Form" ), mSearchButtonBox );
 
 1774     boxLayout->addWidget( clearButton );
 
 1775     boxLayout->addStretch( 1 );
 
 1777     QPushButton *flashButton = 
new QPushButton();
 
 1778     flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1779     flashButton->setText( tr( 
"&Flash Features" ) );
 
 1780     connect( flashButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchFlash );
 
 1781     boxLayout->addWidget( flashButton );
 
 1783     QPushButton *zoomButton = 
new QPushButton();
 
 1784     zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1785     zoomButton->setText( tr( 
"&Zoom to Features" ) );
 
 1786     connect( zoomButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchZoomTo );
 
 1787     boxLayout->addWidget( zoomButton );
 
 1789     QToolButton *selectButton = 
new QToolButton();
 
 1790     selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1791     selectButton->setText( tr( 
"&Select Features" ) );
 
 1793     selectButton->setPopupMode( QToolButton::MenuButtonPopup );
 
 1794     selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
 
 1795     connect( selectButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchSetSelection );
 
 1796     QMenu *selectMenu = 
new QMenu( selectButton );
 
 1797     QAction *selectAction = 
new QAction( tr( 
"Select Features" ), selectMenu );
 
 1799     connect( selectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchSetSelection );
 
 1800     selectMenu->addAction( selectAction );
 
 1801     QAction *addSelectAction = 
new QAction( tr( 
"Add to Current Selection" ), selectMenu );
 
 1803     connect( addSelectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchAddToSelection );
 
 1804     selectMenu->addAction( addSelectAction );
 
 1805     QAction *deselectAction = 
new QAction( tr( 
"Remove from Current Selection" ), selectMenu );
 
 1807     connect( deselectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchRemoveFromSelection );
 
 1808     selectMenu->addAction( deselectAction );
 
 1809     QAction *filterSelectAction = 
new QAction( tr( 
"Filter Current Selection" ), selectMenu );
 
 1811     connect( filterSelectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchIntersectSelection );
 
 1812     selectMenu->addAction( filterSelectAction );
 
 1813     selectButton->setMenu( selectMenu );
 
 1814     boxLayout->addWidget( selectButton );
 
 1818       QToolButton *filterButton = 
new QToolButton();
 
 1819       filterButton->setText( tr( 
"Filter Features" ) );
 
 1820       filterButton->setPopupMode( QToolButton::MenuButtonPopup );
 
 1821       filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
 
 1822       connect( filterButton, &QToolButton::clicked, 
this, &QgsAttributeForm::filterTriggered );
 
 1823       QMenu *filterMenu = 
new QMenu( filterButton );
 
 1824       QAction *filterAndAction = 
new QAction( tr( 
"Filter Within (\"AND\")" ), filterMenu );
 
 1825       connect( filterAndAction, &QAction::triggered, 
this, &QgsAttributeForm::filterAndTriggered );
 
 1826       filterMenu->addAction( filterAndAction );
 
 1827       QAction *filterOrAction = 
new QAction( tr( 
"Extend Filter (\"OR\")" ), filterMenu );
 
 1828       connect( filterOrAction, &QAction::triggered, 
this, &QgsAttributeForm::filterOrTriggered );
 
 1829       filterMenu->addAction( filterOrAction );
 
 1830       filterButton->setMenu( filterMenu );
 
 1831       boxLayout->addWidget( filterButton );
 
 1835       QPushButton *closeButton = 
new QPushButton( tr( 
"Close" ), mSearchButtonBox );
 
 1837       closeButton->setShortcut( Qt::Key_Escape );
 
 1838       boxLayout->addWidget( closeButton );
 
 1841     layout->addWidget( mSearchButtonBox );
 
 1859   const auto constMInterfaces = mInterfaces;
 
 1870   QApplication::restoreOverrideCursor();
 
 1873 void QgsAttributeForm::cleanPython()
 
 1875   if ( !mPyFormVarName.isNull() )
 
 1877     QString expr = QStringLiteral( 
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
 
 1882 void QgsAttributeForm::initPython()
 
 1889        && mLayer->
editFormConfig().initCodeSource() != QgsEditFormConfig::CodeSourceNone )
 
 1898       case QgsEditFormConfig::CodeSourceFile:
 
 1899         if ( !initFilePath.isEmpty() )
 
 1903           if ( inputFile && inputFile->open( QFile::ReadOnly ) )
 
 1906             QTextStream inf( inputFile );
 
 1907             inf.setCodec( 
"UTF-8" );
 
 1908             initCode = inf.readAll();
 
 1913             QgsLogger::warning( QStringLiteral( 
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
 
 1922       case QgsEditFormConfig::CodeSourceDialog:
 
 1924         if ( initCode.isEmpty() )
 
 1926           QgsLogger::warning( QStringLiteral( 
"The python code provided in the dialog is empty!" ) );
 
 1930       case QgsEditFormConfig::CodeSourceEnvironment:
 
 1931       case QgsEditFormConfig::CodeSourceNone:
 
 1937     if ( !initCode.isEmpty() )
 
 1943                                   tr( 
"Python macro could not be run due to missing permissions." ),
 
 1944                                   Qgis::MessageLevel::Warning );
 
 1951     if ( 
QgsPythonRunner::eval( QStringLiteral( 
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
 
 1953       static int sFormId = 0;
 
 1954       mPyFormVarName = QStringLiteral( 
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
 
 1956       QString form = QStringLiteral( 
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
 
 1957                      .arg( mPyFormVarName )
 
 1958                      .arg( ( quint64 ) 
this );
 
 1962       QgsDebugMsg( QStringLiteral( 
"running featureForm init: %1" ).arg( mPyFormVarName ) );
 
 1965       if ( numArgs == QLatin1String( 
"3" ) )
 
 1973         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 ) );
 
 1976         QString expr = QString( 
"%1(%2)" )
 
 1977                        .arg( mLayer->editFormInit() )
 
 1978                        .arg( mPyFormVarName );
 
 1979         QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr, 
"QgsAttributeFormInterface" );
 
 1989       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 ) );
 
 1997   WidgetInfo newWidgetInfo;
 
 1999   switch ( widgetDef->type() )
 
 2001     case QgsAttributeEditorElement::AeTypeField:
 
 2003       const QgsAttributeEditorField *fieldDef = 
dynamic_cast<const QgsAttributeEditorField *
>( widgetDef );
 
 2008       int fldIdx = fields.
lookupField( fieldDef->name() );
 
 2009       if ( fldIdx < fields.
count() && fldIdx >= 0 )
 
 2015         mFormEditorWidgets.insert( fldIdx, formWidget );
 
 2016         mFormWidgets.append( formWidget );
 
 2020         newWidgetInfo.widget = formWidget;
 
 2021         addWidgetWrapper( eww );
 
 2023         newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
 
 2024         newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
 
 2027       newWidgetInfo.labelOnTop = mLayer->
editFormConfig().labelOnTop( fldIdx );
 
 2029       newWidgetInfo.labelText.replace( 
'&', QLatin1String( 
"&&" ) ); 
 
 2030       newWidgetInfo.toolTip = QStringLiteral( 
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
 
 2031       newWidgetInfo.showLabel = widgetDef->showLabel();
 
 2036     case QgsAttributeEditorElement::AeTypeRelation:
 
 2038       const QgsAttributeEditorRelation *relDef = 
static_cast<const QgsAttributeEditorRelation *
>( widgetDef );
 
 2040       QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( relDef->relationWidgetTypeId(), relDef->relation(), context );
 
 2052       mWidgets.append( rww );
 
 2053       mFormWidgets.append( formWidget );
 
 2055       newWidgetInfo.widget = formWidget;
 
 2056       newWidgetInfo.showLabel = relDef->showLabel();
 
 2057       newWidgetInfo.labelText = relDef->label();
 
 2058       if ( newWidgetInfo.labelText.isEmpty() )
 
 2060       newWidgetInfo.labelOnTop = 
true;
 
 2064     case QgsAttributeEditorElement::AeTypeContainer:
 
 2066       const QgsAttributeEditorContainer *container = 
dynamic_cast<const QgsAttributeEditorContainer *
>( widgetDef );
 
 2070       int columnCount = container->columnCount();
 
 2072       if ( columnCount <= 0 )
 
 2076       QWidget *myContainer = 
nullptr;
 
 2077       if ( container->isGroupBox() )
 
 2079         QGroupBox *groupBox = 
new QGroupBox( parent );
 
 2080         widgetName = QStringLiteral( 
"QGroupBox" );
 
 2081         if ( container->showLabel() )
 
 2082           groupBox->setTitle( container->name() );
 
 2083         myContainer = groupBox;
 
 2084         newWidgetInfo.widget = myContainer;
 
 2088         myContainer = 
new QWidget();
 
 2094           scrollArea->setWidget( myContainer );
 
 2095           scrollArea->setWidgetResizable( 
true );
 
 2096           scrollArea->setFrameShape( QFrame::NoFrame );
 
 2097           widgetName = QStringLiteral( 
"QScrollArea QWidget" );
 
 2099           newWidgetInfo.widget = scrollArea;
 
 2103           newWidgetInfo.widget = myContainer;
 
 2104           widgetName = QStringLiteral( 
"QWidget" );
 
 2108       if ( container->backgroundColor().isValid() )
 
 2110         QString style {QStringLiteral( 
"background-color: %1;" ).arg( container->backgroundColor().name() )};
 
 2111         newWidgetInfo.widget->setStyleSheet( style );
 
 2114       QGridLayout *gbLayout = 
new QGridLayout();
 
 2115       myContainer->setLayout( gbLayout );
 
 2120       const QList<QgsAttributeEditorElement *> children = container->children();
 
 2122       for ( QgsAttributeEditorElement *childDef : children )
 
 2124         WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
 
 2126         if ( childDef->type() == QgsAttributeEditorElement::AeTypeContainer )
 
 2128           QgsAttributeEditorContainer *containerDef = 
static_cast<QgsAttributeEditorContainer *
>( childDef );
 
 2129           if ( containerDef->visibilityExpression().enabled() )
 
 2131             registerContainerInformation( 
new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
 
 2135         if ( widgetInfo.labelText.isNull() || ! widgetInfo.showLabel )
 
 2137           gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
 
 2142           QLabel *mypLabel = 
new QLabel( widgetInfo.labelText );
 
 2145           if ( childDef->type() == QgsAttributeEditorElement::AeTypeField )
 
 2147             const QgsAttributeEditorField *fieldDef { 
static_cast<QgsAttributeEditorField *
>( childDef ) };
 
 2149             const int fldIdx = fieldDef->idx();
 
 2150             if ( fldIdx < fields.
count() && fldIdx >= 0 )
 
 2152               const QString fieldName { fields.
at( fldIdx ).
name() };
 
 2153               if ( mLayer->
editFormConfig().dataDefinedFieldProperties( fieldName ).hasProperty( QgsEditFormConfig::DataDefinedProperty::Alias ) )
 
 2155                 const QgsProperty property { mLayer->
editFormConfig().dataDefinedFieldProperties( fieldName ).property( QgsEditFormConfig::DataDefinedProperty::Alias ) };
 
 2156                 if ( property.isActive() && ! 
property.expressionString().isEmpty() )
 
 2158                   mLabelDataDefinedProperties[ mypLabel ] = property;
 
 2164           mypLabel->setToolTip( widgetInfo.toolTip );
 
 2165           if ( columnCount > 1 && !widgetInfo.labelOnTop )
 
 2167             mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
 
 2170           mypLabel->setBuddy( widgetInfo.widget );
 
 2172           if ( widgetInfo.labelOnTop )
 
 2174             QVBoxLayout *
c = 
new QVBoxLayout();
 
 2175             mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
 
 2176             c->layout()->addWidget( mypLabel );
 
 2177             c->layout()->addWidget( widgetInfo.widget );
 
 2178             gbLayout->addLayout( 
c, row, column, 1, 2 );
 
 2183             gbLayout->addWidget( mypLabel, row, column++ );
 
 2184             gbLayout->addWidget( widgetInfo.widget, row, column++ );
 
 2188         if ( column >= columnCount * 2 )
 
 2194       QWidget *spacer = 
new QWidget();
 
 2195       spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
 
 2196       gbLayout->addWidget( spacer, ++row, 0 );
 
 2197       gbLayout->setRowStretch( row, 1 );
 
 2199       newWidgetInfo.labelText = QString();
 
 2200       newWidgetInfo.labelOnTop = 
true;
 
 2201       newWidgetInfo.showLabel = widgetDef->showLabel();
 
 2205     case QgsAttributeEditorElement::AeTypeQmlElement:
 
 2207       const QgsAttributeEditorQmlElement *elementDef = 
static_cast<const QgsAttributeEditorQmlElement *
>( widgetDef );
 
 2210       qmlWrapper->
setQmlCode( elementDef->qmlCode() );
 
 2214       mWidgets.append( qmlWrapper );
 
 2216       newWidgetInfo.widget = qmlWrapper->
widget();
 
 2217       newWidgetInfo.labelText = elementDef->name();
 
 2218       newWidgetInfo.labelOnTop = 
true;
 
 2219       newWidgetInfo.showLabel = widgetDef->showLabel();
 
 2223     case QgsAttributeEditorElement::AeTypeHtmlElement:
 
 2225       const QgsAttributeEditorHtmlElement *elementDef = 
static_cast<const QgsAttributeEditorHtmlElement *
>( widgetDef );
 
 2229       htmlWrapper->
setHtmlCode( elementDef->htmlCode() );
 
 2231       mWidgets.append( htmlWrapper );
 
 2233       newWidgetInfo.widget = htmlWrapper->
widget();
 
 2234       newWidgetInfo.labelText = elementDef->name();
 
 2235       newWidgetInfo.labelOnTop = 
true;
 
 2236       newWidgetInfo.showLabel = widgetDef->showLabel();
 
 2241       QgsDebugMsg( QStringLiteral( 
"Unknown attribute editor widget type encountered..." ) );
 
 2245   return newWidgetInfo;
 
 2266   mWidgets.append( eww );
 
 2269 void QgsAttributeForm::createWrappers()
 
 2271   QList<QWidget *> myWidgets = findChildren<QWidget *>();
 
 2272   const QList<QgsField> fields = mLayer->
fields().
toList();
 
 2274   const auto constMyWidgets = myWidgets;
 
 2275   for ( QWidget *myWidget : constMyWidgets )
 
 2278     QVariant vRel = myWidget->property( 
"qgisRelation" );
 
 2279     if ( vRel.isValid() )
 
 2289         mWidgets.append( rww );
 
 2294       const auto constFields = fields;
 
 2297         if ( 
field.
name() == myWidget->objectName() )
 
 2302           addWidgetWrapper( eww );
 
 2309 void QgsAttributeForm::afterWidgetInit()
 
 2311   bool isFirstEww = 
true;
 
 2313   const auto constMWidgets = mWidgets;
 
 2322         setFocusProxy( eww->
widget() );
 
 2337   if ( e->type() == QEvent::KeyPress )
 
 2339     QKeyEvent *keyEvent = 
dynamic_cast<QKeyEvent *
>( e );
 
 2340     if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
 
 2352     QSet< int > &mixedValueFields,
 
 2353     QHash< int, QVariant > &fieldSharedValues )
 const 
 2355   mixedValueFields.clear();
 
 2356   fieldSharedValues.clear();
 
 2362     for ( 
int i = 0; i < mLayer->
fields().count(); ++i )
 
 2364       if ( mixedValueFields.contains( i ) )
 
 2369         fieldSharedValues[i] = f.
attribute( i );
 
 2373         if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
 
 2375           fieldSharedValues.remove( i );
 
 2376           mixedValueFields.insert( i );
 
 2382     if ( mixedValueFields.count() == mLayer->
fields().
count() )
 
 2391 void QgsAttributeForm::layerSelectionChanged()
 
 2404       resetMultiEdit( 
true );
 
 2411   mIsSettingMultiEditFeatures = 
true;
 
 2412   mMultiEditFeatureIds = fids;
 
 2414   if ( fids.isEmpty() )
 
 2417     QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
 
 2418     for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
 
 2420       wIt.value()->initialize( QVariant() );
 
 2422     mIsSettingMultiEditFeatures = 
false;
 
 2429   QSet< int > mixedValueFields;
 
 2430   QHash< int, QVariant > fieldSharedValues;
 
 2431   scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
 
 2438   const auto constMixedValueFields = mixedValueFields;
 
 2439   for ( 
int fieldIndex : std::as_const( mixedValueFields ) )
 
 2443       const QStringList additionalFields = w->editorWidget()->additionalFields();
 
 2444       QVariantList additionalFieldValues;
 
 2445       for ( 
const QString &additionalField : additionalFields )
 
 2446         additionalFieldValues << firstFeature.
attribute( additionalField );
 
 2447       w->initialize( firstFeature.
attribute( fieldIndex ), 
true, additionalFieldValues );
 
 2450   QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
 
 2451   for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
 
 2456       const QStringList additionalFields = w->editorWidget()->additionalFields();
 
 2457       for ( 
const QString &additionalField : additionalFields )
 
 2460         if ( constMixedValueFields.contains( index ) )
 
 2467       QVariantList additionalFieldValues;
 
 2470         for ( 
const QString &additionalField : additionalFields )
 
 2471           additionalFieldValues << firstFeature.
attribute( additionalField );
 
 2472         w->initialize( firstFeature.
attribute( sharedValueIt.key() ), 
true, additionalFieldValues );
 
 2476         for ( 
const QString &additionalField : additionalFields )
 
 2479           Q_ASSERT( fieldSharedValues.contains( index ) );
 
 2480           additionalFieldValues << fieldSharedValues.value( index );
 
 2482         w->initialize( sharedValueIt.value(), 
false, additionalFieldValues );
 
 2486   mIsSettingMultiEditFeatures = 
false;
 
 2491   if ( mOwnsMessageBar )
 
 2493   mOwnsMessageBar = 
false;
 
 2494   mMessageBar = messageBar;
 
 2504   QStringList filters;
 
 2507     QString filter = widget->currentFilterExpression();
 
 2508     if ( !filter.isNull() )
 
 2509       filters << 
'(' + filter + 
')';
 
 2512   return filters.join( QLatin1String( 
" AND " ) );
 
 2517   mExtraContextScope.reset( extraScope );
 
 2522   bool newVisibility = expression.evaluate( expressionContext ).toBool();
 
 2524   if ( newVisibility != isVisible )
 
 2532       widget->setVisible( newVisibility );
 
 2535     isVisible = newVisibility;
 
 2548   if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
 
 2551   const QString hint = tr( 
"No feature joined" );
 
 2552   const auto constInfos = infos;
 
 2555     if ( !info->isDynamicFormEnabled() )
 
 2560     mJoinedFeatures[info] = joinFeature;
 
 2562     if ( info->hasSubset() )
 
 2566       const auto constSubsetNames = subsetNames;
 
 2567       for ( 
const QString &
field : constSubsetNames )
 
 2569         QString prefixedName = info->prefixedFieldName( 
field );
 
 2571         QString hintText = hint;
 
 2587         QString prefixedName = info->prefixedFieldName( 
field );
 
 2589         QString hintText = hint;
 
 2603 bool QgsAttributeForm::fieldIsEditable( 
int fieldIndex )
 const 
 2608 void QgsAttributeForm::updateDefaultValueDependencies()
 
 2610   mDefaultValueDependencies.clear();
 
 2618       const QSet<QString> referencedColumns = exp.referencedColumns();
 
 2619       for ( 
const QString &referencedColumn : referencedColumns )
 
 2625           for ( 
const int id : allAttributeIds )
 
 2627             mDefaultValueDependencies.insertMulti( 
id, eww );
 
 2632           mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
 
 2645   mIconMap[eww->
widget()]->hide();
 
 2659         const QString file = QStringLiteral( 
"/mIconJoinNotEditable.svg" );
 
 2660         const QString tooltip = tr( 
"Join settings do not allow editing" );
 
 2661         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
 
 2665         const QString file = QStringLiteral( 
"mIconJoinHasNotUpsertOnEdit.svg" );
 
 2666         const QString tooltip = tr( 
"Join settings do not allow upsert on edit" );
 
 2667         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
 
 2671         const QString file = QStringLiteral( 
"/mIconJoinedLayerNotEditable.svg" );
 
 2672         const QString tooltip = tr( 
"Joined layer is not toggled editable" );
 
 2673         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
 
 2679 void QgsAttributeForm::reloadIcon( 
const QString &file, 
const QString &tooltip, QSvgWidget *sw )
 
 2682   sw->setToolTip( tooltip );
 
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 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.
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 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.
QgsRelationManager * relationManager
static QgsProject * instance()
Returns the QgsProject singleton instance.
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.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
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.
void editingStarted()
Emitted when editing on this layer has started.
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.
Q_INVOKABLE void selectByExpression(const QString &expression, QgsVectorLayer::SelectBehavior behavior=QgsVectorLayer::SetSelection)
Selects matching features using an expression.
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.
SelectBehavior
Selection behavior.
@ RemoveFromSelection
Remove from current selection.
@ IntersectSelection
Modify current selection to include only select features which match.
@ AddToSelection
Add selection to current selection.
@ SetSelection
Set selection, removing any existing selection.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
void beforeAddingExpressionField(const QString &fieldName)
Will be emitted, when an expression field is going to be added to this vector layer.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QVariant defaultValue(int index, const QgsFeature &feature=QgsFeature(), QgsExpressionContext *context=nullptr) const
Returns the calculated default value for the specified field index.
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
QgsEditFormConfig editFormConfig
void beforeModifiedCheck() const
Emitted when the layer is checked for modifications. Use for last-minute additions.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsVariantEqual(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether they are equal, two NULL values are always treated a...
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
QMap< int, QVariant > QgsAttributeMap
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugMsgLevel(str, level)