49 #include <QTextStream>
52 #include <QFormLayout>
53 #include <QGridLayout>
57 #include <QPushButton>
59 #include <QMessageBox>
60 #include <QToolButton>
63 int QgsAttributeForm::sFormCounter = 0;
68 , mOwnsMessageBar( true )
70 , mFormNr( sFormCounter++ )
72 , mPreventFeatureRefresh( false )
73 , mIsSettingMultiEditFeatures( false )
74 , mUnsavedMultiEditChanges( false )
75 , mEditCommandMessage( tr(
"Attributes changed" ) )
88 updateContainersVisibility();
95 qDeleteAll( mInterfaces );
122 mInterfaces.append( iface );
138 if ( mUnsavedMultiEditChanges )
141 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
142 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
143 if ( res == QMessageBox::Yes )
148 clearMultiEditMessages();
150 mUnsavedMultiEditChanges =
false;
198 w->setContext( newContext );
204 w->setVisible( relationWidgetsVisible );
211 mSearchButtonBox->setVisible(
false );
215 synchronizeEnabledState();
216 mSearchButtonBox->setVisible(
false );
220 resetMultiEdit(
false );
221 synchronizeEnabledState();
222 mSearchButtonBox->setVisible(
false );
226 mSearchButtonBox->setVisible(
true );
231 mSearchButtonBox->setVisible(
false );
237 mSearchButtonBox->setVisible(
false );
246 const auto constMWidgets = mWidgets;
261 QVariant mainValue = eww->
value();
263 additionalFieldValues[index] = value;
264 eww->
setValues( mainValue, additionalFieldValues );
273 mIsSettingFeature =
true;
284 synchronizeEnabledState();
288 mIsSettingFeature =
false;
289 const auto constMInterfaces = mInterfaces;
292 iface->featureChanged();
308 mIsSettingFeature =
false;
311 bool QgsAttributeForm::saveEdits()
314 bool changedLayer =
false;
319 bool doUpdate =
false;
340 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
341 QVariantList srcVars = QVariantList() << eww->
value();
342 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
346 for (
const QString &fieldName : additionalFields )
350 dstVars << dst.at( idx );
354 Q_ASSERT( dstVars.count() == srcVars.count() );
356 for (
int i = 0; i < dstVars.count(); i++ )
359 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
361 dst[fieldIndexes[i]] = srcVars[i];
371 const auto constMInterfaces = mInterfaces;
374 if ( !iface->acceptChanges( updatedFeature ) )
386 bool res = mLayer->
addFeature( updatedFeature );
405 for (
int i = 0; i < dst.count(); ++i )
408 || !dst.at( i ).isValid()
409 || !fieldIsEditable( i ) )
415 QgsDebugMsgLevel( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
416 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ), 2 );
417 QgsDebugMsgLevel( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
418 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ), 2 );
420 newValues[i] = dst.at( i );
421 oldValues[i] = src.at( i );
428 if ( success && n > 0 )
455 bool QgsAttributeForm::updateDefaultValues(
const int originIdx )
459 updateDefaultValueDependencies();
461 if ( !mDefaultValueDependencies.contains( originIdx ) )
474 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
475 QVariantList srcVars = QVariantList() << eww->
value();
476 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
480 for (
const QString &fieldName : additionalFields )
484 dstVars << dst.at( idx );
488 Q_ASSERT( dstVars.count() == srcVars.count() );
490 for (
int i = 0; i < dstVars.count(); i++ )
493 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() && fieldIsEditable( fieldIndexes[i] ) )
495 dst[fieldIndexes[i]] = srcVars[i];
503 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
516 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
527 void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
532 mUnsavedMultiEditChanges =
false;
536 void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
538 clearMultiEditMessages();
539 resetMultiEdit( link == QLatin1String(
"#apply" ) );
542 void QgsAttributeForm::filterTriggered()
544 QString filter = createFilterExpression();
550 void QgsAttributeForm::searchZoomTo()
552 QString filter = createFilterExpression();
553 if ( filter.isEmpty() )
559 void QgsAttributeForm::searchFlash()
561 QString filter = createFilterExpression();
562 if ( filter.isEmpty() )
568 void QgsAttributeForm::filterAndTriggered()
570 QString filter = createFilterExpression();
571 if ( filter.isEmpty() )
579 void QgsAttributeForm::filterOrTriggered()
581 QString filter = createFilterExpression();
582 if ( filter.isEmpty() )
590 void QgsAttributeForm::pushSelectedFeaturesMessage()
596 tr(
"%n matching feature(s) selected",
"matching features", count ),
603 tr(
"No matching features found" ),
619 QString filter = createFilterExpression();
620 if ( filter.isEmpty() )
624 pushSelectedFeaturesMessage();
629 void QgsAttributeForm::searchSetSelection()
634 void QgsAttributeForm::searchAddToSelection()
639 void QgsAttributeForm::searchRemoveFromSelection()
644 void QgsAttributeForm::searchIntersectSelection()
649 bool QgsAttributeForm::saveMultiEdits()
653 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
654 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
661 || !fieldIsEditable( wIt.key() ) )
669 newAttributeValues.insert( wIt.key(), w->
currentValue() );
672 if ( newAttributeValues.isEmpty() )
680 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
681 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
682 if ( res != QMessageBox::Ok )
693 const auto constMMultiEditFeatureIds = mMultiEditFeatureIds;
696 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
697 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
703 clearMultiEditMessages();
716 if ( !mButtonBox->isVisible() )
717 mMessageBar->
pushItem( mMultiEditMessageBarItem );
728 wrapper->notifyAboutToSave();
766 success = saveEdits();
770 success = saveMultiEdits();
775 mUnsavedMultiEditChanges =
false;
783 mValuesInitialized =
false;
784 const auto constMWidgets = mWidgets;
787 ww->setFeature( mFeature );
789 mValuesInitialized =
true;
795 const auto widgets { findChildren< QgsAttributeFormEditorWidget * >() };
802 void QgsAttributeForm::clearMultiEditMessages()
804 if ( mMultiEditUnsavedMessageBarItem )
806 if ( !mButtonBox->isVisible() )
807 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
808 mMultiEditUnsavedMessageBarItem =
nullptr;
810 if ( mMultiEditMessageBarItem )
812 if ( !mButtonBox->isVisible() )
813 mMessageBar->
popWidget( mMultiEditMessageBarItem );
814 mMultiEditMessageBarItem =
nullptr;
818 QString QgsAttributeForm::createFilterExpression()
const
824 if ( !filter.isEmpty() )
828 if ( filters.isEmpty() )
831 QString filter = filters.join( QStringLiteral(
") AND (" ) ).prepend(
'(' ).append(
')' );
836 void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
841 bool signalEmitted =
false;
843 if ( mValuesInitialized )
859 for (
int i = 0; i < additionalFields.count(); i++ )
861 const QString fieldName = additionalFields.at( i );
862 const QVariant value = additionalFieldValues.at( i );
866 signalEmitted =
true;
868 updateJoinedFields( *eww );
874 if ( !mIsSettingMultiEditFeatures )
876 mUnsavedMultiEditChanges =
true;
878 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
879 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
880 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
881 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
882 clearMultiEditMessages();
885 if ( !mButtonBox->isVisible() )
886 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
896 updateConstraints( eww );
899 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
900 updateDefaultValues( eww->
fieldIdx() );
901 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
906 if ( !signalEmitted )
915 void QgsAttributeForm::updateAllConstraints()
917 const auto constMWidgets = mWidgets;
922 updateConstraints( eww );
930 if ( currentFormFeature( ft ) )
942 updateConstraint( ft, eww );
945 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
948 updateConstraint( ft, depsEww );
951 synchronizeEnabledState();
959 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
960 for ( ContainerInformation *info : infos )
962 info->apply( &context );
967 void QgsAttributeForm::updateContainersVisibility()
974 const QVector<ContainerInformation *> infos = mContainerVisibilityInformation;
976 for ( ContainerInformation *info : infos )
978 info->apply( &context );
982 updateAllConstraints();
996 if ( mJoinedFeatures.contains( info ) )
1013 void QgsAttributeForm::updateLabels()
1015 if ( ! mLabelDataDefinedProperties.isEmpty() )
1018 if ( currentFormFeature( currentFeature ) )
1025 for (
auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
1027 QLabel *label { it.key() };
1029 const QString value { it->valueAsString( context, QString(), &ok ) };
1030 if ( ok && ! value.isEmpty() )
1032 label->setText( value );
1039 bool QgsAttributeForm::currentFormFeature(
QgsFeature &feature )
1052 if ( dst.count() > eww->
fieldIdx() )
1054 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1055 QVariantList srcVars = QVariantList() << eww->
value();
1056 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1060 for (
const QString &fieldName : additionalFields )
1063 fieldIndexes << idx;
1064 dstVars << dst.at( idx );
1068 Q_ASSERT( dstVars.count() == srcVars.count() );
1070 for (
int i = 0; i < dstVars.count(); i++ )
1074 if ( ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) || dstVars[i].isNull() != srcVars[i].isNull() ) && srcVars[i].isValid() )
1076 dst[fieldIndexes[i]] = srcVars[i];
1093 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1095 mContainerVisibilityInformation.append( info );
1097 const QSet<QString> referencedColumns = info->expression.referencedColumns();
1099 for (
const QString &col : referencedColumns )
1101 mContainerInformationDependency[ col ].append( info );
1105 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
1129 void QgsAttributeForm::onAttributeAdded(
int idx )
1131 mPreventFeatureRefresh =
false;
1135 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
1143 void QgsAttributeForm::onAttributeDeleted(
int idx )
1145 mPreventFeatureRefresh =
false;
1149 attrs.remove( idx );
1157 void QgsAttributeForm::onUpdatedFields()
1159 mPreventFeatureRefresh =
false;
1176 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
1186 void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1194 if ( formEditorWidget )
1200 QList<QgsEditorWidgetWrapper *> wDeps;
1212 if ( name != ewwName )
1219 for (
const QString &colName : referencedColumns )
1221 if ( name == colName )
1223 wDeps.append( eww );
1244 void QgsAttributeForm::preventFeatureRefresh()
1246 mPreventFeatureRefresh =
true;
1275 void QgsAttributeForm::synchronizeEnabledState()
1277 bool isEditable = ( mFeature.
isValid()
1293 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1294 ww->setEnabled( enabled );
1302 QStringList invalidFields, descriptions;
1303 bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
1305 isEditable = isEditable & validConstraint;
1309 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1311 okButton->setEnabled( isEditable );
1314 void QgsAttributeForm::init()
1316 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1319 QWidget *formWidget =
nullptr;
1321 bool buttonBoxVisible =
true;
1325 buttonBoxVisible = mButtonBox->isVisible();
1327 mButtonBox =
nullptr;
1330 if ( mSearchButtonBox )
1332 delete mSearchButtonBox;
1333 mSearchButtonBox =
nullptr;
1336 qDeleteAll( mWidgets );
1339 while ( QWidget *w = this->findChild<QWidget *>() )
1345 QVBoxLayout *vl =
new QVBoxLayout();
1347 vl->setContentsMargins( 0, 0, 0, 0 );
1349 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1350 vl->addWidget( mMessageBar );
1355 QGridLayout *layout =
new QGridLayout();
1356 QWidget *container =
new QWidget();
1357 container->setLayout( layout );
1358 vl->addWidget( container );
1360 mFormEditorWidgets.clear();
1361 mFormWidgets.clear();
1364 setContentsMargins( 0, 0, 0, 0 );
1373 if ( file && file->open( QFile::ReadOnly ) )
1377 QFileInfo fi( file->fileName() );
1378 loader.setWorkingDirectory( fi.dir() );
1379 formWidget = loader.load( file,
this );
1382 formWidget->setWindowFlags( Qt::Widget );
1383 layout->addWidget( formWidget );
1386 mButtonBox = findChild<QDialogButtonBox *>();
1389 formWidget->installEventFilter(
this );
1401 int columnCount = 1;
1410 if ( !containerDef )
1415 tabWidget =
nullptr;
1416 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1417 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1420 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1429 layout->addWidget( tabWidget, row, column, 1, 2 );
1433 QWidget *tabPage =
new QWidget( tabWidget );
1435 tabWidget->addTab( tabPage, widgDef->name() );
1439 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1441 QGridLayout *tabPageLayout =
new QGridLayout();
1442 tabPage->setLayout( tabPageLayout );
1444 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1445 tabPageLayout->addWidget( widgetInfo.widget );
1450 tabWidget =
nullptr;
1451 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1452 QLabel *label =
new QLabel( widgetInfo.labelText );
1453 label->setToolTip( widgetInfo.toolTip );
1454 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1456 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1459 label->setBuddy( widgetInfo.widget );
1461 if ( !widgetInfo.showLabel )
1463 QVBoxLayout *
c =
new QVBoxLayout();
1464 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1465 c->addWidget( widgetInfo.widget );
1466 layout->addLayout(
c, row, column, 1, 2 );
1469 else if ( widgetInfo.labelOnTop )
1471 QVBoxLayout *
c =
new QVBoxLayout();
1472 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1473 c->addWidget( label );
1474 c->addWidget( widgetInfo.widget );
1475 layout->addLayout(
c, row, column, 1, 2 );
1480 layout->addWidget( label, row, column++ );
1481 layout->addWidget( widgetInfo.widget, row, column++ );
1485 if ( widgDef->type() == QgsAttributeEditorElement::AttributeEditorType::AeTypeField )
1488 const int fieldIdx = fieldElement->
idx();
1489 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
1491 const QString fieldName { mLayer->
fields().
at( fieldIdx ).
name() };
1495 if ( property.isActive() && !
property.expressionString().isEmpty() )
1497 mLabelDataDefinedProperties[ label ] = property;
1504 if ( column >= columnCount * 2 )
1510 formWidget = container;
1519 formWidget =
new QWidget(
this );
1520 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1521 formWidget->setLayout( gridLayout );
1527 scrollArea->setWidget( formWidget );
1528 scrollArea->setWidgetResizable(
true );
1529 scrollArea->setFrameShape( QFrame::NoFrame );
1530 scrollArea->setFrameShadow( QFrame::Plain );
1531 scrollArea->setFocusProxy(
this );
1532 layout->addWidget( scrollArea );
1536 layout->addWidget( formWidget );
1543 for (
const QgsField &field : fields )
1551 QString labelText = fieldName;
1552 labelText.replace(
'&', QStringLiteral(
"&&" ) );
1556 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1562 QLabel *label =
new QLabel( labelText );
1564 QSvgWidget *i =
new QSvgWidget();
1565 i->setFixedSize( 18, 18 );
1570 if ( property.isActive() && ! property.expressionString().isEmpty() )
1572 mLabelDataDefinedProperties[ label ] = property;
1578 QWidget *w =
nullptr;
1583 mFormEditorWidgets.insert( idx, formWidget );
1584 mFormWidgets.append( formWidget );
1587 label->setBuddy( eww->
widget() );
1591 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() ) ) );
1596 w->setObjectName( field.name() );
1600 addWidgetWrapper( eww );
1601 mIconMap[eww->
widget()] = i;
1606 gridLayout->addWidget( label, row++, 0, 1, 2 );
1607 gridLayout->addWidget( w, row++, 0, 1, 2 );
1608 gridLayout->addWidget( i, row++, 0, 1, 2 );
1612 gridLayout->addWidget( label, row, 0 );
1613 gridLayout->addWidget( w, row, 1 );
1614 gridLayout->addWidget( i, row++, 2 );
1626 gridLayout->addWidget( formWidget, row++, 0, 1, 2 );
1628 mWidgets.append( rww );
1629 mFormWidgets.append( formWidget );
1634 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1635 gridLayout->addItem( spacerItem, row, 0 );
1636 gridLayout->setRowStretch( row, 1 );
1641 updateDefaultValueDependencies();
1645 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1646 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1647 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1649 mButtonBox->setVisible( buttonBoxVisible );
1651 if ( !mSearchButtonBox )
1653 mSearchButtonBox =
new QWidget();
1654 QHBoxLayout *boxLayout =
new QHBoxLayout();
1655 boxLayout->setMargin( 0 );
1656 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1657 mSearchButtonBox->setLayout( boxLayout );
1658 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1660 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
1662 boxLayout->addWidget( clearButton );
1663 boxLayout->addStretch( 1 );
1665 QPushButton *flashButton =
new QPushButton();
1666 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1667 flashButton->setText( tr(
"&Flash Features" ) );
1668 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1669 boxLayout->addWidget( flashButton );
1671 QPushButton *zoomButton =
new QPushButton();
1672 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1673 zoomButton->setText( tr(
"&Zoom to Features" ) );
1674 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
1675 boxLayout->addWidget( zoomButton );
1677 QToolButton *selectButton =
new QToolButton();
1678 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1679 selectButton->setText( tr(
"&Select Features" ) );
1681 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1682 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1683 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
1684 QMenu *selectMenu =
new QMenu( selectButton );
1685 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
1687 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
1688 selectMenu->addAction( selectAction );
1689 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
1691 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
1692 selectMenu->addAction( addSelectAction );
1693 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
1695 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
1696 selectMenu->addAction( deselectAction );
1697 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
1699 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
1700 selectMenu->addAction( filterSelectAction );
1701 selectButton->setMenu( selectMenu );
1702 boxLayout->addWidget( selectButton );
1706 QToolButton *filterButton =
new QToolButton();
1707 filterButton->setText( tr(
"Filter Features" ) );
1708 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
1709 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1710 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
1711 QMenu *filterMenu =
new QMenu( filterButton );
1712 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
1713 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
1714 filterMenu->addAction( filterAndAction );
1715 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
1716 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
1717 filterMenu->addAction( filterOrAction );
1718 filterButton->setMenu( filterMenu );
1719 boxLayout->addWidget( filterButton );
1723 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
1725 closeButton->setShortcut( Qt::Key_Escape );
1726 boxLayout->addWidget( closeButton );
1729 layout->addWidget( mSearchButtonBox );
1747 const auto constMInterfaces = mInterfaces;
1758 QApplication::restoreOverrideCursor();
1761 void QgsAttributeForm::cleanPython()
1763 if ( !mPyFormVarName.isNull() )
1765 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
1770 void QgsAttributeForm::initPython()
1787 if ( !initFilePath.isEmpty() )
1791 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
1794 QTextStream inf( inputFile );
1795 initCode = inf.readAll();
1800 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
1811 if ( initCode.isEmpty() )
1813 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
1824 if ( !initCode.isEmpty() )
1830 tr(
"Python macro could not be run due to missing permissions." ),
1831 Qgis::MessageLevel::Warning,
1839 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
1841 static int sFormId = 0;
1842 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
1844 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
1845 .arg( mPyFormVarName )
1846 .arg( ( quint64 )
this );
1850 QgsDebugMsg( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
1853 if ( numArgs == QLatin1String(
"3" ) )
1861 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 ) );
1864 QString expr = QString(
"%1(%2)" )
1865 .arg( mLayer->editFormInit() )
1866 .arg( mPyFormVarName );
1867 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
1877 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 ) );
1885 WidgetInfo newWidgetInfo;
1887 switch ( widgetDef->
type() )
1897 if ( fldIdx < fields.
count() && fldIdx >= 0 )
1903 mFormEditorWidgets.insert( fldIdx, formWidget );
1904 mFormWidgets.append( formWidget );
1908 newWidgetInfo.widget = formWidget;
1909 addWidgetWrapper( eww );
1911 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
1912 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
1917 newWidgetInfo.labelText.replace(
'&', QStringLiteral(
"&&" ) );
1918 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
1919 newWidgetInfo.showLabel = widgetDef->
showLabel();
1941 mWidgets.append( rww );
1942 mFormWidgets.append( formWidget );
1944 newWidgetInfo.widget = formWidget;
1945 newWidgetInfo.labelText = QString();
1946 newWidgetInfo.labelOnTop =
true;
1958 if ( columnCount <= 0 )
1962 QWidget *myContainer =
nullptr;
1965 QGroupBox *groupBox =
new QGroupBox( parent );
1966 widgetName = QStringLiteral(
"QGroupBox" );
1968 groupBox->setTitle( container->
name() );
1969 myContainer = groupBox;
1970 newWidgetInfo.widget = myContainer;
1974 myContainer =
new QWidget();
1980 scrollArea->setWidget( myContainer );
1981 scrollArea->setWidgetResizable(
true );
1982 scrollArea->setFrameShape( QFrame::NoFrame );
1983 widgetName = QStringLiteral(
"QScrollArea QWidget" );
1985 newWidgetInfo.widget = scrollArea;
1989 newWidgetInfo.widget = myContainer;
1990 widgetName = QStringLiteral(
"QWidget" );
1996 QString style {QStringLiteral(
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
1997 newWidgetInfo.widget->setStyleSheet( style );
2000 QGridLayout *gbLayout =
new QGridLayout();
2001 myContainer->setLayout( gbLayout );
2006 const QList<QgsAttributeEditorElement *> children = container->
children();
2010 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2017 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
2021 if ( widgetInfo.labelText.isNull() )
2023 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2028 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2029 mypLabel->setToolTip( widgetInfo.toolTip );
2030 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2032 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2035 mypLabel->setBuddy( widgetInfo.widget );
2037 if ( widgetInfo.labelOnTop )
2039 QVBoxLayout *
c =
new QVBoxLayout();
2040 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2041 c->layout()->addWidget( mypLabel );
2042 c->layout()->addWidget( widgetInfo.widget );
2043 gbLayout->addLayout(
c, row, column, 1, 2 );
2048 gbLayout->addWidget( mypLabel, row, column++ );
2049 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2053 if ( column >= columnCount * 2 )
2059 QWidget *spacer =
new QWidget();
2060 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2061 gbLayout->addWidget( spacer, ++row, 0 );
2062 gbLayout->setRowStretch( row, 1 );
2064 newWidgetInfo.labelText = QString();
2065 newWidgetInfo.labelOnTop =
true;
2079 mWidgets.append( qmlWrapper );
2081 newWidgetInfo.widget = qmlWrapper->
widget();
2082 newWidgetInfo.labelText = elementDef->
name();
2083 newWidgetInfo.labelOnTop =
true;
2084 newWidgetInfo.showLabel = widgetDef->
showLabel();
2098 mWidgets.append( htmlWrapper );
2100 newWidgetInfo.widget = htmlWrapper->
widget();
2101 newWidgetInfo.labelText = elementDef->
name();
2102 newWidgetInfo.labelOnTop =
true;
2103 newWidgetInfo.showLabel = widgetDef->
showLabel();
2108 QgsDebugMsg( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
2112 newWidgetInfo.showLabel = widgetDef->
showLabel();
2114 return newWidgetInfo;
2135 mWidgets.append( eww );
2138 void QgsAttributeForm::createWrappers()
2140 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2141 const QList<QgsField> fields = mLayer->
fields().
toList();
2143 const auto constMyWidgets = myWidgets;
2144 for ( QWidget *myWidget : constMyWidgets )
2147 QVariant vRel = myWidget->property(
"qgisRelation" );
2148 if ( vRel.isValid() )
2158 mWidgets.append( rww );
2163 const auto constFields = fields;
2164 for (
const QgsField &field : constFields )
2166 if ( field.name() == myWidget->objectName() )
2171 addWidgetWrapper( eww );
2178 void QgsAttributeForm::afterWidgetInit()
2180 bool isFirstEww =
true;
2182 const auto constMWidgets = mWidgets;
2191 setFocusProxy( eww->
widget() );
2206 if ( e->type() == QEvent::KeyPress )
2208 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
2209 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
2221 QSet< int > &mixedValueFields,
2222 QHash< int, QVariant > &fieldSharedValues )
const
2224 mixedValueFields.clear();
2225 fieldSharedValues.clear();
2231 for (
int i = 0; i < mLayer->
fields().count(); ++i )
2233 if ( mixedValueFields.contains( i ) )
2238 fieldSharedValues[i] = f.
attribute( i );
2242 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
2244 fieldSharedValues.remove( i );
2245 mixedValueFields.insert( i );
2251 if ( mixedValueFields.count() == mLayer->
fields().
count() )
2260 void QgsAttributeForm::layerSelectionChanged()
2272 resetMultiEdit(
true );
2279 mIsSettingMultiEditFeatures =
true;
2280 mMultiEditFeatureIds = fids;
2282 if ( fids.isEmpty() )
2285 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2286 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2288 wIt.value()->initialize( QVariant() );
2290 mIsSettingMultiEditFeatures =
false;
2297 QSet< int > mixedValueFields;
2298 QHash< int, QVariant > fieldSharedValues;
2299 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2306 const auto constMixedValueFields = mixedValueFields;
2307 for (
int fieldIndex : qgis::as_const( mixedValueFields ) )
2311 const QStringList additionalFields = w->editorWidget()->additionalFields();
2312 QVariantList additionalFieldValues;
2313 for (
const QString &additionalField : additionalFields )
2314 additionalFieldValues << firstFeature.
attribute( additionalField );
2315 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
2318 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2319 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2324 const QStringList additionalFields = w->editorWidget()->additionalFields();
2325 for (
const QString &additionalField : additionalFields )
2328 if ( constMixedValueFields.contains( index ) )
2335 QVariantList additionalFieldValues;
2338 for (
const QString &additionalField : additionalFields )
2339 additionalFieldValues << firstFeature.
attribute( additionalField );
2340 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
2344 for (
const QString &additionalField : additionalFields )
2347 Q_ASSERT( fieldSharedValues.contains( index ) );
2348 additionalFieldValues << fieldSharedValues.value( index );
2350 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
2354 mIsSettingMultiEditFeatures =
false;
2359 if ( mOwnsMessageBar )
2361 mOwnsMessageBar =
false;
2362 mMessageBar = messageBar;
2372 QStringList filters;
2375 QString filter = widget->currentFilterExpression();
2376 if ( !filter.isNull() )
2377 filters <<
'(' + filter +
')';
2380 return filters.join( QStringLiteral(
" AND " ) );
2383 int QgsAttributeForm::messageTimeout()
2386 return settings.
value( QStringLiteral(
"qgis/messageTimeout" ), 5 ).toInt();
2391 bool newVisibility = expression.evaluate( expressionContext ).toBool();
2393 if ( newVisibility != isVisible )
2401 widget->setVisible( newVisibility );
2404 isVisible = newVisibility;
2417 if ( infos.count() == 0 || !currentFormFeature( formFeature ) )
2420 const QString hint = tr(
"No feature joined" );
2421 const auto constInfos = infos;
2424 if ( !info->isDynamicFormEnabled() )
2429 mJoinedFeatures[info] = joinFeature;
2431 if ( info->hasSubset() )
2435 const auto constSubsetNames = subsetNames;
2436 for (
const QString &field : constSubsetNames )
2438 QString prefixedName = info->prefixedFieldName( field );
2440 QString hintText = hint;
2454 for (
const QgsField &field : joinFields )
2456 QString prefixedName = info->prefixedFieldName( field );
2458 QString hintText = hint;
2472 bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
2477 void QgsAttributeForm::updateDefaultValueDependencies()
2479 mDefaultValueDependencies.clear();
2487 const QSet<QString> referencedColumns = exp.referencedColumns();
2488 for (
const QString &referencedColumn : referencedColumns )
2494 for (
const int id : allAttributeIds )
2496 mDefaultValueDependencies.insertMulti(
id, eww );
2501 mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
2514 mIconMap[eww->
widget()]->hide();
2528 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
2529 const QString tooltip = tr(
"Join settings do not allow editing" );
2530 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2534 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
2535 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
2536 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2540 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
2541 const QString tooltip = tr(
"Joined layer is not toggled editable" );
2542 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2548 void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
2551 sw->setToolTip( tooltip );