71int QgsAttributeForm::sFormCounter = 0;
76 , mOwnsMessageBar( true )
78 , mFormNr( sFormCounter++ )
80 , mPreventFeatureRefresh( false )
81 , mIsSettingMultiEditFeatures( false )
82 , mUnsavedMultiEditChanges( false )
83 , mEditCommandMessage( tr(
"Attributes changed" ) )
96 updateContainersVisibility();
104 qDeleteAll( mInterfaces );
131 mInterfaces.append( iface );
147 if ( mUnsavedMultiEditChanges )
150 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
151 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
152 if ( res == QMessageBox::Yes )
157 clearMultiEditMessages();
159 mUnsavedMultiEditChanges =
false;
211 w->setContext( newContext );
217 w->setVisible( relationWidgetsVisible );
224 mSearchButtonBox->setVisible(
false );
229 mSearchButtonBox->setVisible(
false );
234 mSearchButtonBox->setVisible(
false );
238 resetMultiEdit(
false );
240 mSearchButtonBox->setVisible(
false );
244 mSearchButtonBox->setVisible(
true );
250 mSearchButtonBox->setVisible(
false );
258 mSearchButtonBox->setVisible(
false );
267 const auto constMWidgets = mWidgets;
282 QVariant mainValue = eww->
value();
284 additionalFieldValues[index] = value;
285 eww->
setValues( mainValue, additionalFieldValues );
294 mIsSettingFeature =
true;
311 mIsSettingFeature =
false;
312 const auto constMInterfaces = mInterfaces;
315 iface->featureChanged();
331 mIsSettingFeature =
false;
334bool QgsAttributeForm::saveEdits( QString *error )
337 bool changedLayer =
false;
342 bool doUpdate =
false;
362 *error = tr(
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
365 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
366 QVariantList srcVars = QVariantList() << eww->
value();
367 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
371 for (
const QString &fieldName : additionalFields )
375 dstVars << dst.at( idx );
379 Q_ASSERT( dstVars.count() == srcVars.count() );
381 for (
int i = 0; i < dstVars.count(); i++ )
384 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
386 dst[fieldIndexes[i]] = srcVars[i];
396 const auto constMInterfaces = mInterfaces;
399 if ( !iface->acceptChanges( updatedFeature ) )
409 mFeature = updatedFeature;
415 bool res = mLayer->
addFeature( updatedFeature );
434 for (
int i = 0; i < dst.count(); ++i )
437 || !dst.at( i ).isValid()
438 || !fieldIsEditable( i ) )
444 QgsDebugMsgLevel( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
445 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( dst.at( i ) ) ).arg( dst.at( i ).isValid() ), 2 );
446 QgsDebugMsgLevel( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
447 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( src.at( i ) ) ).arg( src.at( i ).isValid() ), 2 );
449 newValues[i] = dst.at( i );
450 oldValues[i] = src.at( i );
457 if ( success && n > 0 )
484QgsFeature QgsAttributeForm::getUpdatedFeature()
const
496 QVariantList dstVars = QVariantList() << featureAttributes.at( eww->
fieldIdx() );
497 QVariantList srcVars = QVariantList() << eww->
value();
498 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
502 for (
const QString &fieldName : additionalFields )
506 dstVars << featureAttributes.at( idx );
510 Q_ASSERT( dstVars.count() == srcVars.count() );
512 for (
int i = 0; i < dstVars.count(); i++ )
514 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
515 featureAttributes[fieldIndexes[i]] = srcVars[i];
520 return updatedFeature;
523void QgsAttributeForm::updateValuesDependencies(
const int originIdx )
525 updateValuesDependenciesDefaultValues( originIdx );
526 updateValuesDependenciesVirtualFields( originIdx );
529void QgsAttributeForm::updateValuesDependenciesDefaultValues(
const int originIdx )
531 if ( !mDefaultValueDependencies.contains( originIdx ) )
539 QgsFeature updatedFeature = getUpdatedFeature();
542 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
559 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
570void QgsAttributeForm::updateValuesDependenciesVirtualFields(
const int originIdx )
572 if ( !mVirtualFieldsDependencies.contains( originIdx ) )
579 QgsFeature updatedFeature = getUpdatedFeature();
582 const QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
590 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
596 const QVariant value = exp.evaluate( &context );
602void QgsAttributeForm::updateRelatedLayerFields()
605 updateRelatedLayerFieldsDependencies();
607 if ( mRelatedLayerFieldsDependencies.isEmpty() )
614 QgsFeature updatedFeature = getUpdatedFeature();
617 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mRelatedLayerFieldsDependencies;
621 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
627 QVariant value = exp.evaluate( &context );
632void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
637 mUnsavedMultiEditChanges =
false;
641void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
643 clearMultiEditMessages();
644 resetMultiEdit( link == QLatin1String(
"#apply" ) );
647void QgsAttributeForm::filterTriggered()
649 QString filter = createFilterExpression();
655void QgsAttributeForm::searchZoomTo()
657 QString filter = createFilterExpression();
658 if ( filter.isEmpty() )
664void QgsAttributeForm::searchFlash()
666 QString filter = createFilterExpression();
667 if ( filter.isEmpty() )
673void QgsAttributeForm::filterAndTriggered()
675 QString filter = createFilterExpression();
676 if ( filter.isEmpty() )
684void QgsAttributeForm::filterOrTriggered()
686 QString filter = createFilterExpression();
687 if ( filter.isEmpty() )
695void QgsAttributeForm::pushSelectedFeaturesMessage()
701 tr(
"%n matching feature(s) selected",
"matching features", count ),
707 tr(
"No matching features found" ),
721 QString filter = createFilterExpression();
722 if ( filter.isEmpty() )
726 pushSelectedFeaturesMessage();
731void QgsAttributeForm::searchSetSelection()
736void QgsAttributeForm::searchAddToSelection()
741void QgsAttributeForm::searchRemoveFromSelection()
746void QgsAttributeForm::searchIntersectSelection()
751bool QgsAttributeForm::saveMultiEdits()
755 const QList<int> fieldIndexes = mFormEditorWidgets.uniqueKeys();
756 mFormEditorWidgets.constBegin();
757 for (
int fieldIndex : fieldIndexes )
759 const QList<QgsAttributeFormEditorWidget *> widgets = mFormEditorWidgets.values( fieldIndex );
760 if ( !widgets.first()->hasChanged() )
763 if ( !widgets.first()->currentValue().isValid()
764 || !fieldIsEditable( fieldIndex ) )
771 widget->changesCommitted();
773 newAttributeValues.insert( fieldIndex, widgets.first()->currentValue() );
776 if ( newAttributeValues.isEmpty() )
784 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
785 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
786 if ( res != QMessageBox::Ok )
797 const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
800 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
801 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
807 clearMultiEditMessages();
820 if ( !mButtonBox->isVisible() )
821 mMessageBar->
pushItem( mMultiEditMessageBarItem );
847 wrapper->notifyAboutToSave();
887 success = saveEdits( error );
891 success = saveMultiEdits();
896 mUnsavedMultiEditChanges =
false;
905 mValuesInitialized =
false;
906 const auto constMWidgets = mWidgets;
909 ww->setFeature( mFeature );
913 updateFieldDependencies();
923 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
924 updateValuesDependencies( eww->
fieldIdx() );
925 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
928 mValuesInitialized =
true;
934 const auto widgets { findChildren< QgsAttributeFormEditorWidget * >() };
941void QgsAttributeForm::clearMultiEditMessages()
943 if ( mMultiEditUnsavedMessageBarItem )
945 if ( !mButtonBox->isVisible() )
946 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
947 mMultiEditUnsavedMessageBarItem =
nullptr;
949 if ( mMultiEditMessageBarItem )
951 if ( !mButtonBox->isVisible() )
952 mMessageBar->
popWidget( mMultiEditMessageBarItem );
953 mMultiEditMessageBarItem =
nullptr;
957QString QgsAttributeForm::createFilterExpression()
const
962 QString filter = w->currentFilterExpression();
963 if ( !filter.isEmpty() )
967 if ( filters.isEmpty() )
970 QString filter = filters.join( QLatin1String(
") AND (" ) ).prepend(
'(' ).append(
')' );
979 if ( mExtraContextScope )
986void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
991 bool signalEmitted =
false;
993 if ( mValuesInitialized )
1012 for (
int i = 0; i < additionalFields.count(); i++ )
1014 const QString fieldName = additionalFields.at( i );
1015 const QVariant value = additionalFieldValues.at( i );
1019 signalEmitted =
true;
1021 if ( mValuesInitialized )
1022 updateJoinedFields( *eww );
1028 if ( !mIsSettingMultiEditFeatures )
1030 mUnsavedMultiEditChanges =
true;
1032 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
1033 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
1034 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1035 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
1036 clearMultiEditMessages();
1039 if ( !mButtonBox->isVisible() )
1040 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
1043 signalEmitted =
true;
1053 updateConstraints( eww );
1056 if ( mValuesInitialized )
1059 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1060 updateValuesDependencies( eww->
fieldIdx() );
1061 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1068 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1071 if ( formEditorWidget->editorWidget() == eww )
1076 formEditorWidget->editorWidget()->blockSignals(
true );
1077 formEditorWidget->editorWidget()->setValue( value );
1078 formEditorWidget->editorWidget()->blockSignals(
false );
1081 if ( !signalEmitted )
1086 bool attributeHasChanged = !mIsSettingFeature;
1088 attributeHasChanged &= !mIsSettingMultiEditFeatures;
1094void QgsAttributeForm::updateAllConstraints()
1096 const auto constMWidgets = mWidgets;
1101 updateConstraints( eww );
1109 if ( currentFormValuesFeature( ft ) )
1121 updateConstraint( ft, eww );
1124 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
1127 updateConstraint( ft, depsEww );
1135 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
1136 for ( ContainerInformation *info : infos )
1138 info->apply( &context );
1143void QgsAttributeForm::updateContainersVisibility()
1147 const QVector<ContainerInformation *> infos = mContainerVisibilityCollapsedInformation;
1149 for ( ContainerInformation *info : infos )
1151 info->apply( &context );
1155 updateAllConstraints();
1173 if ( mJoinedFeatures.contains( info ) )
1191void QgsAttributeForm::updateLabels()
1193 if ( ! mLabelDataDefinedProperties.isEmpty() )
1196 if ( currentFormValuesFeature( currentFeature ) )
1200 for (
auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
1202 QLabel *label { it.key() };
1204 const QString value { it->valueAsString( context, QString(), &ok ) };
1205 if ( ok && ! value.isEmpty() )
1207 label->setText( value );
1214bool QgsAttributeForm::currentFormValuesFeature(
QgsFeature &feature )
1227 if ( dst.count() > eww->
fieldIdx() )
1229 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1230 QVariantList srcVars = QVariantList() << eww->
value();
1231 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1235 for (
const QString &fieldName : additionalFields )
1238 fieldIndexes << idx;
1239 dstVars << dst.at( idx );
1243 Q_ASSERT( dstVars.count() == srcVars.count() );
1245 for (
int i = 0; i < dstVars.count(); i++ )
1251 dst[fieldIndexes[i]] = srcVars[i];
1268void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1270 mContainerVisibilityCollapsedInformation.append( info );
1272 const QSet<QString> referencedColumns = info->expression.referencedColumns().unite( info->collapsedExpression.referencedColumns() );
1274 for (
const QString &col : referencedColumns )
1276 mContainerInformationDependency[ col ].append( info );
1280bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1304bool QgsAttributeForm::currentFormValidHardConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1325void QgsAttributeForm::onAttributeAdded(
int idx )
1327 mPreventFeatureRefresh =
false;
1331 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
1339void QgsAttributeForm::onAttributeDeleted(
int idx )
1341 mPreventFeatureRefresh =
false;
1345 attrs.remove( idx );
1353void QgsAttributeForm::onRelatedFeaturesChanged()
1355 updateRelatedLayerFields();
1358void QgsAttributeForm::onUpdatedFields()
1360 mPreventFeatureRefresh =
false;
1377 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
1387void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1393 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1396 formEditorWidget->setConstraintStatus( constraint, description, err, result );
1401 QList<QgsEditorWidgetWrapper *> wDeps;
1413 if ( name != ewwName )
1420 for (
const QString &colName : referencedColumns )
1422 if ( name == colName )
1424 wDeps.append( eww );
1437 return setupRelationWidgetWrapper( QString(), rel, context );
1450void QgsAttributeForm::preventFeatureRefresh()
1452 mPreventFeatureRefresh =
true;
1483 return mNeedsGeometry;
1486void QgsAttributeForm::synchronizeState()
1488 bool isEditable = ( mFeature.
isValid()
1498 const QList<QgsAttributeFormEditorWidget *> formWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1501 formWidget->setConstraintResultVisible( isEditable );
1505 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1506 ww->setEnabled( enabled );
1512 ww->setEnabled( isEditable );
1520 QStringList invalidFields, descriptions;
1521 mValidConstraints = currentFormValidHardConstraints( invalidFields, descriptions );
1525 if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
1527 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 );
1528 mMessageBar->
pushItem( mConstraintsFailMessageBarItem );
1530 else if ( mValidConstraints && mConstraintsFailMessageBarItem )
1532 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1533 mConstraintsFailMessageBarItem =
nullptr;
1536 else if ( mConstraintsFailMessageBarItem )
1538 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1539 mConstraintsFailMessageBarItem =
nullptr;
1542 isEditable = isEditable & mValidConstraints;
1546 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1548 okButton->setEnabled( isEditable );
1551void QgsAttributeForm::init()
1553 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1556 QWidget *formWidget =
nullptr;
1557 mNeedsGeometry =
false;
1559 bool buttonBoxVisible =
true;
1563 buttonBoxVisible = mButtonBox->isVisible();
1565 mButtonBox =
nullptr;
1568 if ( mSearchButtonBox )
1570 delete mSearchButtonBox;
1571 mSearchButtonBox =
nullptr;
1574 qDeleteAll( mWidgets );
1577 while ( QWidget *w = this->findChild<QWidget *>() )
1583 QVBoxLayout *vl =
new QVBoxLayout();
1584 vl->setContentsMargins( 0, 0, 0, 0 );
1586 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1587 vl->addWidget( mMessageBar );
1592 QGridLayout *layout =
new QGridLayout();
1593 QWidget *container =
new QWidget();
1594 container->setLayout( layout );
1595 vl->addWidget( container );
1597 mFormEditorWidgets.clear();
1598 mFormWidgets.clear();
1601 setContentsMargins( 0, 0, 0, 0 );
1610 if ( file && file->open( QFile::ReadOnly ) )
1614 QFileInfo fi( file->fileName() );
1615 loader.setWorkingDirectory( fi.dir() );
1616 formWidget = loader.load( file,
this );
1619 formWidget->setWindowFlags( Qt::Widget );
1620 layout->addWidget( formWidget );
1623 mButtonBox = findChild<QDialogButtonBox *>();
1626 formWidget->installEventFilter(
this );
1638 int columnCount = 1;
1639 bool hasRootFields =
false;
1640 bool addSpacer =
true;
1649 if ( !containerDef )
1654 tabWidget =
nullptr;
1655 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1656 if ( widgetInfo.labelStyle.overrideColor )
1658 if ( widgetInfo.labelStyle.color.isValid() )
1660 widgetInfo.widget->setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1663 if ( widgetInfo.labelStyle.overrideFont )
1665 widgetInfo.widget->setFont( widgetInfo.labelStyle.font );
1667 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1679 layout->addWidget( tabWidget, row, column, 1, 2 );
1683 QWidget *tabPage =
new QWidget( tabWidget );
1685 tabWidget->addTab( tabPage, widgDef->name() );
1686 tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
1690 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1692 QGridLayout *tabPageLayout =
new QGridLayout();
1693 tabPage->setLayout( tabPageLayout );
1695 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1696 tabPageLayout->addWidget( widgetInfo.widget );
1701 hasRootFields =
true;
1702 tabWidget =
nullptr;
1703 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1706 if ( widgetInfo.showLabel )
1708 if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
1710 collapsibleGroupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1713 if ( widgetInfo.labelStyle.overrideFont )
1715 collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
1718 collapsibleGroupBox->setTitle( widgetInfo.labelText );
1721 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1722 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
1723 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1725 QVBoxLayout *
c =
new QVBoxLayout();
1726 c->addWidget( collapsibleGroupBox );
1727 layout->addLayout(
c, row, column, 1, 2 );
1735 hasRootFields =
true;
1736 tabWidget =
nullptr;
1737 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1738 QLabel *label =
new QLabel( widgetInfo.labelText );
1740 if ( widgetInfo.labelStyle.overrideColor )
1742 if ( widgetInfo.labelStyle.color.isValid() )
1744 label->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1748 if ( widgetInfo.labelStyle.overrideFont )
1750 label->setFont( widgetInfo.labelStyle.font );
1753 label->setToolTip( widgetInfo.toolTip );
1754 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1756 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1759 label->setBuddy( widgetInfo.widget );
1762 if ( widgetInfo.widget
1763 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
1764 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
1765 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
1768 if ( !widgetInfo.showLabel )
1770 QVBoxLayout *
c =
new QVBoxLayout();
1771 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1772 c->addWidget( widgetInfo.widget );
1773 layout->addLayout(
c, row, column, 1, 2 );
1776 else if ( widgetInfo.labelOnTop )
1778 QVBoxLayout *
c =
new QVBoxLayout();
1779 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1780 c->addWidget( label );
1781 c->addWidget( widgetInfo.widget );
1782 layout->addLayout(
c, row, column, 1, 2 );
1787 layout->addWidget( label, row, column++ );
1788 layout->addWidget( widgetInfo.widget, row, column++ );
1795 const int fieldIdx = fieldElement->
idx();
1796 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
1798 const QString fieldName { mLayer->
fields().
at( fieldIdx ).
name() };
1802 if ( property.isActive() && !
property.expressionString().isEmpty() )
1804 mLabelDataDefinedProperties[ label ] = property;
1811 if ( column >= columnCount * 2 )
1818 if ( hasRootFields && addSpacer )
1820 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1821 layout->addItem( spacerItem, row, 0 );
1822 layout->setRowStretch( row, 1 );
1825 formWidget = container;
1834 formWidget =
new QWidget(
this );
1835 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1836 formWidget->setLayout( gridLayout );
1842 scrollArea->setWidget( formWidget );
1843 scrollArea->setWidgetResizable(
true );
1844 scrollArea->setFrameShape( QFrame::NoFrame );
1845 scrollArea->setFrameShadow( QFrame::Plain );
1846 scrollArea->setFocusProxy(
this );
1847 layout->addWidget( scrollArea );
1851 layout->addWidget( formWidget );
1866 QString labelText = fieldName;
1867 labelText.replace(
'&', QLatin1String(
"&&" ) );
1871 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1877 QLabel *label =
new QLabel( labelText );
1879 QSvgWidget *i =
new QSvgWidget();
1880 i->setFixedSize( 18, 18 );
1885 if ( property.isActive() && ! property.expressionString().isEmpty() )
1887 mLabelDataDefinedProperties[ label ] = property;
1893 QWidget *w =
nullptr;
1898 mFormEditorWidgets.insert( idx, formWidget );
1899 mFormWidgets.append( formWidget );
1902 label->setBuddy( eww->
widget() );
1906 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() ) ) );
1915 mWidgets.append( eww );
1916 mIconMap[eww->
widget()] = i;
1921 gridLayout->addWidget( label, row++, 0, 1, 2 );
1922 gridLayout->addWidget( w, row++, 0, 1, 2 );
1923 gridLayout->addWidget( i, row++, 0, 1, 2 );
1927 gridLayout->addWidget( label, row, 0 );
1928 gridLayout->addWidget( w, row, 1 );
1929 gridLayout->addWidget( i, row++, 2 );
1943 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1944 collapsibleGroupBoxLayout->addWidget( formWidget );
1945 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1947 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
1949 mWidgets.append( rww );
1950 mFormWidgets.append( formWidget );
1955 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1956 gridLayout->addItem( spacerItem, row, 0 );
1957 gridLayout->setRowStretch( row, 1 );
1962 updateFieldDependencies();
1966 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1967 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1968 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1970 mButtonBox->setVisible( buttonBoxVisible );
1972 if ( !mSearchButtonBox )
1974 mSearchButtonBox =
new QWidget();
1975 QHBoxLayout *boxLayout =
new QHBoxLayout();
1976 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1977 mSearchButtonBox->setLayout( boxLayout );
1978 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1980 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
1982 boxLayout->addWidget( clearButton );
1983 boxLayout->addStretch( 1 );
1985 QPushButton *flashButton =
new QPushButton();
1986 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1987 flashButton->setText( tr(
"&Flash Features" ) );
1988 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1989 boxLayout->addWidget( flashButton );
1991 QPushButton *openAttributeTableButton =
new QPushButton();
1992 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1993 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
1994 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
1995 connect( openAttributeTableButton, &QToolButton::clicked,
this, [ = ]
1999 boxLayout->addWidget( openAttributeTableButton );
2001 QPushButton *zoomButton =
new QPushButton();
2002 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2003 zoomButton->setText( tr(
"&Zoom to Features" ) );
2004 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
2005 boxLayout->addWidget( zoomButton );
2007 QToolButton *selectButton =
new QToolButton();
2008 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2009 selectButton->setText( tr(
"&Select Features" ) );
2011 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
2012 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
2013 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
2014 QMenu *selectMenu =
new QMenu( selectButton );
2015 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
2017 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
2018 selectMenu->addAction( selectAction );
2019 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
2021 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
2022 selectMenu->addAction( addSelectAction );
2023 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
2025 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
2026 selectMenu->addAction( deselectAction );
2027 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
2029 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
2030 selectMenu->addAction( filterSelectAction );
2031 selectButton->setMenu( selectMenu );
2032 boxLayout->addWidget( selectButton );
2036 QToolButton *filterButton =
new QToolButton();
2037 filterButton->setText( tr(
"Filter Features" ) );
2038 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
2039 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2040 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
2041 QMenu *filterMenu =
new QMenu( filterButton );
2042 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
2043 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
2044 filterMenu->addAction( filterAndAction );
2045 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
2046 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
2047 filterMenu->addAction( filterOrAction );
2048 filterButton->setMenu( filterMenu );
2049 boxLayout->addWidget( filterButton );
2053 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
2055 closeButton->setShortcut( Qt::Key_Escape );
2056 boxLayout->addWidget( closeButton );
2059 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2077 const auto constMInterfaces = mInterfaces;
2088 QApplication::restoreOverrideCursor();
2091void QgsAttributeForm::cleanPython()
2093 if ( !mPyFormVarName.isNull() )
2095 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
2100void QgsAttributeForm::initPython()
2117 if ( !initFilePath.isEmpty() )
2121 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2124 QTextStream inf( inputFile );
2125#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2126 inf.setCodec(
"UTF-8" );
2128 initCode = inf.readAll();
2133 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
2144 if ( initCode.isEmpty() )
2146 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
2157 if ( !initCode.isEmpty() )
2163 tr(
"Python macro could not be run due to missing permissions." ),
2171 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
2173 static int sFormId = 0;
2174 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
2176 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
2177 .arg( mPyFormVarName )
2178 .arg( ( quint64 )
this );
2182 QgsDebugMsg( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
2185 if ( numArgs == QLatin1String(
"3" ) )
2193 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 ) );
2196 QString expr = QString(
"%1(%2)" )
2197 .arg( mLayer->editFormInit() )
2198 .arg( mPyFormVarName );
2199 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2209 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 ) );
2217 WidgetInfo newWidgetInfo;
2219 newWidgetInfo.labelStyle = widgetDef->
labelStyle();
2221 switch ( widgetDef->
type() )
2233 mWidgets.append( actionWrapper );
2234 newWidgetInfo.widget = actionWrapper->
widget();
2235 newWidgetInfo.showLabel =
false;
2248 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2254 mFormEditorWidgets.insert( fldIdx, formWidget );
2255 mFormWidgets.append( formWidget );
2259 newWidgetInfo.widget = formWidget;
2260 mWidgets.append( eww );
2262 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2263 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
2268 newWidgetInfo.labelText.replace(
'&', QLatin1String(
"&&" ) );
2269 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
2270 newWidgetInfo.showLabel = widgetDef->
showLabel();
2291 mWidgets.append( rww );
2292 mFormWidgets.append( formWidget );
2294 newWidgetInfo.widget = formWidget;
2295 newWidgetInfo.showLabel = relDef->
showLabel();
2296 newWidgetInfo.labelText = relDef->
label();
2297 if ( newWidgetInfo.labelText.isEmpty() )
2299 newWidgetInfo.labelOnTop =
true;
2311 if ( columnCount <= 0 )
2315 QWidget *myContainer =
nullptr;
2319 widgetName = QStringLiteral(
"QGroupBox" );
2322 groupBox->setTitle( container->
name() );
2323 if ( newWidgetInfo.labelStyle.overrideColor )
2325 if ( newWidgetInfo.labelStyle.color.isValid() )
2327 groupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2330 if ( newWidgetInfo.labelStyle.overrideFont )
2332 groupBox->setFont( newWidgetInfo.labelStyle.font );
2335 myContainer = groupBox;
2336 newWidgetInfo.widget = myContainer;
2341 myContainer =
new QWidget();
2345 scrollArea->setWidget( myContainer );
2346 scrollArea->setWidgetResizable(
true );
2347 scrollArea->setFrameShape( QFrame::NoFrame );
2348 widgetName = QStringLiteral(
"QScrollArea QWidget" );
2350 newWidgetInfo.widget = scrollArea;
2355 QString style {QStringLiteral(
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
2356 newWidgetInfo.widget->setStyleSheet( style );
2359 QGridLayout *gbLayout =
new QGridLayout();
2360 myContainer->setLayout( gbLayout );
2364 bool addSpacer =
true;
2366 const QList<QgsAttributeEditorElement *> children = container->
children();
2370 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2381 if ( widgetInfo.labelText.isNull() || ! widgetInfo.showLabel )
2383 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2388 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2390 if ( widgetInfo.labelStyle.overrideColor )
2392 if ( widgetInfo.labelStyle.color.isValid() )
2394 mypLabel->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2398 if ( widgetInfo.labelStyle.overrideFont )
2400 mypLabel->setFont( widgetInfo.labelStyle.font );
2408 const int fldIdx = fieldDef->
idx();
2409 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2411 const QString fieldName { fields.
at( fldIdx ).
name() };
2415 if ( property.isActive() && !
property.expressionString().isEmpty() )
2417 mLabelDataDefinedProperties[ mypLabel ] = property;
2423 mypLabel->setToolTip( widgetInfo.toolTip );
2424 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2426 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2429 mypLabel->setBuddy( widgetInfo.widget );
2431 if ( widgetInfo.labelOnTop )
2433 QVBoxLayout *
c =
new QVBoxLayout();
2434 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2435 c->layout()->addWidget( mypLabel );
2436 c->layout()->addWidget( widgetInfo.widget );
2437 gbLayout->addLayout(
c, row, column, 1, 2 );
2442 gbLayout->addWidget( mypLabel, row, column++ );
2443 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2447 if ( column >= columnCount * 2 )
2453 if ( widgetInfo.widget
2454 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2455 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2456 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2460 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
2466 QWidget *spacer =
new QWidget();
2467 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2468 gbLayout->addWidget( spacer, ++row, 0 );
2469 gbLayout->setRowStretch( row, 1 );
2472 newWidgetInfo.labelText = QString();
2473 newWidgetInfo.labelOnTop =
true;
2474 newWidgetInfo.showLabel = widgetDef->
showLabel();
2487 mWidgets.append( qmlWrapper );
2489 newWidgetInfo.widget = qmlWrapper->
widget();
2490 newWidgetInfo.labelText = elementDef->
name();
2491 newWidgetInfo.labelOnTop =
true;
2492 newWidgetInfo.showLabel = widgetDef->
showLabel();
2504 mWidgets.append( htmlWrapper );
2506 newWidgetInfo.widget = htmlWrapper->
widget();
2507 newWidgetInfo.labelText = elementDef->
name();
2508 newWidgetInfo.labelOnTop =
true;
2509 newWidgetInfo.showLabel = widgetDef->
showLabel();
2515 QgsDebugMsg( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
2519 return newWidgetInfo;
2522void QgsAttributeForm::createWrappers()
2524 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2525 const QList<QgsField> fields = mLayer->
fields().
toList();
2527 const auto constMyWidgets = myWidgets;
2528 for ( QWidget *myWidget : constMyWidgets )
2531 QVariant vRel = myWidget->property(
"qgisRelation" );
2532 if ( vRel.isValid() )
2542 mWidgets.append( rww );
2547 const auto constFields = fields;
2550 if (
field.
name() == myWidget->objectName() )
2555 mWidgets.append( eww );
2562void QgsAttributeForm::afterWidgetInit()
2564 bool isFirstEww =
true;
2566 const auto constMWidgets = mWidgets;
2575 setFocusProxy( eww->
widget() );
2585 if ( relationWidgetWrapper )
2598 if ( e->type() == QEvent::KeyPress )
2600 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
2601 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
2613 QSet< int > &mixedValueFields,
2614 QHash< int, QVariant > &fieldSharedValues )
const
2616 mixedValueFields.clear();
2617 fieldSharedValues.clear();
2623 for (
int i = 0; i < mLayer->
fields().count(); ++i )
2625 if ( mixedValueFields.contains( i ) )
2630 fieldSharedValues[i] = f.
attribute( i );
2634 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
2636 fieldSharedValues.remove( i );
2637 mixedValueFields.insert( i );
2643 if ( mixedValueFields.count() == mLayer->
fields().
count() )
2652void QgsAttributeForm::layerSelectionChanged()
2665 resetMultiEdit(
true );
2672 mIsSettingMultiEditFeatures =
true;
2673 mMultiEditFeatureIds = fids;
2675 if ( fids.isEmpty() )
2678 QMultiMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2679 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2681 wIt.value()->initialize( QVariant() );
2683 mIsSettingMultiEditFeatures =
false;
2690 QSet< int > mixedValueFields;
2691 QHash< int, QVariant > fieldSharedValues;
2692 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2699 const auto constMixedValueFields = mixedValueFields;
2700 for (
int fieldIndex : std::as_const( mixedValueFields ) )
2702 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( fieldIndex );
2703 if ( formEditorWidgets.isEmpty() )
2706 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
2707 QVariantList additionalFieldValues;
2708 for (
const QString &additionalField : additionalFields )
2709 additionalFieldValues << firstFeature.
attribute( additionalField );
2712 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
2714 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2715 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2717 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( sharedValueIt.key() );
2718 if ( formEditorWidgets.isEmpty() )
2722 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
2723 for (
const QString &additionalField : additionalFields )
2726 if ( constMixedValueFields.contains( index ) )
2733 QVariantList additionalFieldValues;
2736 for (
const QString &additionalField : additionalFields )
2737 additionalFieldValues << firstFeature.
attribute( additionalField );
2739 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
2743 for (
const QString &additionalField : additionalFields )
2746 Q_ASSERT( fieldSharedValues.contains( index ) );
2747 additionalFieldValues << fieldSharedValues.value( index );
2750 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
2754 setMultiEditFeatureIdsRelations( fids );
2756 mIsSettingMultiEditFeatures =
false;
2761 if ( mOwnsMessageBar )
2763 mOwnsMessageBar =
false;
2764 mMessageBar = messageBar;
2774 QStringList filters;
2777 QString filter = widget->currentFilterExpression();
2778 if ( !filter.isNull() )
2779 filters <<
'(' + filter +
')';
2782 return filters.join( QLatin1String(
" AND " ) );
2787 mExtraContextScope.reset( extraScope );
2793 const bool newVisibility = expression.evaluate( expressionContext ).toBool();
2795 if ( expression.isValid() && ! expression.hasEvalError() && newVisibility != isVisible )
2803 widget->setVisible( newVisibility );
2806 isVisible = newVisibility;
2809 const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
2811 if ( collapsedExpression.isValid() && ! collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
2816 collapsibleGroupBox->
setCollapsed( newCollapsedState );
2817 isCollapsed = newCollapsedState;
2831 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
2834 const QString hint = tr(
"No feature joined" );
2835 const auto constInfos = infos;
2838 if ( !info->isDynamicFormEnabled() )
2843 mJoinedFeatures[info] = joinFeature;
2845 if ( info->hasSubset() )
2849 const auto constSubsetNames = subsetNames;
2850 for (
const QString &
field : constSubsetNames )
2852 QString prefixedName = info->prefixedFieldName(
field );
2854 QString hintText = hint;
2870 QString prefixedName = info->prefixedFieldName(
field );
2872 QString hintText = hint;
2886bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
2891void QgsAttributeForm::updateFieldDependencies()
2893 mDefaultValueDependencies.clear();
2894 mVirtualFieldsDependencies.clear();
2895 mRelatedLayerFieldsDependencies.clear();
2904 updateFieldDependenciesDefaultValue( eww );
2905 updateFieldDependenciesVirtualFields( eww );
2906 updateRelatedLayerFieldsDependencies( eww );
2914 if ( exp.needsGeometry() )
2915 mNeedsGeometry =
true;
2917 const QSet<QString> referencedColumns = exp.referencedColumns();
2918 for (
const QString &referencedColumn : referencedColumns )
2924 for (
const int id : allAttributeIds )
2926 mDefaultValueDependencies.insertMulti(
id, eww );
2931 mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
2939 if ( expressionField.isEmpty() )
2944 if ( exp.needsGeometry() )
2945 mNeedsGeometry =
true;
2947 const QSet<QString> referencedColumns = exp.referencedColumns();
2948 for (
const QString &referencedColumn : referencedColumns )
2953 for (
const int id : allAttributeIds )
2955 mVirtualFieldsDependencies.insertMulti(
id, eww );
2960 mVirtualFieldsDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
2970 if ( expressionField.contains( QStringLiteral(
"relation_aggregate" ) )
2971 || expressionField.contains( QStringLiteral(
"get_features" ) ) )
2972 mRelatedLayerFieldsDependencies.insert( eww );
2976 mRelatedLayerFieldsDependencies.clear();
2981 if ( ! editorWidgetWrapper )
2984 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
2989void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
2994 if ( !relationEditorWidget )
3007 mIconMap[eww->
widget()]->hide();
3021 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
3022 const QString tooltip = tr(
"Join settings do not allow editing" );
3023 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3027 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
3028 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
3029 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3033 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
3034 const QString tooltip = tr(
"Joined layer is not toggled editable" );
3035 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3041void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
3044 sw->setToolTip( tooltip );
@ Warning
Warning message.
@ Info
Information message.
@ Success
Used for reporting a successful operation.
SelectBehavior
Specifies how a selection should be applied.
@ SetSelection
Set selection, removing any existing selection.
@ AddToSelection
Add selection to current selection.
@ IntersectSelection
Modify current selection to include only select features which match.
@ RemoveFromSelection
Remove from current selection.
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
This element will load a layer action onto the form.
const QgsAction & action(const QgsVectorLayer *layer) const
Returns the (possibly lazy loaded) action for the given layer.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QgsOptionalExpression visibilityExpression() const
The visibility expression is used in the attribute form to show or hide this container based on an ex...
virtual bool isGroupBox() const
Returns if this container is going to be a group box.
QgsOptionalExpression collapsedExpression() const
The collapsed expression is used in the attribute form to set the collapsed status of the group box c...
bool collapsed() const
For group box containers returns if this group box is collapsed.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
QColor backgroundColor() const
backgroundColor
int columnCount() const
Gets the number of columns in this group.
This class contains context information for attribute editor widgets.
FormMode formMode() const
Returns the form mode.
QString attributeFormModeString() const
Returns given attributeFormMode as string.
@ Embed
A form was embedded as a widget on another form.
bool allowCustomUi() const
Returns true if the attribute editor should permit use of custom UI forms.
@ SearchMode
Form values are used for searching/filtering the layer.
@ FixAttributeMode
Fix feature mode, for modifying the feature attributes without saving. The updated feature is availab...
@ IdentifyMode
Identify the feature.
@ SingleEditMode
Single edit mode, for editing a single feature.
@ AggregateSearchMode
Form is in aggregate search mode, show each widget in this mode.
@ MultiEditMode
Multi edit mode, for editing fields of multiple features at once.
void setAttributeFormMode(const Mode &attributeFormMode)
Set attributeFormMode for the edited form.
Mode attributeFormMode() const
Returns current attributeFormMode.
This is an abstract base class for any elements of a drag and drop form.
LabelStyle labelStyle() const
Returns the label style.
AttributeEditorType type() const
The type of this element.
bool showLabel() const
Controls if this element should be labeled with a title (field, relation or groupname).
QString name() const
Returns the name of this element.
@ AeTypeHtmlElement
A HTML element.
@ AeTypeQmlElement
A QML element.
@ AeTypeContainer
A container.
@ AeTypeRelation
A relation.
@ AeTypeAction
A layer action element (since QGIS 3.22)
This element will load a field's widget onto the form.
int idx() const
Returns the index of the field.
An attribute editor widget that will represent arbitrary HTML code.
QString htmlCode() const
The Html code that will be represented within this widget.
An attribute editor widget that will represent arbitrary QML code.
QString qmlCode() const
The QML code that will be represented within this widget.
This element will load a relation editor onto the form.
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
QVariantMap relationEditorConfiguration() const
Returns the relation editor widget configuration.
QVariant nmRelationId() const
Determines the relation id of the second relation involved in an N:M relation.
bool forceSuppressFormPopup() const
Determines the force suppress form popup status.
QString relationWidgetTypeId() const
Returns the current relation widget type id.
QString label() const
Determines the label of this element.
A groupbox that collapses/expands when toggled.
void setStyleSheet(const QString &style)
Overridden to prepare base call and avoid crash due to specific QT versions.
void setCollapsed(bool collapse)
Collapse or uncollapse this groupbox.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table form...
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
void setValid(bool validity)
Sets the validity of the feature.
bool isValid() const
Returns the validity of this feature.
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
ConstraintOrigin
Origin of constraints.
@ ConstraintOriginNotSet
Constraint is not set.
@ ConstraintOriginLayer
Constraint was set by layer.
QString constraintExpression() const
Returns the constraint expression for the field, if set.
static QString fieldToolTipExtended(const QgsField &field, const QgsVectorLayer *layer)
Returns a HTML formatted tooltip string for a field, containing details like the field name,...
Encapsulate a field in an attribute table or data source.
QString displayName() const
Returns the name to use when displaying this field.
QgsDefaultValue defaultValueDefinition
QgsFieldConstraints constraints
Container of fields for a vector layer.
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
int count() const
Returns number of items.
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
bool exists(int i) const
Returns if a field index is valid.
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
static bool pythonMacroAllowed(void(*lambda)()=nullptr, QgsMessageBar *messageBar=nullptr)
Returns true if python macros are currently allowed to be run If the global option is to ask user,...
static void warning(const QString &msg)
Goes to qWarning.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
void editingStarted()
Emitted when editing on this layer has started.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
Represents an item shown within a QgsMessageBar widget.
A bar for displaying non-blocking messages to the user.
bool popWidget(QgsMessageBarItem *item)
Remove the specified item from the bar, and display the next most recent one in the stack.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar, after hiding the currently visible one and putting it in a stack.
QFile * localFile(const QString &filePathOrUrl)
Returns a QFile from a local file or to a temporary file previously fetched by the registry.
bool enabled() const
Check if this optional is enabled.
T data() const
Access the payload data.
QgsRelationManager * relationManager
static QgsProject * instance()
Returns the QgsProject singleton instance.
bool hasProperty(int key) const override
Returns true if the collection contains a property with the specified key.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
A store for object properties.
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement.
static bool eval(const QString &command, QString &result)
Eval a Python statement.
This class manages a set of relations between layers.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
bool isInvalidJSON()
Returns whether the text edit widget contains Invalid JSON.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
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.
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined 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)
static bool fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature)
Tests whether a field is editable for a particular feature.
Represents a vector layer which manages a vector based data sets.
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else.
void beforeRemovingExpressionField(int idx)
Will be emitted, when an expression field is going to be deleted from this vector layer.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QString expressionField(int index) const
Returns the expression used for a given expression field.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Q_INVOKABLE void selectByExpression(const QString &expression, Qgis::SelectBehavior behavior=Qgis::SelectBehavior::SetSelection, QgsExpressionContext *context=nullptr)
Selects matching features using an expression.
void endEditCommand()
Finish edit command and add it to undo/redo stack.
void destroyEditCommand()
Destroy active command and reverts all changes in it.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap(), bool skipDefaultValues=false)
Changes attributes' values for a feature (but does not immediately commit the changes).
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
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)