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)