43 #include <QTextStream>    46 #include <QFormLayout>    47 #include <QGridLayout>    51 #include <QPushButton>    53 #include <QMessageBox>    54 #include <QToolButton>    57 int QgsAttributeForm::sFormCounter = 0;
    62   , mOwnsMessageBar( true )
    64   , mFormNr( sFormCounter++ )
    66   , mPreventFeatureRefresh( false )
    67   , mIsSettingMultiEditFeatures( false )
    68   , mUnsavedMultiEditChanges( false )
    69   , mEditCommandMessage( tr( 
"Attributes changed" ) )
    82   updateContainersVisibility();
    88   qDeleteAll( mInterfaces );
   115   mInterfaces.append( iface );
   131     if ( mUnsavedMultiEditChanges )
   134       int res = QMessageBox::question( 
this, tr( 
"Multiedit Attributes" ),
   135                                        tr( 
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
   136       if ( res == QMessageBox::Yes )
   141     clearMultiEditMessages();
   143   mUnsavedMultiEditChanges = 
false;
   191     w->setContext( newContext );
   197     w->setVisible( relationWidgetsVisible );
   204       mSearchButtonBox->setVisible( 
false );
   208       synchronizeEnabledState();
   209       mSearchButtonBox->setVisible( 
false );
   213       resetMultiEdit( 
false );
   214       synchronizeEnabledState();
   215       mSearchButtonBox->setVisible( 
false );
   219       mSearchButtonBox->setVisible( 
true );
   224       mSearchButtonBox->setVisible( 
false );
   230       mSearchButtonBox->setVisible( 
false );
   242     if ( eww && eww->
field().
name() == field )
   252   mIsSettingFeature = 
true;
   263       synchronizeEnabledState();
   267       mIsSettingFeature = 
false;
   286   mIsSettingFeature = 
false;
   289 bool QgsAttributeForm::saveEdits()
   292   bool changedLayer = 
false;
   298     bool doUpdate = 
false;
   313         QVariant dstVar = dst.at( eww->
fieldIdx() );
   314         QVariant srcVar = eww->
value();
   319         bool changed = ( dstVar != srcVar && !dstVar.isNull() && !srcVar.isNull() )
   320                        || ( dstVar.isNull() != srcVar.isNull() );
   321         if ( changed && srcVar.isValid() && fieldIsEditable( eww->
fieldIdx() ) )
   346         bool res = mLayer->
addFeature( updatedFeature );
   365         for ( 
int i = 0; i < dst.count(); ++i )
   368                || !dst.at( i ).isValid()                 
   369                || !fieldIsEditable( i ) )                
   374           QgsDebugMsg( QStringLiteral( 
"Updating field %1" ).arg( i ) );
   375           QgsDebugMsg( QStringLiteral( 
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
   376                        .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ) );
   377           QgsDebugMsg( QStringLiteral( 
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
   378                        .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ) );
   380           newValues[i] = dst.at( i );
   381           oldValues[i] = src.at( i );
   388         if ( success && n > 0 )
   415 void QgsAttributeForm::resetMultiEdit( 
bool promptToSave )
   420   mUnsavedMultiEditChanges = 
false;
   424 void QgsAttributeForm::multiEditMessageClicked( 
const QString &link )
   426   clearMultiEditMessages();
   427   resetMultiEdit( link == QLatin1String( 
"#apply" ) );
   430 void QgsAttributeForm::filterTriggered()
   432   QString filter = createFilterExpression();
   438 void QgsAttributeForm::searchZoomTo()
   440   QString filter = createFilterExpression();
   441   if ( filter.isEmpty() )
   447 void QgsAttributeForm::searchFlash()
   449   QString filter = createFilterExpression();
   450   if ( filter.isEmpty() )
   456 void QgsAttributeForm::filterAndTriggered()
   458   QString filter = createFilterExpression();
   459   if ( filter.isEmpty() )
   467 void QgsAttributeForm::filterOrTriggered()
   469   QString filter = createFilterExpression();
   470   if ( filter.isEmpty() )
   478 void QgsAttributeForm::pushSelectedFeaturesMessage()
   484                               tr( 
"%1 matching %2 selected" ).arg( count )
   485                               .arg( count == 1 ? tr( 
"feature" ) : tr( 
"features" ) ),
   492                               tr( 
"No matching features found" ),
   500   QString filter = createFilterExpression();
   501   if ( filter.isEmpty() )
   505   pushSelectedFeaturesMessage();
   510 void QgsAttributeForm::searchSetSelection()
   515 void QgsAttributeForm::searchAddToSelection()
   520 void QgsAttributeForm::searchRemoveFromSelection()
   525 void QgsAttributeForm::searchIntersectSelection()
   530 bool QgsAttributeForm::saveMultiEdits()
   534   QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
   535   for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
   542          || !fieldIsEditable( wIt.key() ) ) 
   550     newAttributeValues.insert( wIt.key(), w->
currentValue() );
   553   if ( newAttributeValues.isEmpty() )
   561   int res = QMessageBox::information( 
this, tr( 
"Multiedit Attributes" ),
   562                                       tr( 
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
   563   if ( res != QMessageBox::Ok )
   576     QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
   577     for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
   583   clearMultiEditMessages();
   596   if ( !mButtonBox->isVisible() )
   597     mMessageBar->
pushItem( mMultiEditMessageBarItem );
   608     wrapper->notifyAboutToSave();
   646       success = saveEdits();
   650       success = saveMultiEdits();
   655   mUnsavedMultiEditChanges = 
false;
   663   mValuesInitialized = 
false;
   668   mValuesInitialized = 
true;
   680 void QgsAttributeForm::clearMultiEditMessages()
   682   if ( mMultiEditUnsavedMessageBarItem )
   684     if ( !mButtonBox->isVisible() )
   685       mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
   686     mMultiEditUnsavedMessageBarItem = 
nullptr;
   688   if ( mMultiEditMessageBarItem )
   690     if ( !mButtonBox->isVisible() )
   691       mMessageBar->
popWidget( mMultiEditMessageBarItem );
   692     mMultiEditMessageBarItem = 
nullptr;
   696 QString QgsAttributeForm::createFilterExpression()
 const   701     QString filter = w->currentFilterExpression();
   702     if ( !filter.isEmpty() )
   706   if ( filters.isEmpty() )
   709   QString filter = filters.join( QStringLiteral( 
") AND (" ) ).prepend( 
'(' ).append( 
')' );
   714 void QgsAttributeForm::onAttributeChanged( 
const QVariant &value )
   719   bool signalEmitted = 
false;
   721   if ( mValuesInitialized )
   735       signalEmitted = 
true;
   737       updateJoinedFields( *eww );
   743       if ( !mIsSettingMultiEditFeatures )
   745         mUnsavedMultiEditChanges = 
true;
   747         QLabel *msgLabel = 
new QLabel( tr( 
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
   748         msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
   749         msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
   750         connect( msgLabel, &QLabel::linkActivated, 
this, &QgsAttributeForm::multiEditMessageClicked );
   751         clearMultiEditMessages();
   754         if ( !mButtonBox->isVisible() )
   755           mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
   765   updateConstraints( eww );
   767   if ( !signalEmitted )
   776 void QgsAttributeForm::updateAllConstraints()
   782       updateConstraints( eww );
   790   if ( currentFormFeature( ft ) )
   802     updateConstraint( ft, eww );
   805     const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
   808       updateConstraint( ft, depsEww );
   811     synchronizeEnabledState();
   818     const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
   819     for ( ContainerInformation *info : infos )
   821       info->apply( &mExpressionContext );
   826 void QgsAttributeForm::updateContainersVisibility()
   830   const QVector<ContainerInformation *> infos = mContainerVisibilityInformation;
   832   for ( ContainerInformation *info : infos )
   834     info->apply( &mExpressionContext );
   838   updateAllConstraints();
   852       if ( mJoinedFeatures.contains( info ) )
   882     if ( dst.count() > eww->
fieldIdx() )
   884       QVariant dstVar = dst.at( eww->
fieldIdx() );
   885       QVariant srcVar = eww->
value();
   888       if ( ( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() )
   904 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
   906   mContainerVisibilityInformation.append( info );
   908   const QSet<QString> referencedColumns = info->expression.referencedColumns();
   910   for ( 
const QString &col : referencedColumns )
   912     mContainerInformationDependency[ col ].append( info );
   916 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
   940 void QgsAttributeForm::onAttributeAdded( 
int idx )
   942   mPreventFeatureRefresh = 
false;
   946     attrs.insert( idx, QVariant( 
layer()->fields().at( idx ).type() ) );
   954 void QgsAttributeForm::onAttributeDeleted( 
int idx )
   956   mPreventFeatureRefresh = 
false;
   968 void QgsAttributeForm::onUpdatedFields()
   970   mPreventFeatureRefresh = 
false;
   982           attrs[i].convert( 
layer()->fields().at( i ).type() );
   987         attrs[i] = QVariant( 
layer()->fields().at( i ).type() );
   997 void QgsAttributeForm::onConstraintStatusChanged( 
const QString &constraint,
  1005   if ( formEditorWidget )
  1011   QList<QgsEditorWidgetWrapper *> wDeps;
  1023       if ( name != ewwName )
  1030         for ( 
const QString &colName : referencedColumns )
  1032           if ( name == colName )
  1034             wDeps.append( eww );
  1055 void QgsAttributeForm::preventFeatureRefresh()
  1057   mPreventFeatureRefresh = 
true;
  1074 void QgsAttributeForm::synchronizeEnabledState()
  1076   bool isEditable = ( mFeature.
isValid()
  1092       bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
  1093       ww->setEnabled( enabled );
  1101     QStringList invalidFields, descriptions;
  1102     bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
  1104     isEditable = isEditable & validConstraint;
  1108   QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
  1110     okButton->setEnabled( isEditable );
  1113 void QgsAttributeForm::init()
  1115   QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
  1118   QWidget *formWidget = 
nullptr;
  1120   bool buttonBoxVisible = 
true;
  1124     buttonBoxVisible = mButtonBox->isVisible();
  1126     mButtonBox = 
nullptr;
  1129   if ( mSearchButtonBox )
  1131     delete mSearchButtonBox;
  1132     mSearchButtonBox = 
nullptr;
  1135   qDeleteAll( mWidgets );
  1138   while ( QWidget *w = this->findChild<QWidget *>() )
  1144   QVBoxLayout *vl = 
new QVBoxLayout();
  1146   vl->setContentsMargins( 0, 0, 0, 0 );
  1148   mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
  1149   vl->addWidget( mMessageBar );
  1154   QGridLayout *layout = 
new QGridLayout();
  1155   QWidget *container = 
new QWidget();
  1156   container->setLayout( layout );
  1157   vl->addWidget( container );
  1159   mFormEditorWidgets.clear();
  1160   mFormWidgets.clear();
  1163   setContentsMargins( 0, 0, 0, 0 );
  1172     if ( file && file->open( QFile::ReadOnly ) )
  1176       QFileInfo fi( file->fileName() );
  1177       loader.setWorkingDirectory( fi.dir() );
  1178       formWidget = loader.load( file, 
this );
  1181         formWidget->setWindowFlags( Qt::Widget );
  1182         layout->addWidget( formWidget );
  1185         mButtonBox = findChild<QDialogButtonBox *>();
  1188         formWidget->installEventFilter( 
this );
  1200     int columnCount = 1;
  1209         if ( !containerDef )
  1214           tabWidget = 
nullptr;
  1215           WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
  1216           layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
  1219             registerContainerInformation( 
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
  1228             layout->addWidget( tabWidget, row, column, 1, 2 );
  1232           QWidget *tabPage = 
new QWidget( tabWidget );
  1234           tabWidget->addTab( tabPage, widgDef->name() );
  1238             registerContainerInformation( 
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
  1240           QGridLayout *tabPageLayout = 
new QGridLayout();
  1241           tabPage->setLayout( tabPageLayout );
  1243           WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
  1244           tabPageLayout->addWidget( widgetInfo.widget );
  1249         tabWidget = 
nullptr;
  1250         WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
  1251         QLabel *label = 
new QLabel( widgetInfo.labelText );
  1252         label->setToolTip( widgetInfo.toolTip );
  1253         if ( columnCount > 1 && !widgetInfo.labelOnTop )
  1255           label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
  1258         label->setBuddy( widgetInfo.widget );
  1260         if ( !widgetInfo.showLabel )
  1262           QVBoxLayout *
c = 
new QVBoxLayout();
  1263           label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
  1264           c->addWidget( widgetInfo.widget );
  1265           layout->addLayout( c, row, column, 1, 2 );
  1268         else if ( widgetInfo.labelOnTop )
  1270           QVBoxLayout *
c = 
new QVBoxLayout();
  1271           label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
  1272           c->addWidget( label );
  1273           c->addWidget( widgetInfo.widget );
  1274           layout->addLayout( c, row, column, 1, 2 );
  1279           layout->addWidget( label, row, column++ );
  1280           layout->addWidget( widgetInfo.widget, row, column++ );
  1284       if ( column >= columnCount * 2 )
  1290     formWidget = container;
  1299     formWidget = 
new QWidget( 
this );
  1300     QGridLayout *gridLayout = 
new QGridLayout( formWidget );
  1301     formWidget->setLayout( gridLayout );
  1307       scrollArea->setWidget( formWidget );
  1308       scrollArea->setWidgetResizable( 
true );
  1309       scrollArea->setFrameShape( QFrame::NoFrame );
  1310       scrollArea->setFrameShadow( QFrame::Plain );
  1311       scrollArea->setFocusProxy( 
this );
  1312       layout->addWidget( scrollArea );
  1316       layout->addWidget( formWidget );
  1323     for ( 
const QgsField &field : fields )
  1331       QString labelText = fieldName;
  1332       labelText.replace( 
'&', QStringLiteral( 
"&&" ) ); 
  1336       if ( widgetSetup.
type() == QLatin1String( 
"Hidden" ) )
  1342       QLabel *l = 
new QLabel( labelText );
  1343       l->setToolTip( QStringLiteral( 
"<b>%1</b><p>%2</p>" ).arg( fieldName, field.comment() ) );
  1344       QSvgWidget *i = 
new QSvgWidget();
  1345       i->setFixedSize( 18, 18 );
  1349       QWidget *w = 
nullptr;
  1354         mFormEditorWidgets.insert( idx, formWidget );
  1355         mFormWidgets.append( formWidget );
  1358         l->setBuddy( eww->
widget() );
  1362         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() ) ) );
  1367         w->setObjectName( field.name() );
  1371         addWidgetWrapper( eww );
  1372         mIconMap[eww->
widget()] = i;
  1377         gridLayout->addWidget( l, row++, 0, 1, 2 );
  1378         gridLayout->addWidget( w, row++, 0, 1, 2 );
  1379         gridLayout->addWidget( i, row++, 0, 1, 2 );
  1383         gridLayout->addWidget( l, row, 0 );
  1384         gridLayout->addWidget( w, row, 1 );
  1385         gridLayout->addWidget( i, row++, 2 );
  1395       gridLayout->addWidget( formWidget, row++, 0, 1, 2 );
  1397       mWidgets.append( rww );
  1398       mFormWidgets.append( formWidget );
  1403       QSpacerItem *spacerItem = 
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
  1404       gridLayout->addItem( spacerItem, row, 0 );
  1405       gridLayout->setRowStretch( row, 1 );
  1412     mButtonBox = 
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
  1413     mButtonBox->setObjectName( QStringLiteral( 
"buttonBox" ) );
  1414     layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
  1416   mButtonBox->setVisible( buttonBoxVisible );
  1418   if ( !mSearchButtonBox )
  1420     mSearchButtonBox = 
new QWidget();
  1421     QHBoxLayout *boxLayout = 
new QHBoxLayout();
  1422     boxLayout->setMargin( 0 );
  1423     boxLayout->setContentsMargins( 0, 0, 0, 0 );
  1424     mSearchButtonBox->setLayout( boxLayout );
  1425     mSearchButtonBox->setObjectName( QStringLiteral( 
"searchButtonBox" ) );
  1427     QPushButton *clearButton = 
new QPushButton( tr( 
"&Reset form" ), mSearchButtonBox );
  1429     boxLayout->addWidget( clearButton );
  1430     boxLayout->addStretch( 1 );
  1432     QPushButton *flashButton = 
new QPushButton();
  1433     flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
  1434     flashButton->setText( tr( 
"&Flash features" ) );
  1435     connect( flashButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchFlash );
  1436     boxLayout->addWidget( flashButton );
  1438     QPushButton *zoomButton = 
new QPushButton();
  1439     zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
  1440     zoomButton->setText( tr( 
"&Zoom to features" ) );
  1441     connect( zoomButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchZoomTo );
  1442     boxLayout->addWidget( zoomButton );
  1444     QToolButton *selectButton = 
new QToolButton();
  1445     selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
  1446     selectButton->setText( tr( 
"&Select features" ) );
  1448     selectButton->setPopupMode( QToolButton::MenuButtonPopup );
  1449     selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
  1450     connect( selectButton, &QToolButton::clicked, 
this, &QgsAttributeForm::searchSetSelection );
  1451     QMenu *selectMenu = 
new QMenu( selectButton );
  1452     QAction *selectAction = 
new QAction( tr( 
"Select Features" ), selectMenu );
  1454     connect( selectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchSetSelection );
  1455     selectMenu->addAction( selectAction );
  1456     QAction *addSelectAction = 
new QAction( tr( 
"Add to Current Selection" ), selectMenu );
  1458     connect( addSelectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchAddToSelection );
  1459     selectMenu->addAction( addSelectAction );
  1460     QAction *deselectAction = 
new QAction( tr( 
"Remove from Current Selection" ), selectMenu );
  1462     connect( deselectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchRemoveFromSelection );
  1463     selectMenu->addAction( deselectAction );
  1464     QAction *filterSelectAction = 
new QAction( tr( 
"Filter Current Selection" ), selectMenu );
  1466     connect( filterSelectAction, &QAction::triggered, 
this, &QgsAttributeForm::searchIntersectSelection );
  1467     selectMenu->addAction( filterSelectAction );
  1468     selectButton->setMenu( selectMenu );
  1469     boxLayout->addWidget( selectButton );
  1473       QToolButton *filterButton = 
new QToolButton();
  1474       filterButton->setText( tr( 
"Filter features" ) );
  1475       filterButton->setPopupMode( QToolButton::MenuButtonPopup );
  1476       filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
  1477       connect( filterButton, &QToolButton::clicked, 
this, &QgsAttributeForm::filterTriggered );
  1478       QMenu *filterMenu = 
new QMenu( filterButton );
  1479       QAction *filterAndAction = 
new QAction( tr( 
"Filter Within (\"AND\")" ), filterMenu );
  1480       connect( filterAndAction, &QAction::triggered, 
this, &QgsAttributeForm::filterAndTriggered );
  1481       filterMenu->addAction( filterAndAction );
  1482       QAction *filterOrAction = 
new QAction( tr( 
"Extend Filter (\"OR\")" ), filterMenu );
  1483       connect( filterOrAction, &QAction::triggered, 
this, &QgsAttributeForm::filterOrTriggered );
  1484       filterMenu->addAction( filterOrAction );
  1485       filterButton->setMenu( filterMenu );
  1486       boxLayout->addWidget( filterButton );
  1490       QPushButton *closeButton = 
new QPushButton( tr( 
"Close" ), mSearchButtonBox );
  1492       closeButton->setShortcut( Qt::Key_Escape );
  1493       boxLayout->addWidget( closeButton );
  1496     layout->addWidget( mSearchButtonBox );
  1524   QApplication::restoreOverrideCursor();
  1527 void QgsAttributeForm::cleanPython()
  1529   if ( !mPyFormVarName.isNull() )
  1531     QString expr = QStringLiteral( 
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
  1536 void QgsAttributeForm::initPython()
  1553         if ( ! initFilePath.isEmpty() )
  1555           QFile inputFile( initFilePath );
  1557           if ( inputFile.open( QFile::ReadOnly ) )
  1560             QTextStream inf( &inputFile );
  1561             initCode = inf.readAll();
  1566             QgsLogger::warning( QStringLiteral( 
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
  1577         if ( initCode.isEmpty() )
  1579           QgsLogger::warning( QStringLiteral( 
"The python code provided in the dialog is empty!" ) );
  1591     if ( ! initCode.isEmpty() )
  1600     if ( 
QgsPythonRunner::eval( QStringLiteral( 
"len(inspect.getargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
  1602       static int sFormId = 0;
  1603       mPyFormVarName = QStringLiteral( 
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
  1605       QString form = QStringLiteral( 
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
  1606                      .arg( mPyFormVarName )
  1607                      .arg( ( quint64 ) 
this );
  1611       QgsDebugMsg( QStringLiteral( 
"running featureForm init: %1" ).arg( mPyFormVarName ) );
  1614       if ( numArgs == QLatin1String( 
"3" ) )
  1622         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 ) );
  1625         QString expr = QString( 
"%1(%2)" )
  1626                        .arg( mLayer->editFormInit() )
  1627                        .arg( mPyFormVarName );
  1628         QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr, 
"QgsAttributeFormInterface" );
  1638       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 ) );
  1646   WidgetInfo newWidgetInfo;
  1648   switch ( widgetDef->
type() )
  1658       if ( fldIdx < fields.
count() && fldIdx >= 0 )
  1664         mFormEditorWidgets.insert( fldIdx, formWidget );
  1665         mFormWidgets.append( formWidget );
  1669         newWidgetInfo.widget = formWidget;
  1670         addWidgetWrapper( eww );
  1672         newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
  1673         newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
  1678       newWidgetInfo.labelText.replace( 
'&', QStringLiteral( 
"&&" ) ); 
  1679       newWidgetInfo.toolTip = QStringLiteral( 
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
  1680       newWidgetInfo.showLabel = widgetDef->
showLabel();
  1698       mWidgets.append( rww );
  1699       mFormWidgets.append( formWidget );
  1701       newWidgetInfo.widget = formWidget;
  1702       newWidgetInfo.labelText = QString();
  1703       newWidgetInfo.labelOnTop = 
true;
  1715       if ( columnCount <= 0 )
  1718       QWidget *myContainer = 
nullptr;
  1721         QGroupBox *groupBox = 
new QGroupBox( parent );
  1723           groupBox->setTitle( container->
name() );
  1724         myContainer = groupBox;
  1725         newWidgetInfo.widget = myContainer;
  1729         myContainer = 
new QWidget();
  1735           scrollArea->setWidget( myContainer );
  1736           scrollArea->setWidgetResizable( 
true );
  1737           scrollArea->setFrameShape( QFrame::NoFrame );
  1739           newWidgetInfo.widget = scrollArea;
  1743           newWidgetInfo.widget = myContainer;
  1747       QGridLayout *gbLayout = 
new QGridLayout();
  1748       myContainer->setLayout( gbLayout );
  1753       const QList<QgsAttributeEditorElement *> children = container->
children();
  1757         WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
  1764             registerContainerInformation( 
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
  1768         if ( widgetInfo.labelText.isNull() )
  1770           gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
  1775           QLabel *mypLabel = 
new QLabel( widgetInfo.labelText );
  1776           mypLabel->setToolTip( widgetInfo.toolTip );
  1777           if ( columnCount > 1 && !widgetInfo.labelOnTop )
  1779             mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
  1782           mypLabel->setBuddy( widgetInfo.widget );
  1784           if ( widgetInfo.labelOnTop )
  1786             QVBoxLayout *
c = 
new QVBoxLayout();
  1787             mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
  1788             c->layout()->addWidget( mypLabel );
  1789             c->layout()->addWidget( widgetInfo.widget );
  1790             gbLayout->addLayout( c, row, column, 1, 2 );
  1795             gbLayout->addWidget( mypLabel, row, column++ );
  1796             gbLayout->addWidget( widgetInfo.widget, row, column++ );
  1800         if ( column >= columnCount * 2 )
  1806       QWidget *spacer = 
new QWidget();
  1807       spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
  1808       gbLayout->addWidget( spacer, ++row, 0 );
  1809       gbLayout->setRowStretch( row, 1 );
  1811       newWidgetInfo.labelText = QString();
  1812       newWidgetInfo.labelOnTop = 
true;
  1826       mWidgets.append( qmlWrapper );
  1828       newWidgetInfo.widget = qmlWrapper->
widget();
  1829       newWidgetInfo.labelText = elementDef->
name();
  1830       newWidgetInfo.labelOnTop = 
true;
  1831       newWidgetInfo.showLabel = widgetDef->
showLabel();
  1836       QgsDebugMsg( QStringLiteral( 
"Unknown attribute editor widget type encountered..." ) );
  1840   newWidgetInfo.showLabel = widgetDef->
showLabel();
  1842   return newWidgetInfo;
  1861   mWidgets.append( eww );
  1864 void QgsAttributeForm::createWrappers()
  1866   QList<QWidget *> myWidgets = findChildren<QWidget *>();
  1867   const QList<QgsField> fields = mLayer->
fields().
toList();
  1869   Q_FOREACH ( QWidget *myWidget, myWidgets )
  1872     QVariant vRel = myWidget->property( 
"qgisRelation" );
  1873     if ( vRel.isValid() )
  1883         mWidgets.append( rww );
  1888       Q_FOREACH ( 
const QgsField &field, fields )
  1890         if ( field.
name() == myWidget->objectName() )
  1895           addWidgetWrapper( eww );
  1902 void QgsAttributeForm::afterWidgetInit()
  1904   bool isFirstEww = 
true;
  1914         setFocusProxy( eww->
widget() );
  1929   if ( e->type() == QEvent::KeyPress )
  1931     QKeyEvent *keyEvent = 
dynamic_cast<QKeyEvent *
>( e );
  1932     if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
  1943 void QgsAttributeForm::scanForEqualAttributes( 
QgsFeatureIterator &fit, QSet< int > &mixedValueFields, QHash< int, QVariant > &fieldSharedValues )
 const  1945   mixedValueFields.clear();
  1946   fieldSharedValues.clear();
  1952     for ( 
int i = 0; i < mLayer->
fields().
count(); ++i )
  1954       if ( mixedValueFields.contains( i ) )
  1959         fieldSharedValues[i] = f.
attribute( i );
  1963         if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
  1965           fieldSharedValues.remove( i );
  1966           mixedValueFields.insert( i );
  1972     if ( mixedValueFields.count() == mLayer->
fields().
count() )
  1981 void QgsAttributeForm::layerSelectionChanged()
  1993       resetMultiEdit( 
true );
  2000   mIsSettingMultiEditFeatures = 
true;
  2001   mMultiEditFeatureIds = fids;
  2003   if ( fids.isEmpty() )
  2006     QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
  2007     for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
  2009       wIt.value()->initialize( QVariant() );
  2011     mIsSettingMultiEditFeatures = 
false;
  2018   QSet< int > mixedValueFields;
  2019   QHash< int, QVariant > fieldSharedValues;
  2020   scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
  2027   Q_FOREACH ( 
int field, mixedValueFields )
  2031       w->initialize( firstFeature.
attribute( field ), true );
  2034   QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
  2035   for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
  2039       w->initialize( sharedValueIt.value(), false );
  2042   mIsSettingMultiEditFeatures = 
false;
  2047   if ( mOwnsMessageBar )
  2049   mOwnsMessageBar = 
false;
  2050   mMessageBar = messageBar;
  2060   QStringList filters;
  2063     QString filter = widget->currentFilterExpression();
  2064     if ( !filter.isNull() )
  2065       filters << 
'(' + filter + 
')';
  2068   return filters.join( QStringLiteral( 
" AND " ) );
  2071 int QgsAttributeForm::messageTimeout()
  2074   return settings.
value( QStringLiteral( 
"qgis/messageTimeout" ), 5 ).toInt();
  2079   bool newVisibility = expression.evaluate( expressionContext ).toBool();
  2081   if ( newVisibility != isVisible )
  2085       tabWidget->setTabVisible( widget, newVisibility );
  2089       widget->setVisible( newVisibility );
  2092     isVisible = newVisibility;
  2105   if ( infos.count() == 0 || !currentFormFeature( formFeature ) )
  2108   const QString hint = tr( 
"No feature joined" );
  2116     mJoinedFeatures[info] = joinFeature;
  2122       Q_FOREACH ( 
const QString &field, subsetNames )
  2126         QString hintText = hint;
  2140       for ( 
const QgsField &field : joinFields )
  2144         QString hintText = hint;
  2158 bool QgsAttributeForm::fieldIsEditable( 
int fieldIndex )
 const  2170       editable = fieldIsEditable( *( info->
joinLayer() ), srcFieldIndex, mFeature.
id() );
  2173     editable = fieldIsEditable( *mLayer, fieldIndex, mFeature.
id() );
  2190   mIconMap[eww->
widget()]->hide();
  2204         const QString file = QStringLiteral( 
"/mIconJoinNotEditable.svg" );
  2205         const QString tooltip = tr( 
"Join settings do not allow editing" );
  2206         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
  2210         const QString file = QStringLiteral( 
"mIconJoinHasNotUpsertOnEdit.svg" );
  2211         const QString tooltip = tr( 
"Join settings do not allow upsert on edit" );
  2212         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
  2216         const QString file = QStringLiteral( 
"/mIconJoinedLayerNotEditable.svg" );
  2217         const QString tooltip = tr( 
"Joined layer is not toggled editable" );
  2218         reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
  2224 void QgsAttributeForm::reloadIcon( 
const QString &file, 
const QString &tooltip, QSvgWidget *sw )
  2227   sw->setToolTip( tooltip );
 QList< QgsField > toList() const 
Utility function to return a list of QgsField instances. 
 
QString prefixedFieldName(const QgsField &field) const 
Returns the prefixed name of the field. 
 
Class for parsing and evaluation of expressions (formerly called "search strings"). 
 
QString displayName() const 
Returns the name to use when displaying this field. 
 
Wrapper for iterator of features from vector data provider or vector layer. 
 
Constraint was set by layer. 
 
An attribute editor widget that will represent arbitrary QML code. 
 
QString attributeFormModeString() const 
Returns given attributeFormMode as string. 
 
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
 
QSet< QgsFeatureId > QgsFeatureIds
 
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const 
Returns the joined feature corresponding to the feature. 
 
void setAttributeFormMode(const Mode &attributeFormMode)
Set attributeFormMode for the edited form. 
 
This is an abstract base class for any elements of a drag and drop form. 
 
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name. 
 
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication. 
 
virtual bool isGroupBox() const 
Returns if this container is going to be rendered as a group box. 
 
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const 
Finds the vector join for a layer field index. 
 
bool isValid() const 
Returns the validity of this feature. 
 
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations. 
 
This class is a composition of two QSettings instances: 
 
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const 
Returns joins where the field of a target layer is considered as an id. 
 
Form is in aggregate search mode, show each widget in this mode. 
 
bool hasUpsertOnEdit() const 
Returns whether a feature created on the target layer has to impact the joined layer by creating a ne...
 
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context. 
 
This class contains context information for attribute editor widgets. 
 
ConstraintOrigin
Origin of constraints. 
 
static void warning(const QString &msg)
Goes to qWarning. 
 
QSet< QString > referencedColumns() const 
Gets list of columns referenced by the expression. 
 
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file. 
 
Remove from current selection. 
 
#define Q_NOWARN_DEPRECATED_PUSH
 
int selectedFeatureCount() const 
Returns the number of features that are selected in this layer. 
 
A bar for displaying non-blocking messages to the user. 
 
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const 
Returns the value for setting key. 
 
Container of fields for a vector layer. 
 
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon. 
 
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes. 
 
void beforeRemovingExpressionField(int idx)
Will be emitted, when an expression field is going to be deleted from this vector layer...
 
This element will load a field's widget onto the form. 
 
This element will load a relation editor onto the form. 
 
bool allowCustomUi() const 
Returns true if the attribute editor should permit use of custom UI forms. 
 
Multi edit mode, for editing fields of multiple features at once. 
 
const QgsRelation & relation() const 
Gets the id of the relation which shall be embedded. 
 
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
 
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 feat...
 
QgsField at(int i) const 
Gets field at particular index (must be in range 0..N-1) 
 
QString name() const 
Returns the name of this element. 
 
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 isEditable() const 
Returns whether joined fields may be edited through the form of the target layer. ...
 
bool isEditable() const FINAL
Returns true if the provider is in editing mode. 
 
bool exists(int i) const 
Returns if a field index is valid. 
 
QgsFields fields() const FINAL
Returns the list of fields of this layer. 
 
bool isDynamicFormEnabled() const 
Returns whether the form has to be dynamically updated with joined fields when a feature is being cre...
 
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted...
 
QStringList * joinFieldNamesSubset() const 
Gets subset of fields to be used from joined layer. 
 
bool popWidget(QgsMessageBarItem *item)
Remove the passed widget from the bar (if previously added), then display the next one in the stack i...
 
Q_INVOKABLE QgsRelation relation(const QString &id) const 
Gets access to a relation by its id. 
 
bool showUnlinkButton() const 
Determines if the "unlink feature" button should be shown. 
 
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories...
 
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
 
Defines left outer join from our vector layer to some other vector layer. 
 
QMap< int, QVariant > QgsAttributeMap
 
void editingStopped()
Is emitted, when edited changes successfully have been written to the data provider. 
 
This class wraps a request for features to a vector layer (or directly its vector data provider)...
 
AttributeEditorType type() const 
The type of this element. 
 
void destroyEditCommand()
Destroy active command and reverts all changes in it. 
 
int count() const 
Returns number of items. 
 
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
This signal is emitted when selection was changed. 
 
Encapsulate a field in an attribute table or data source. 
 
const QgsFeatureIds & selectedFeatureIds() const 
Returns a list of the selected features IDs in this layer. 
 
int lookupField(const QString &fieldName) const 
Looks up field's index from the field name. 
 
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::Info, int duration=5)
convenience method for pushing a message to the bar 
 
QgsRelationManager relationManager
 
QgsField field(int fieldIdx) const 
Gets field at particular index (must be in range 0..N-1) 
 
QgsEditFormConfig editFormConfig
 
Add selection to current selection. 
 
void editingStarted()
Is emitted, when editing on this layer has started. 
 
FormMode formMode() const 
Returns the form mode. 
 
void endEditCommand()
Finish edit command and add it to undo/redo stack. 
 
QString attributeDisplayName(int index) const 
Convenience function that returns the attribute alias if defined or the field name else...
 
static bool eval(const QString &command, QString &result)
Eval a Python statement. 
 
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object. 
 
int indexFromName(const QString &fieldName) const 
Gets the field index from the field name. 
 
Set selection, removing any existing selection. 
 
QgsOptionalExpression visibilityExpression() const 
The visibility expression is used in the attribute form to show or hide this container based on an ex...
 
QFile * localFile(const QString &filePathOrUrl)
Returns a QFile from a local file or to a temporary file previously fetched by the registry...
 
void setValid(bool validity)
Sets the validity of the feature. 
 
Modify current selection to include only select features which match. 
 
SelectBehavior
Selection behavior. 
 
bool hasSubset(bool blacklisted=true) const 
Returns true if blacklisted fields is not empty or if a subset of names has been set. 
 
void selectByExpression(const QString &expression, SelectBehavior behavior=SetSelection)
Select matching features using an expression. 
 
bool enabled() const 
Check if this optional is enabled. 
 
void beforeAddingExpressionField(const QString &fieldName)
Will be emitted, when an expression field is going to be added to this vector layer. 
 
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar after hiding the currently visible one and putting it in a stack...
 
QVariant attribute(const QString &name) const 
Lookup attribute value from attribute name. 
 
virtual QgsVectorDataProvider::Capabilities capabilities() const 
Returns flags containing the supported capabilities. 
 
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement. 
 
QList< QgsAttributeEditorElement * > children() const 
Gets a list of the children elements of this container. 
 
bool showLinkButton() const 
Determines if the "link feature" button should be shown. 
 
This class manages a set of relations between layers. 
 
void beforeModifiedCheck() const 
Is emitted, when layer is checked for modifications. Use for last-minute additions. 
 
Single edit mode, for editing a single feature. 
 
int columnCount() const 
Gets the number of columns in this group. 
 
static QgsProject * instance()
Returns the QgsProject singleton instance. 
 
QString qmlCode() const 
The QML code that will be represented within this widget. 
 
T data() const 
Access the payload data. 
 
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
 
int size() const 
Returns number of items. 
 
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) FINAL
Adds a single feature to the sink. 
 
FieldOrigin fieldOrigin(int fieldIdx) const 
Gets field's origin (value from an enumeration) 
 
This is a container for attribute editors, used to group them visually in the attribute form if it is...
 
QgsVectorLayer * joinLayer() const 
Returns joined layer (may be null if the reference was set by layer ID and not resolved yet) ...
 
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request. 
 
bool showLabel() const 
Controls if this element should be labeled with a title (field, relation or groupname). 
 
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). 
 
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider. 
 
bool nextFeature(QgsFeature &f)
 
Form values are used for searching/filtering the layer. 
 
bool qgsVariantEqual(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether they are equal, NULL values are treated as equal...
 
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). 
 
Represents a vector layer which manages a vector based data sets. 
 
void updatedFields()
Is emitted, whenever the fields available from this layer have been changed. 
 
Allows modification of attribute values. 
 
A form was embedded as a widget on another form.