17#include "moc_qgsattributeform.cpp"
76int QgsAttributeForm::sFormCounter = 0;
81 , mOwnsMessageBar( true )
83 , mFormNr( sFormCounter++ )
85 , mPreventFeatureRefresh( false )
86 , mIsSettingMultiEditFeatures( false )
87 , mUnsavedMultiEditChanges( false )
88 , mEditCommandMessage( tr(
"Attributes changed" ) )
101 updateContainersVisibility();
103 updateEditableState();
110 qDeleteAll( mInterfaces );
137 mInterfaces.append( iface );
153 if ( mUnsavedMultiEditChanges )
156 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
157 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
158 if ( res == QMessageBox::Yes )
163 clearMultiEditMessages();
165 mUnsavedMultiEditChanges =
false;
217 w->setContext( newContext );
223 w->setVisible( relationWidgetsVisible );
230 mSearchButtonBox->setVisible(
false );
235 mSearchButtonBox->setVisible(
false );
240 mSearchButtonBox->setVisible(
false );
244 resetMultiEdit(
false );
246 mSearchButtonBox->setVisible(
false );
250 mSearchButtonBox->setVisible(
true );
256 mSearchButtonBox->setVisible(
false );
264 mSearchButtonBox->setVisible(
false );
273 const auto constMWidgets = mWidgets;
288 QVariant mainValue = eww->
value();
290 additionalFieldValues[index] = value;
291 eww->
setValues( mainValue, additionalFieldValues );
305 mIsSettingFeature =
true;
322 mIsSettingFeature =
false;
323 const auto constMInterfaces = mInterfaces;
326 iface->featureChanged();
342 mIsSettingFeature =
false;
345bool QgsAttributeForm::saveEdits( QString *error )
348 bool changedLayer =
false;
353 bool doUpdate =
false;
375 *error = tr(
"JSON value for %1 is invalid and has not been saved" ).arg( eww->
field().
name() );
378 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
379 QVariantList srcVars = QVariantList() << eww->
value();
380 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
384 for (
const QString &fieldName : additionalFields )
388 dstVars << dst.at( idx );
392 Q_ASSERT( dstVars.count() == srcVars.count() );
394 for (
int i = 0; i < dstVars.count(); i++ )
397 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
399 dst[fieldIndexes[i]] = srcVars[i];
409 const auto constMInterfaces = mInterfaces;
412 if ( !iface->acceptChanges( updatedFeature ) )
422 mFeature = updatedFeature;
428 bool res = mLayer->
addFeature( updatedFeature );
447 for (
int i = 0; i < dst.count(); ++i )
450 || !dst.at( i ).isValid()
451 || !fieldIsEditable( i ) )
457 QgsDebugMsgLevel( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
458 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( dst.at( i ) ) ).arg( dst.at( i ).isValid() ), 2 );
459 QgsDebugMsgLevel( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
460 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg(
QgsVariantUtils::isNull( src.at( i ) ) ).arg( src.at( i ).isValid() ), 2 );
462 newValues[i] = dst.at( i );
463 oldValues[i] = src.at( i );
468 std::unique_ptr<QgsVectorLayerToolsContext> context = std::make_unique<QgsVectorLayerToolsContext>();
470 context->setExpressionContext( &expressionContext );
473 if ( success && n > 0 )
500QgsFeature QgsAttributeForm::getUpdatedFeature()
const
512 QVariantList dstVars = QVariantList() << featureAttributes.at( eww->
fieldIdx() );
513 QVariantList srcVars = QVariantList() << eww->
value();
514 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
518 for (
const QString &fieldName : additionalFields )
522 dstVars << featureAttributes.at( idx );
526 Q_ASSERT( dstVars.count() == srcVars.count() );
528 for (
int i = 0; i < dstVars.count(); i++ )
530 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
531 featureAttributes[fieldIndexes[i]] = srcVars[i];
536 return updatedFeature;
539void QgsAttributeForm::updateValuesDependencies(
const int originIdx )
541 updateValuesDependenciesDefaultValues( originIdx );
542 updateValuesDependenciesVirtualFields( originIdx );
545void QgsAttributeForm::updateValuesDependenciesDefaultValues(
const int originIdx )
547 if ( !mDefaultValueDependencies.contains( originIdx ) )
555 QgsFeature updatedFeature = getUpdatedFeature();
558 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
575 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
587void QgsAttributeForm::updateValuesDependenciesVirtualFields(
const int originIdx )
589 if ( !mVirtualFieldsDependencies.contains( originIdx ) )
596 QgsFeature updatedFeature = getUpdatedFeature();
599 const QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
607 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
613 const QVariant value = exp.evaluate( &context );
619void QgsAttributeForm::updateValuesDependenciesParent()
622 QgsFeature updatedFeature = getUpdatedFeature();
623 QList<int> updatedFields;
626 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mParentDependencies;
630 if ( updatedFields.contains( eww->
fieldIdx() ) )
641void QgsAttributeForm::updateRelatedLayerFields()
644 updateRelatedLayerFieldsDependencies();
646 if ( mRelatedLayerFieldsDependencies.isEmpty() )
653 QgsFeature updatedFeature = getUpdatedFeature();
656 const QSet<QgsEditorWidgetWrapper *> relevantWidgets = mRelatedLayerFieldsDependencies;
660 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
666 QVariant value = exp.evaluate( &context );
671void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
676 mUnsavedMultiEditChanges =
false;
680void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
682 clearMultiEditMessages();
683 resetMultiEdit( link == QLatin1String(
"#apply" ) );
686void QgsAttributeForm::filterTriggered()
688 QString filter = createFilterExpression();
694void QgsAttributeForm::searchZoomTo()
696 QString filter = createFilterExpression();
697 if ( filter.isEmpty() )
703void QgsAttributeForm::searchFlash()
705 QString filter = createFilterExpression();
706 if ( filter.isEmpty() )
712void QgsAttributeForm::filterAndTriggered()
714 QString filter = createFilterExpression();
715 if ( filter.isEmpty() )
723void QgsAttributeForm::filterOrTriggered()
725 QString filter = createFilterExpression();
726 if ( filter.isEmpty() )
734void QgsAttributeForm::pushSelectedFeaturesMessage()
740 tr(
"%n matching feature(s) selected",
"matching features", count ),
746 tr(
"No matching features found" ),
760 QString filter = createFilterExpression();
761 if ( filter.isEmpty() )
765 pushSelectedFeaturesMessage();
770void QgsAttributeForm::searchSetSelection()
775void QgsAttributeForm::searchAddToSelection()
780void QgsAttributeForm::searchRemoveFromSelection()
785void QgsAttributeForm::searchIntersectSelection()
790bool QgsAttributeForm::saveMultiEdits()
794 const QList<int> fieldIndexes = mFormEditorWidgets.uniqueKeys();
795 mFormEditorWidgets.constBegin();
796 for (
int fieldIndex : fieldIndexes )
798 const QList<QgsAttributeFormEditorWidget *> widgets = mFormEditorWidgets.values( fieldIndex );
799 if ( !widgets.first()->hasChanged() )
802 if ( !widgets.first()->currentValue().isValid()
803 || !fieldIsEditable( fieldIndex ) )
810 widget->changesCommitted();
812 newAttributeValues.insert( fieldIndex, widgets.first()->currentValue() );
815 if ( newAttributeValues.isEmpty() )
823 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
824 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
825 if ( res != QMessageBox::Ok )
836 const auto constMultiEditFeatureIds = mMultiEditFeatureIds;
839 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
840 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
846 clearMultiEditMessages();
859 if ( !mButtonBox->isVisible() )
860 mMessageBar->
pushItem( mMultiEditMessageBarItem );
886 wrapper->notifyAboutToSave();
926 success = saveEdits( error );
930 success = saveMultiEdits();
935 mUnsavedMultiEditChanges =
false;
944 mValuesInitialized =
false;
945 const auto constMWidgets = mWidgets;
948 ww->setFeature( mFeature );
959 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
960 updateValuesDependenciesVirtualFields( eww->
fieldIdx() );
961 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
964 mValuesInitialized =
true;
970 const auto widgets { findChildren< QgsAttributeFormEditorWidget * >() };
977void QgsAttributeForm::clearMultiEditMessages()
979 if ( mMultiEditUnsavedMessageBarItem )
981 if ( !mButtonBox->isVisible() )
982 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
983 mMultiEditUnsavedMessageBarItem =
nullptr;
985 if ( mMultiEditMessageBarItem )
987 if ( !mButtonBox->isVisible() )
988 mMessageBar->
popWidget( mMultiEditMessageBarItem );
989 mMultiEditMessageBarItem =
nullptr;
993QString QgsAttributeForm::createFilterExpression()
const
998 QString filter = w->currentFilterExpression();
999 if ( !filter.isEmpty() )
1003 if ( filters.isEmpty() )
1006 QString filter = filters.join( QLatin1String(
") AND (" ) ).prepend(
'(' ).append(
')' );
1015 if ( mExtraContextScope )
1028void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
1033 bool signalEmitted =
false;
1035 if ( mValuesInitialized )
1042 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1045 if ( formEditorWidget->editorWidget() == eww )
1050 whileBlocking( formEditorWidget->editorWidget() )->setValue( value );
1067 for (
int i = 0; i < additionalFields.count(); i++ )
1069 const QString fieldName = additionalFields.at( i );
1070 const QVariant value = additionalFieldValues.at( i );
1074 signalEmitted =
true;
1076 if ( mValuesInitialized )
1077 updateJoinedFields( *eww );
1083 if ( !mIsSettingMultiEditFeatures )
1085 mUnsavedMultiEditChanges =
true;
1087 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
1088 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
1089 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1090 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
1091 clearMultiEditMessages();
1094 if ( !mButtonBox->isVisible() )
1095 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
1098 signalEmitted =
true;
1108 updateConstraints( eww );
1111 if ( mValuesInitialized )
1114 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
1115 updateValuesDependencies( eww->
fieldIdx() );
1116 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
1121 updateEditableState();
1123 if ( !signalEmitted )
1128 bool attributeHasChanged = !mIsSettingFeature;
1130 attributeHasChanged &= !mIsSettingMultiEditFeatures;
1136void QgsAttributeForm::updateAllConstraints()
1138 const auto constMWidgets = mWidgets;
1143 updateConstraints( eww );
1151 if ( currentFormValuesFeature( ft ) )
1163 updateConstraint( ft, eww );
1166 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
1169 updateConstraint( ft, depsEww );
1177 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
1178 for ( ContainerInformation *info : infos )
1180 info->apply( &context );
1185void QgsAttributeForm::updateContainersVisibility()
1189 const QVector<ContainerInformation *> infos = mContainerVisibilityCollapsedInformation;
1191 for ( ContainerInformation *info : infos )
1193 info->apply( &context );
1203 updateAllConstraints();
1219 if ( mJoinedFeatures.contains( info ) )
1236void QgsAttributeForm::updateLabels()
1238 if ( ! mLabelDataDefinedProperties.isEmpty() )
1241 if ( currentFormValuesFeature( currentFeature ) )
1245 for (
auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
1247 QLabel *label { it.key() };
1249 const QString value { it->valueAsString( context, QString(), &ok ) };
1250 if ( ok && ! value.isEmpty() )
1252 label->setText( value );
1259void QgsAttributeForm::updateEditableState()
1261 if ( ! mEditableDataDefinedProperties.isEmpty() )
1264 if ( currentFormValuesFeature( currentFeature ) )
1268 for (
auto it = mEditableDataDefinedProperties.constBegin() ; it != mEditableDataDefinedProperties.constEnd(); ++it )
1270 QWidget *w { it.key() };
1272 const bool isEditable { it->valueAsBool( context,
true, &ok ) && mLayer && mLayer->
isEditable() };
1282 w->setEnabled( isEditable );
1290bool QgsAttributeForm::currentFormValuesFeature(
QgsFeature &feature )
1303 if ( dst.count() > eww->
fieldIdx() )
1305 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1306 QVariantList srcVars = QVariantList() << eww->
value();
1307 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1311 for (
const QString &fieldName : additionalFields )
1314 fieldIndexes << idx;
1315 dstVars << dst.at( idx );
1319 Q_ASSERT( dstVars.count() == srcVars.count() );
1321 for (
int i = 0; i < dstVars.count(); i++ )
1327 dst[fieldIndexes[i]] = srcVars[i];
1344void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1346 mContainerVisibilityCollapsedInformation.append( info );
1348 const QSet<QString> referencedColumns = info->expression.referencedColumns().unite( info->collapsedExpression.referencedColumns() );
1350 for (
const QString &col : referencedColumns )
1352 mContainerInformationDependency[ col ].append( info );
1356bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1380bool QgsAttributeForm::currentFormValidHardConstraints( QStringList &invalidFields, QStringList &descriptions )
const
1401void QgsAttributeForm::onAttributeAdded(
int idx )
1403 mPreventFeatureRefresh =
false;
1415void QgsAttributeForm::onAttributeDeleted(
int idx )
1417 mPreventFeatureRefresh =
false;
1421 attrs.remove( idx );
1429void QgsAttributeForm::onRelatedFeaturesChanged()
1431 updateRelatedLayerFields();
1434void QgsAttributeForm::onUpdatedFields()
1436 mPreventFeatureRefresh =
false;
1463void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1469 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1473 formEditorWidget->setConstraintStatus( constraint, description, err, result );
1474 if ( formEditorWidget->editorWidget() != eww )
1476 formEditorWidget->editorWidget()->updateConstraint( result, err );
1483 QList<QgsEditorWidgetWrapper *> wDeps;
1495 if ( name != ewwName )
1502 for (
const QString &colName : referencedColumns )
1504 if ( name == colName )
1506 wDeps.append( eww );
1519 return setupRelationWidgetWrapper( QString(), rel, context );
1532void QgsAttributeForm::preventFeatureRefresh()
1534 mPreventFeatureRefresh =
true;
1560 updateValuesDependenciesParent();
1574 return mNeedsGeometry;
1577void QgsAttributeForm::synchronizeState()
1579 bool isEditable = ( mFeature.
isValid()
1589 const QList<QgsAttributeFormEditorWidget *> formWidgets = mFormEditorWidgets.values( eww->
fieldIdx() );
1592 formWidget->setConstraintResultVisible( isEditable );
1596 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1597 ww->setEnabled( enabled );
1603 ww->setEnabled( isEditable );
1614 if ( mConstraintsFailMessageBarItem )
1616 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1619 mMessageBar->
pushItem( mConstraintsFailMessageBarItem );
1623 QStringList invalidFields, descriptions;
1624 mValidConstraints = currentFormValidHardConstraints( invalidFields, descriptions );
1628 if ( !mValidConstraints && !mConstraintsFailMessageBarItem )
1630 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 );
1631 mMessageBar->
pushItem( mConstraintsFailMessageBarItem );
1633 else if ( mValidConstraints && mConstraintsFailMessageBarItem )
1635 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1636 mConstraintsFailMessageBarItem =
nullptr;
1639 else if ( mConstraintsFailMessageBarItem )
1641 mMessageBar->
popWidget( mConstraintsFailMessageBarItem );
1642 mConstraintsFailMessageBarItem =
nullptr;
1645 isEditable = isEditable & mValidConstraints;
1650 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1652 okButton->setEnabled( isEditable );
1655void QgsAttributeForm::init()
1657 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1660 QWidget *formWidget =
nullptr;
1661 mNeedsGeometry =
false;
1663 bool buttonBoxVisible =
true;
1667 buttonBoxVisible = mButtonBox->isVisible();
1669 mButtonBox =
nullptr;
1672 if ( mSearchButtonBox )
1674 delete mSearchButtonBox;
1675 mSearchButtonBox =
nullptr;
1678 qDeleteAll( mWidgets );
1681 while ( QWidget *w = this->findChild<QWidget *>() )
1687 QVBoxLayout *vl =
new QVBoxLayout();
1688 vl->setContentsMargins( 0, 0, 0, 0 );
1690 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1691 vl->addWidget( mMessageBar );
1696 QGridLayout *layout =
new QGridLayout();
1697 QWidget *container =
new QWidget();
1698 container->setLayout( layout );
1699 vl->addWidget( container );
1701 mFormEditorWidgets.clear();
1702 mFormWidgets.clear();
1705 setContentsMargins( 0, 0, 0, 0 );
1714 if ( file && file->open( QFile::ReadOnly ) )
1718 QFileInfo fi( file->fileName() );
1719 loader.setWorkingDirectory( fi.dir() );
1720 formWidget = loader.load( file,
this );
1723 formWidget->setWindowFlags( Qt::Widget );
1724 layout->addWidget( formWidget );
1727 mButtonBox = findChild<QDialogButtonBox *>();
1730 formWidget->installEventFilter(
this );
1742 int columnCount = 1;
1743 bool hasRootFields =
false;
1744 bool addSpacer =
true;
1753 if ( !containerDef )
1756 switch ( containerDef->
type() )
1760 tabWidget =
nullptr;
1761 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1762 if ( widgetInfo.labelStyle.overrideColor )
1764 if ( widgetInfo.labelStyle.color.isValid() )
1766 widgetInfo.widget->setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1769 if ( widgetInfo.labelStyle.overrideFont )
1771 widgetInfo.widget->setFont( widgetInfo.labelStyle.font );
1774 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1775 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1777 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1779 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1781 layout->setRowStretch( row, widgDef->verticalStretch() );
1795 tabWidget =
nullptr;
1796 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1797 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1798 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1800 layout->setRowStretch( row, widgDef->verticalStretch() );
1803 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1805 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1821 layout->addWidget( tabWidget, row, column, 1, 2 );
1825 QWidget *tabPage =
new QWidget( tabWidget );
1827 tabWidget->addTab( tabPage, widgDef->name() );
1828 tabWidget->
setTabStyle( tabWidget->tabBar()->count() - 1, widgDef->labelStyle() );
1832 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1834 QGridLayout *tabPageLayout =
new QGridLayout();
1835 tabPage->setLayout( tabPageLayout );
1837 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1838 tabPageLayout->addWidget( widgetInfo.widget );
1845 hasRootFields =
true;
1846 tabWidget =
nullptr;
1847 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1850 if ( widgetInfo.showLabel )
1852 if ( widgetInfo.labelStyle.overrideColor && widgetInfo.labelStyle.color.isValid() )
1854 collapsibleGroupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1857 if ( widgetInfo.labelStyle.overrideFont )
1859 collapsibleGroupBox->setFont( widgetInfo.labelStyle.font );
1862 collapsibleGroupBox->setTitle( widgetInfo.labelText );
1865 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
1866 collapsibleGroupBoxLayout->addWidget( widgetInfo.widget );
1867 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
1869 QVBoxLayout *
c =
new QVBoxLayout();
1870 c->addWidget( collapsibleGroupBox );
1871 layout->addLayout(
c, row, column, 1, 2 );
1873 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1874 layout->setRowStretch( row, widgDef->verticalStretch() );
1875 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1876 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1885 hasRootFields =
true;
1886 tabWidget =
nullptr;
1887 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1888 QLabel *label =
new QLabel( widgetInfo.labelText );
1890 if ( widgetInfo.labelStyle.overrideColor )
1892 if ( widgetInfo.labelStyle.color.isValid() )
1894 label->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
1898 if ( widgetInfo.labelStyle.overrideFont )
1900 label->setFont( widgetInfo.labelStyle.font );
1903 label->setToolTip( widgetInfo.toolTip );
1904 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1906 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1909 label->setBuddy( widgetInfo.widget );
1912 if ( widgetInfo.widget
1913 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
1914 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
1915 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
1918 if ( !widgetInfo.showLabel )
1920 QVBoxLayout *
c =
new QVBoxLayout();
1921 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1922 c->addWidget( widgetInfo.widget );
1923 layout->addLayout(
c, row, column, 1, 2 );
1925 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1927 layout->setRowStretch( row, widgDef->verticalStretch() );
1930 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1932 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1937 else if ( widgetInfo.labelOnTop )
1939 QVBoxLayout *
c =
new QVBoxLayout();
1940 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1941 c->addWidget( label );
1942 c->addWidget( widgetInfo.widget );
1943 layout->addLayout(
c, row, column, 1, 2 );
1945 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1947 layout->setRowStretch( row, widgDef->verticalStretch() );
1950 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( column + 1 ) )
1952 layout->setColumnStretch( column + 1, widgDef->horizontalStretch() );
1959 const int widgetColumn = column + 1;
1960 layout->addWidget( label, row, column++ );
1961 layout->addWidget( widgetInfo.widget, row, column++ );
1963 if ( widgDef->verticalStretch() > 0 && widgDef->verticalStretch() > layout->rowStretch( row ) )
1965 layout->setRowStretch( row, widgDef->verticalStretch() );
1968 if ( widgDef->horizontalStretch() > 0 && widgDef->horizontalStretch() > layout->columnStretch( widgetColumn ) )
1970 layout->setColumnStretch( widgetColumn, widgDef->horizontalStretch() );
1978 const int fieldIdx = fieldElement->
idx();
1979 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
1981 const QString fieldName { mLayer->
fields().
at( fieldIdx ).
name() };
1985 if ( property.isActive() )
1987 mLabelDataDefinedProperties[ label ] = property;
1993 if ( property.isActive() )
1995 mEditableDataDefinedProperties[ widgetInfo.widget ] = property;
2002 if ( column >= columnCount * 2 )
2009 if ( hasRootFields && addSpacer )
2011 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2012 layout->addItem( spacerItem, row, 0 );
2013 layout->setRowStretch( row, 1 );
2016 formWidget = container;
2025 formWidget =
new QWidget(
this );
2026 QGridLayout *gridLayout =
new QGridLayout( formWidget );
2027 formWidget->setLayout( gridLayout );
2033 scrollArea->setWidget( formWidget );
2034 scrollArea->setWidgetResizable(
true );
2035 scrollArea->setFrameShape( QFrame::NoFrame );
2036 scrollArea->setFrameShadow( QFrame::Plain );
2037 scrollArea->setFocusProxy(
this );
2038 layout->addWidget( scrollArea );
2042 layout->addWidget( formWidget );
2049 for (
const QgsField &field : fields )
2057 QString labelText = fieldName;
2058 labelText.replace(
'&', QLatin1String(
"&&" ) );
2062 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
2068 QLabel *label =
new QLabel( labelText );
2070 QSvgWidget *i =
new QSvgWidget();
2071 i->setFixedSize( 18, 18 );
2076 if ( property.isActive() )
2078 mLabelDataDefinedProperties[ label ] = property;
2084 QWidget *w =
nullptr;
2089 mFormEditorWidgets.insert( idx, formWidget );
2090 mFormWidgets.append( formWidget );
2093 label->setBuddy( eww->
widget() );
2098 if ( property.isActive() )
2100 mEditableDataDefinedProperties[ formWidget ] = property;
2106 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() ) ) );
2111 w->setObjectName( field.name() );
2115 mWidgets.append( eww );
2116 mIconMap[eww->
widget()] = i;
2121 gridLayout->addWidget( label, row++, 0, 1, 2 );
2122 gridLayout->addWidget( w, row++, 0, 1, 2 );
2123 gridLayout->addWidget( i, row++, 0, 1, 2 );
2127 gridLayout->addWidget( label, row, 0 );
2128 gridLayout->addWidget( w, row, 1 );
2129 gridLayout->addWidget( i, row++, 2 );
2143 QVBoxLayout *collapsibleGroupBoxLayout =
new QVBoxLayout();
2144 collapsibleGroupBoxLayout->addWidget( formWidget );
2145 collapsibleGroupBox->setLayout( collapsibleGroupBoxLayout );
2147 gridLayout->addWidget( collapsibleGroupBox, row++, 0, 1, 2 );
2149 mWidgets.append( rww );
2150 mFormWidgets.append( formWidget );
2155 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
2156 gridLayout->addItem( spacerItem, row, 0 );
2157 gridLayout->setRowStretch( row, 1 );
2163 updateFieldDependencies();
2167 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
2168 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
2169 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2171 mButtonBox->setVisible( buttonBoxVisible );
2173 if ( !mSearchButtonBox )
2175 mSearchButtonBox =
new QWidget();
2176 QHBoxLayout *boxLayout =
new QHBoxLayout();
2177 boxLayout->setContentsMargins( 0, 0, 0, 0 );
2178 mSearchButtonBox->setLayout( boxLayout );
2179 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
2181 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
2183 boxLayout->addWidget( clearButton );
2184 boxLayout->addStretch( 1 );
2186 QPushButton *flashButton =
new QPushButton();
2187 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2188 flashButton->setText( tr(
"&Flash Features" ) );
2189 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
2190 boxLayout->addWidget( flashButton );
2192 QPushButton *openAttributeTableButton =
new QPushButton();
2193 openAttributeTableButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2194 openAttributeTableButton->setText( tr(
"Show in &Table" ) );
2195 openAttributeTableButton->setToolTip( tr(
"Open the attribute table editor with the filtered features" ) );
2196 connect( openAttributeTableButton, &QToolButton::clicked,
this, [ = ]
2200 boxLayout->addWidget( openAttributeTableButton );
2202 QPushButton *zoomButton =
new QPushButton();
2203 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2204 zoomButton->setText( tr(
"&Zoom to Features" ) );
2205 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
2206 boxLayout->addWidget( zoomButton );
2208 QToolButton *selectButton =
new QToolButton();
2209 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2210 selectButton->setText( tr(
"&Select Features" ) );
2212 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
2213 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
2214 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
2215 QMenu *selectMenu =
new QMenu( selectButton );
2216 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
2218 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
2219 selectMenu->addAction( selectAction );
2220 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
2222 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
2223 selectMenu->addAction( addSelectAction );
2224 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
2226 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
2227 selectMenu->addAction( deselectAction );
2228 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
2230 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
2231 selectMenu->addAction( filterSelectAction );
2232 selectButton->setMenu( selectMenu );
2233 boxLayout->addWidget( selectButton );
2237 QToolButton *filterButton =
new QToolButton();
2238 filterButton->setText( tr(
"Filter Features" ) );
2239 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
2240 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
2241 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
2242 QMenu *filterMenu =
new QMenu( filterButton );
2243 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
2244 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
2245 filterMenu->addAction( filterAndAction );
2246 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
2247 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
2248 filterMenu->addAction( filterOrAction );
2249 filterButton->setMenu( filterMenu );
2250 boxLayout->addWidget( filterButton );
2254 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
2256 closeButton->setShortcut( Qt::Key_Escape );
2257 boxLayout->addWidget( closeButton );
2260 layout->addWidget( mSearchButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
2278 const auto constMInterfaces = mInterfaces;
2289 QApplication::restoreOverrideCursor();
2292void QgsAttributeForm::cleanPython()
2294 if ( !mPyFormVarName.isNull() )
2296 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
2301void QgsAttributeForm::initPython()
2318 if ( !initFilePath.isEmpty() )
2322 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
2325 QTextStream inf( inputFile );
2326#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2327 inf.setCodec(
"UTF-8" );
2329 initCode = inf.readAll();
2334 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
2345 if ( initCode.isEmpty() )
2347 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
2358 if ( !initCode.isEmpty() )
2364 tr(
"Python macro could not be run due to missing permissions." ),
2372 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
2374 static int sFormId = 0;
2375 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
2377 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
2378 .arg( mPyFormVarName )
2379 .arg( ( quint64 )
this );
2383 QgsDebugMsgLevel( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ), 2 );
2386 if ( numArgs == QLatin1String(
"3" ) )
2394 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 ) );
2397 QString expr = QString(
"%1(%2)" )
2398 .arg( mLayer->editFormInit() )
2399 .arg( mPyFormVarName );
2400 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
2410 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 ) );
2418 WidgetInfo newWidgetInfo;
2420 newWidgetInfo.labelStyle = widgetDef->
labelStyle();
2422 switch ( widgetDef->
type() )
2434 mWidgets.append( actionWrapper );
2435 newWidgetInfo.widget = actionWrapper->
widget();
2436 newWidgetInfo.showLabel =
false;
2449 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2455 mFormEditorWidgets.insert( fldIdx, formWidget );
2456 mFormWidgets.append( formWidget );
2460 newWidgetInfo.widget = formWidget;
2461 mWidgets.append( eww );
2463 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
2464 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
2469 newWidgetInfo.labelText.replace(
'&', QLatin1String(
"&&" ) );
2470 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
2471 newWidgetInfo.showLabel = widgetDef->
showLabel();
2492 mWidgets.append( rww );
2493 mFormWidgets.append( formWidget );
2495 newWidgetInfo.widget = formWidget;
2496 newWidgetInfo.showLabel = relDef->
showLabel();
2497 newWidgetInfo.labelText = relDef->
label();
2498 if ( newWidgetInfo.labelText.isEmpty() )
2500 newWidgetInfo.labelOnTop =
true;
2512 if ( columnCount <= 0 )
2516 QWidget *myContainer =
nullptr;
2517 bool removeLayoutMargin =
false;
2518 switch ( container->
type() )
2523 widgetName = QStringLiteral(
"QGroupBox" );
2526 groupBox->setTitle( container->
name() );
2527 if ( newWidgetInfo.labelStyle.overrideColor )
2529 if ( newWidgetInfo.labelStyle.color.isValid() )
2531 groupBox->
setStyleSheet( QStringLiteral(
"QGroupBox::title { color: %1; }" ).arg( newWidgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2534 if ( newWidgetInfo.labelStyle.overrideFont )
2536 groupBox->setFont( newWidgetInfo.labelStyle.font );
2539 myContainer = groupBox;
2540 newWidgetInfo.widget = myContainer;
2547 QWidget *rowWidget =
new QWidget();
2548 widgetName = QStringLiteral(
"Row" );
2549 myContainer = rowWidget;
2550 newWidgetInfo.widget = myContainer;
2551 removeLayoutMargin =
true;
2552 columnCount = container->
children().size();
2558 myContainer =
new QWidget();
2562 scrollArea->setWidget( myContainer );
2563 scrollArea->setWidgetResizable(
true );
2564 scrollArea->setFrameShape( QFrame::NoFrame );
2565 widgetName = QStringLiteral(
"QScrollArea QWidget" );
2567 newWidgetInfo.widget = scrollArea;
2574 QString style {QStringLiteral(
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
2575 newWidgetInfo.widget->setStyleSheet( style );
2578 QGridLayout *gbLayout =
new QGridLayout();
2579 if ( removeLayoutMargin )
2580 gbLayout->setContentsMargins( 0, 0, 0, 0 );
2581 myContainer->setLayout( gbLayout );
2585 bool addSpacer =
true;
2587 const QList<QgsAttributeEditorElement *> children = container->
children();
2591 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2603 int widgetColumn = column;
2605 if ( widgetInfo.labelText.isNull() || ! widgetInfo.showLabel )
2607 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2608 widgetColumn = column + 1;
2613 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2615 if ( widgetInfo.labelStyle.overrideColor )
2617 if ( widgetInfo.labelStyle.color.isValid() )
2619 mypLabel->setStyleSheet( QStringLiteral(
"QLabel { color: %1; }" ).arg( widgetInfo.labelStyle.color.name( QColor::HexArgb ) ) );
2623 if ( widgetInfo.labelStyle.overrideFont )
2625 mypLabel->setFont( widgetInfo.labelStyle.font );
2633 const int fldIdx = fieldDef->
idx();
2634 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2636 const QString fieldName { fields.
at( fldIdx ).
name() };
2640 if ( property.isActive() )
2642 mLabelDataDefinedProperties[ mypLabel ] = property;
2648 if ( property.isActive() )
2650 mEditableDataDefinedProperties[ widgetInfo.widget ] = property;
2656 mypLabel->setToolTip( widgetInfo.toolTip );
2657 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2659 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2662 mypLabel->setBuddy( widgetInfo.widget );
2664 if ( widgetInfo.labelOnTop )
2666 widgetColumn = column + 1;
2667 QVBoxLayout *
c =
new QVBoxLayout();
2668 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2669 c->layout()->addWidget( mypLabel );
2670 c->layout()->addWidget( widgetInfo.widget );
2671 gbLayout->addLayout(
c, row, column, 1, 2 );
2676 widgetColumn = column + 1;
2677 gbLayout->addWidget( mypLabel, row, column++ );
2678 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2682 const int childHorizontalStretch = childDef->horizontalStretch();
2683 const int existingColumnStretch = gbLayout->columnStretch( widgetColumn );
2684 if ( childHorizontalStretch > 0 && childHorizontalStretch > existingColumnStretch )
2686 gbLayout->setColumnStretch( widgetColumn, childHorizontalStretch );
2689 if ( childDef->verticalStretch() > 0 && childDef->verticalStretch() > gbLayout->rowStretch( row ) )
2691 gbLayout->setRowStretch( row, childDef->verticalStretch() );
2694 if ( column >= columnCount * 2 )
2700 if ( widgetInfo.widget
2701 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Fixed
2702 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Maximum
2703 && widgetInfo.widget->sizePolicy().verticalPolicy() != QSizePolicy::Preferred )
2707 if ( qobject_cast<QgsAttributeFormRelationEditorWidget *>( widgetInfo.widget ) )
2713 QWidget *spacer =
new QWidget();
2714 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2715 gbLayout->addWidget( spacer, ++row, 0 );
2716 gbLayout->setRowStretch( row, 1 );
2719 newWidgetInfo.labelText = QString();
2720 newWidgetInfo.labelOnTop =
true;
2721 newWidgetInfo.showLabel = widgetDef->
showLabel();
2734 mWidgets.append( qmlWrapper );
2736 newWidgetInfo.widget = qmlWrapper->
widget();
2737 newWidgetInfo.labelText = elementDef->
name();
2738 newWidgetInfo.labelOnTop =
true;
2739 newWidgetInfo.showLabel = widgetDef->
showLabel();
2751 mWidgets.append( htmlWrapper );
2753 newWidgetInfo.widget = htmlWrapper->
widget();
2754 newWidgetInfo.labelText = elementDef->
name();
2755 newWidgetInfo.labelOnTop =
true;
2756 newWidgetInfo.showLabel = widgetDef->
showLabel();
2769 mWidgets.append( textWrapper );
2771 newWidgetInfo.widget = textWrapper->
widget();
2772 newWidgetInfo.labelText = elementDef->
name();
2773 newWidgetInfo.labelOnTop =
false;
2774 newWidgetInfo.showLabel = widgetDef->
showLabel();
2785 mWidgets.append( spacerWrapper );
2787 newWidgetInfo.widget = spacerWrapper->
widget();
2788 newWidgetInfo.labelOnTop =
false;
2789 newWidgetInfo.showLabel =
false;
2794 QgsDebugError( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
2798 return newWidgetInfo;
2801void QgsAttributeForm::createWrappers()
2803 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2804 const QList<QgsField> fields = mLayer->
fields().
toList();
2806 const auto constMyWidgets = myWidgets;
2807 for ( QWidget *myWidget : constMyWidgets )
2810 QVariant vRel = myWidget->property(
"qgisRelation" );
2811 if ( vRel.isValid() )
2821 mWidgets.append( rww );
2826 const auto constFields = fields;
2827 for (
const QgsField &field : constFields )
2829 if ( field.name() == myWidget->objectName() )
2834 mWidgets.append( eww );
2841void QgsAttributeForm::afterWidgetInit()
2843 bool isFirstEww =
true;
2845 const auto constMWidgets = mWidgets;
2854 setFocusProxy( eww->
widget() );
2864 if ( relationWidgetWrapper )
2877 if ( e->type() == QEvent::KeyPress )
2879 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
2880 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
2892 QSet< int > &mixedValueFields,
2893 QHash< int, QVariant > &fieldSharedValues )
const
2895 mixedValueFields.clear();
2896 fieldSharedValues.clear();
2902 for (
int i = 0; i < mLayer->
fields().count(); ++i )
2904 if ( mixedValueFields.contains( i ) )
2909 fieldSharedValues[i] = f.
attribute( i );
2913 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
2915 fieldSharedValues.remove( i );
2916 mixedValueFields.insert( i );
2922 if ( mixedValueFields.count() == mLayer->
fields().
count() )
2931void QgsAttributeForm::layerSelectionChanged()
2944 resetMultiEdit(
true );
2951 mIsSettingMultiEditFeatures =
true;
2952 mMultiEditFeatureIds = fids;
2954 if ( fids.isEmpty() )
2957 QMultiMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2958 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2960 wIt.value()->initialize( QVariant() );
2962 mIsSettingMultiEditFeatures =
false;
2969 QSet< int > mixedValueFields;
2970 QHash< int, QVariant > fieldSharedValues;
2971 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2980 if ( mCurrentFormFeature.
id() != firstFeature.
id( ) )
2985 const auto constMixedValueFields = mixedValueFields;
2986 for (
int fieldIndex : std::as_const( mixedValueFields ) )
2988 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( fieldIndex );
2989 if ( formEditorWidgets.isEmpty() )
2992 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
2993 QVariantList additionalFieldValues;
2994 for (
const QString &additionalField : additionalFields )
2995 additionalFieldValues << firstFeature.
attribute( additionalField );
2998 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
3000 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
3001 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
3003 const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( sharedValueIt.key() );
3004 if ( formEditorWidgets.isEmpty() )
3008 const QStringList additionalFields = formEditorWidgets.first()->editorWidget()->additionalFields();
3009 for (
const QString &additionalField : additionalFields )
3012 if ( constMixedValueFields.contains( index ) )
3019 QVariantList additionalFieldValues;
3022 for (
const QString &additionalField : additionalFields )
3023 additionalFieldValues << firstFeature.
attribute( additionalField );
3025 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
3029 for (
const QString &additionalField : additionalFields )
3032 Q_ASSERT( fieldSharedValues.contains( index ) );
3033 additionalFieldValues << fieldSharedValues.value( index );
3036 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
3040 setMultiEditFeatureIdsRelations( fids );
3042 mIsSettingMultiEditFeatures =
false;
3047 if ( mOwnsMessageBar )
3049 mOwnsMessageBar =
false;
3050 mMessageBar = messageBar;
3060 QStringList filters;
3063 QString filter = widget->currentFilterExpression();
3064 if ( !filter.isNull() )
3065 filters <<
'(' + filter +
')';
3068 return filters.join( QLatin1String(
" AND " ) );
3073 mExtraContextScope.reset( extraScope );
3079 const bool newVisibility = expression.evaluate( expressionContext ).toBool();
3081 if ( expression.isValid() && ! expression.hasEvalError() && newVisibility != isVisible )
3089 widget->setVisible( newVisibility );
3092 isVisible = newVisibility;
3095 const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool();
3097 if ( collapsedExpression.isValid() && ! collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed )
3102 collapsibleGroupBox->
setCollapsed( newCollapsedState );
3103 isCollapsed = newCollapsedState;
3117 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
3120 const QString hint = tr(
"No feature joined" );
3121 const auto constInfos = infos;
3124 if ( !info->isDynamicFormEnabled() )
3129 mJoinedFeatures[info] = joinFeature;
3131 if ( info->hasSubset() )
3135 const auto constSubsetNames = subsetNames;
3136 for (
const QString &field : constSubsetNames )
3138 QString prefixedName = info->prefixedFieldName( field );
3140 QString hintText = hint;
3154 for (
const QgsField &field : joinFields )
3156 QString prefixedName = info->prefixedFieldName( field );
3158 QString hintText = hint;
3172bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
3177void QgsAttributeForm::updateFieldDependencies()
3179 mDefaultValueDependencies.clear();
3180 mVirtualFieldsDependencies.clear();
3181 mRelatedLayerFieldsDependencies.clear();
3182 mParentDependencies.clear();
3191 updateFieldDependenciesParent( eww );
3192 updateFieldDependenciesDefaultValue( eww );
3193 updateFieldDependenciesVirtualFields( eww );
3194 updateRelatedLayerFieldsDependencies( eww );
3202 if ( exp.needsGeometry() )
3203 mNeedsGeometry =
true;
3210 for (
const int id : allAttributeIds )
3212 mDefaultValueDependencies.insertMulti(
id, eww );
3218 const QSet<QString> referencedColumns = exp.referencedColumns();
3219 for (
const QString &referencedColumn : referencedColumns )
3221 mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
3229 if ( expressionField.isEmpty() )
3234 if ( exp.needsGeometry() )
3235 mNeedsGeometry =
true;
3242 for (
const int id : allAttributeIds )
3244 mVirtualFieldsDependencies.insertMulti(
id, eww );
3250 const QSet<QString> referencedColumns = exp.referencedColumns();
3251 for (
const QString &referencedColumn : referencedColumns )
3253 mVirtualFieldsDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
3263 if ( expressionField.contains( QStringLiteral(
"relation_aggregate" ) )
3264 || expressionField.contains( QStringLiteral(
"get_features" ) ) )
3265 mRelatedLayerFieldsDependencies.insert( eww );
3269 mRelatedLayerFieldsDependencies.clear();
3274 if ( ! editorWidgetWrapper )
3277 updateRelatedLayerFieldsDependencies( editorWidgetWrapper );
3287 const QSet< QString > referencedVariablesAndFunctions = expression.referencedVariables() + expression.referencedFunctions();
3288 for (
const QString &referenced : referencedVariablesAndFunctions )
3290 if ( referenced.startsWith( QLatin1String(
"current_parent" ) ) )
3292 mParentDependencies.insert( eww );
3299void QgsAttributeForm::setMultiEditFeatureIdsRelations(
const QgsFeatureIds &fids )
3304 if ( !relationEditorWidget )
3317 mIconMap[eww->
widget()]->hide();
3331 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
3332 const QString tooltip = tr(
"Join settings do not allow editing" );
3333 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3337 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
3338 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
3339 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3343 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
3344 const QString tooltip = tr(
"Joined layer is not toggled editable" );
3345 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
3351void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
3354 sw->setToolTip( tooltip );
@ Row
A row of editors (horizontal layout)
@ File
Load the Python code from an external file.
@ Environment
Use the Python code available in the Python environment.
@ NoSource
Do not use Python code at all.
@ Dialog
Use the Python code provided in the dialog.
@ DragAndDrop
"Drag and drop" layout. Needs to be configured.
@ UiFile
Load a .ui file for the layout. Needs to be configured.
@ Warning
Warning message.
@ Info
Information message.
@ Success
Used for reporting a successful operation.
@ Join
Field originates from a joined layer.
@ Action
A layer action element.
@ QmlElement
A QML element.
@ HtmlElement
A HTML element.
@ TextElement
A text element.
@ SpacerElement
A spacer element.
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...
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 true if this group box is collapsed.
Qgis::AttributeEditorContainerType type() const
Returns the container type.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
QColor backgroundColor() const
Returns the background color of the container.
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.
QgsFeature parentFormFeature() const
Returns the feature of the currently edited parent form in its actual state.
@ Embed
A form was embedded as a widget on another form.
void setParentFormFeature(const QgsFeature &feature)
Sets the feature of the currently edited parent 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.
This is an abstract base class for any elements of a drag and drop form.
LabelStyle labelStyle() const
Returns the label style.
Qgis::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.
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.
An attribute editor widget that will represent a spacer.
bool drawLine() const
Returns true if the spacer element will contain an horizontal line.
An attribute editor widget that will represent arbitrary text code.
QString text() const
The Text that will be represented within this widget.
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 * parentFormScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current parent attribute form/tab...
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)
Fetch next feature and stores in f, returns true on success.
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...
Q_INVOKABLE 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.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
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.
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
Qgis::FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Q_INVOKABLE 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).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A geometry is the spatial representation of a feature.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
static bool pythonEmbeddedInProjectAllowed(void(*lambda)()=nullptr, QgsMessageBar *messageBar=nullptr, Qgis::PythonEmbeddedType embeddedType=Qgis::PythonEmbeddedType::Macro)
Returns true if python embedded in a project is currently allowed to be loaded.
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 final
Returns true if the collection contains a property with the specified key.
QgsProperty property(int key) const final
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.
Represents a relationship between two vector layers.
bool isInvalidJSON()
Returns whether the text edit widget contains Invalid JSON.
Wraps a label widget to display text.
bool needsGeometry() const
Returns true if the widget needs feature geometry.
void setText(const QString &text)
Sets the text code to htmlCode.
void reinitWidget()
Clears the content and makes new initialization.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
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.
Q_INVOKABLE bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false, QgsVectorLayerToolsContext *context=nullptr)
Changes an attribute value for a feature (but does not immediately commit the changes).
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
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 isEditable() const FINAL
Returns true if the provider is in editing mode.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
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.
Q_INVOKABLE bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap(), bool skipDefaultValues=false, QgsVectorLayerToolsContext *context=nullptr)
Changes attributes' values for a feature (but does not immediately commit the changes).
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
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
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)
#define QgsDebugError(str)