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();
96 qDeleteAll( mInterfaces );
123 mInterfaces.append( iface );
139 if ( mUnsavedMultiEditChanges )
142 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
143 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
144 if ( res == QMessageBox::Yes )
149 clearMultiEditMessages();
151 mUnsavedMultiEditChanges =
false;
203 w->setContext( newContext );
209 w->setVisible( relationWidgetsVisible );
216 mSearchButtonBox->setVisible(
false );
220 synchronizeEnabledState();
221 mSearchButtonBox->setVisible(
false );
225 synchronizeEnabledState();
226 mSearchButtonBox->setVisible(
false );
230 resetMultiEdit(
false );
231 synchronizeEnabledState();
232 mSearchButtonBox->setVisible(
false );
236 mSearchButtonBox->setVisible(
true );
241 mSearchButtonBox->setVisible(
false );
247 mSearchButtonBox->setVisible(
false );
256 const auto constMWidgets = mWidgets;
271 QVariant mainValue = eww->
value();
273 additionalFieldValues[index] = value;
274 eww->
setValues( mainValue, additionalFieldValues );
283 mIsSettingFeature =
true;
296 synchronizeEnabledState();
300 mIsSettingFeature =
false;
301 const auto constMInterfaces = mInterfaces;
304 iface->featureChanged();
320 mIsSettingFeature =
false;
323 bool QgsAttributeForm::saveEdits()
326 bool changedLayer =
false;
331 bool doUpdate =
false;
352 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
353 QVariantList srcVars = QVariantList() << eww->
value();
354 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
358 for (
const QString &fieldName : additionalFields )
362 dstVars << dst.at( idx );
366 Q_ASSERT( dstVars.count() == srcVars.count() );
368 for (
int i = 0; i < dstVars.count(); i++ )
371 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
373 dst[fieldIndexes[i]] = srcVars[i];
383 const auto constMInterfaces = mInterfaces;
386 if ( !iface->acceptChanges( updatedFeature ) )
396 mFeature = updatedFeature;
402 bool res = mLayer->
addFeature( updatedFeature );
421 for (
int i = 0; i < dst.count(); ++i )
424 || !dst.at( i ).isValid()
425 || !fieldIsEditable( i ) )
431 QgsDebugMsgLevel( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
432 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ), 2 );
433 QgsDebugMsgLevel( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
434 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ), 2 );
436 newValues[i] = dst.at( i );
437 oldValues[i] = src.at( i );
444 if ( success && n > 0 )
471 bool QgsAttributeForm::updateDefaultValues(
const int originIdx )
475 updateDefaultValueDependencies();
477 if ( !mDefaultValueDependencies.contains( originIdx ) )
490 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
491 QVariantList srcVars = QVariantList() << eww->
value();
492 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
496 for (
const QString &fieldName : additionalFields )
500 dstVars << dst.at( idx );
504 Q_ASSERT( dstVars.count() == srcVars.count() );
506 for (
int i = 0; i < dstVars.count(); i++ )
509 if ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() && fieldIsEditable( fieldIndexes[i] ) )
511 dst[fieldIndexes[i]] = srcVars[i];
519 QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
532 if ( mAlreadyUpdatedFields.contains( eww->
fieldIdx() ) )
544 void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
549 mUnsavedMultiEditChanges =
false;
553 void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
555 clearMultiEditMessages();
556 resetMultiEdit( link == QLatin1String(
"#apply" ) );
559 void QgsAttributeForm::filterTriggered()
561 QString filter = createFilterExpression();
567 void QgsAttributeForm::searchZoomTo()
569 QString filter = createFilterExpression();
570 if ( filter.isEmpty() )
576 void QgsAttributeForm::searchFlash()
578 QString filter = createFilterExpression();
579 if ( filter.isEmpty() )
585 void QgsAttributeForm::filterAndTriggered()
587 QString filter = createFilterExpression();
588 if ( filter.isEmpty() )
596 void QgsAttributeForm::filterOrTriggered()
598 QString filter = createFilterExpression();
599 if ( filter.isEmpty() )
607 void QgsAttributeForm::pushSelectedFeaturesMessage()
613 tr(
"%n matching feature(s) selected",
"matching features", count ),
620 tr(
"No matching features found" ),
636 QString filter = createFilterExpression();
637 if ( filter.isEmpty() )
641 pushSelectedFeaturesMessage();
646 void QgsAttributeForm::searchSetSelection()
651 void QgsAttributeForm::searchAddToSelection()
656 void QgsAttributeForm::searchRemoveFromSelection()
661 void QgsAttributeForm::searchIntersectSelection()
666 bool QgsAttributeForm::saveMultiEdits()
670 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
671 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
678 || !fieldIsEditable( wIt.key() ) )
686 newAttributeValues.insert( wIt.key(), w->
currentValue() );
689 if ( newAttributeValues.isEmpty() )
697 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
698 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
699 if ( res != QMessageBox::Ok )
710 const auto constMMultiEditFeatureIds = mMultiEditFeatureIds;
713 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
714 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
720 clearMultiEditMessages();
733 if ( !mButtonBox->isVisible() )
734 mMessageBar->
pushItem( mMultiEditMessageBarItem );
745 wrapper->notifyAboutToSave();
785 success = saveEdits();
789 success = saveMultiEdits();
794 mUnsavedMultiEditChanges =
false;
802 mValuesInitialized =
false;
803 const auto constMWidgets = mWidgets;
806 ww->setFeature( mFeature );
808 mValuesInitialized =
true;
814 const auto widgets { findChildren< QgsAttributeFormEditorWidget * >() };
821 void QgsAttributeForm::clearMultiEditMessages()
823 if ( mMultiEditUnsavedMessageBarItem )
825 if ( !mButtonBox->isVisible() )
826 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
827 mMultiEditUnsavedMessageBarItem =
nullptr;
829 if ( mMultiEditMessageBarItem )
831 if ( !mButtonBox->isVisible() )
832 mMessageBar->
popWidget( mMultiEditMessageBarItem );
833 mMultiEditMessageBarItem =
nullptr;
837 QString QgsAttributeForm::createFilterExpression()
const
843 if ( !filter.isEmpty() )
847 if ( filters.isEmpty() )
850 QString filter = filters.join( QLatin1String(
") AND (" ) ).prepend(
'(' ).append(
')' );
859 if ( mExtraContextScope )
866 void QgsAttributeForm::onAttributeChanged(
const QVariant &value,
const QVariantList &additionalFieldValues )
871 bool signalEmitted =
false;
873 if ( mValuesInitialized )
892 for (
int i = 0; i < additionalFields.count(); i++ )
894 const QString fieldName = additionalFields.at( i );
895 const QVariant value = additionalFieldValues.at( i );
899 signalEmitted =
true;
901 updateJoinedFields( *eww );
907 if ( !mIsSettingMultiEditFeatures )
909 mUnsavedMultiEditChanges =
true;
911 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
912 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
913 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
914 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
915 clearMultiEditMessages();
918 if ( !mButtonBox->isVisible() )
919 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
929 updateConstraints( eww );
932 mAlreadyUpdatedFields.append( eww->
fieldIdx() );
933 updateDefaultValues( eww->
fieldIdx() );
934 mAlreadyUpdatedFields.removeAll( eww->
fieldIdx() );
939 if ( !signalEmitted )
948 void QgsAttributeForm::updateAllConstraints()
950 const auto constMWidgets = mWidgets;
955 updateConstraints( eww );
963 if ( currentFormValuesFeature( ft ) )
975 updateConstraint( ft, eww );
978 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
981 updateConstraint( ft, depsEww );
984 synchronizeEnabledState();
989 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
990 for ( ContainerInformation *info : infos )
992 info->apply( &context );
997 void QgsAttributeForm::updateContainersVisibility()
1001 const QVector<ContainerInformation *> infos = mContainerVisibilityInformation;
1003 for ( ContainerInformation *info : infos )
1005 info->apply( &context );
1009 updateAllConstraints();
1023 if ( mJoinedFeatures.contains( info ) )
1040 void QgsAttributeForm::updateLabels()
1042 if ( ! mLabelDataDefinedProperties.isEmpty() )
1045 if ( currentFormValuesFeature( currentFeature ) )
1049 for (
auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
1051 QLabel *label { it.key() };
1053 const QString value { it->valueAsString( context, QString(), &ok ) };
1054 if ( ok && ! value.isEmpty() )
1056 label->setText( value );
1063 bool QgsAttributeForm::currentFormValuesFeature(
QgsFeature &feature )
1076 if ( dst.count() > eww->
fieldIdx() )
1078 QVariantList dstVars = QVariantList() << dst.at( eww->
fieldIdx() );
1079 QVariantList srcVars = QVariantList() << eww->
value();
1080 QList<int> fieldIndexes = QList<int>() << eww->
fieldIdx();
1084 for (
const QString &fieldName : additionalFields )
1087 fieldIndexes << idx;
1088 dstVars << dst.at( idx );
1092 Q_ASSERT( dstVars.count() == srcVars.count() );
1094 for (
int i = 0; i < dstVars.count(); i++ )
1098 if ( ( !
qgsVariantEqual( dstVars[i], srcVars[i] ) || dstVars[i].isNull() != srcVars[i].isNull() ) && srcVars[i].isValid() )
1100 dst[fieldIndexes[i]] = srcVars[i];
1117 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1119 mContainerVisibilityInformation.append( info );
1121 const QSet<QString> referencedColumns = info->expression.referencedColumns();
1123 for (
const QString &col : referencedColumns )
1125 mContainerInformationDependency[ col ].append( info );
1129 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
1153 void QgsAttributeForm::onAttributeAdded(
int idx )
1155 mPreventFeatureRefresh =
false;
1159 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
1167 void QgsAttributeForm::onAttributeDeleted(
int idx )
1169 mPreventFeatureRefresh =
false;
1173 attrs.remove( idx );
1181 void QgsAttributeForm::onUpdatedFields()
1183 mPreventFeatureRefresh =
false;
1200 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
1210 void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1218 if ( formEditorWidget )
1224 QList<QgsEditorWidgetWrapper *> wDeps;
1236 if ( name != ewwName )
1243 for (
const QString &colName : referencedColumns )
1245 if ( name == colName )
1247 wDeps.append( eww );
1268 void QgsAttributeForm::preventFeatureRefresh()
1270 mPreventFeatureRefresh =
true;
1299 void QgsAttributeForm::synchronizeEnabledState()
1301 bool isEditable = ( mFeature.
isValid()
1317 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1318 ww->setEnabled( enabled );
1326 QStringList invalidFields, descriptions;
1327 bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
1329 isEditable = isEditable & validConstraint;
1333 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1335 okButton->setEnabled( isEditable );
1338 void QgsAttributeForm::init()
1340 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1343 QWidget *formWidget =
nullptr;
1345 bool buttonBoxVisible =
true;
1349 buttonBoxVisible = mButtonBox->isVisible();
1351 mButtonBox =
nullptr;
1354 if ( mSearchButtonBox )
1356 delete mSearchButtonBox;
1357 mSearchButtonBox =
nullptr;
1360 qDeleteAll( mWidgets );
1363 while ( QWidget *w = this->findChild<QWidget *>() )
1369 QVBoxLayout *vl =
new QVBoxLayout();
1370 vl->setContentsMargins( 0, 0, 0, 0 );
1372 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1373 vl->addWidget( mMessageBar );
1378 QGridLayout *layout =
new QGridLayout();
1379 QWidget *container =
new QWidget();
1380 container->setLayout( layout );
1381 vl->addWidget( container );
1383 mFormEditorWidgets.clear();
1384 mFormWidgets.clear();
1387 setContentsMargins( 0, 0, 0, 0 );
1396 if ( file && file->open( QFile::ReadOnly ) )
1400 QFileInfo fi( file->fileName() );
1401 loader.setWorkingDirectory( fi.dir() );
1402 formWidget = loader.load( file,
this );
1405 formWidget->setWindowFlags( Qt::Widget );
1406 layout->addWidget( formWidget );
1409 mButtonBox = findChild<QDialogButtonBox *>();
1412 formWidget->installEventFilter(
this );
1424 int columnCount = 1;
1433 if ( !containerDef )
1438 tabWidget =
nullptr;
1439 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1440 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1443 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1452 layout->addWidget( tabWidget, row, column, 1, 2 );
1456 QWidget *tabPage =
new QWidget( tabWidget );
1458 tabWidget->addTab( tabPage, widgDef->name() );
1462 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1464 QGridLayout *tabPageLayout =
new QGridLayout();
1465 tabPage->setLayout( tabPageLayout );
1467 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1468 tabPageLayout->addWidget( widgetInfo.widget );
1473 tabWidget =
nullptr;
1474 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1475 QLabel *label =
new QLabel( widgetInfo.labelText );
1476 label->setToolTip( widgetInfo.toolTip );
1477 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1479 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1482 label->setBuddy( widgetInfo.widget );
1484 if ( !widgetInfo.showLabel )
1486 QVBoxLayout *
c =
new QVBoxLayout();
1487 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1488 c->addWidget( widgetInfo.widget );
1489 layout->addLayout(
c, row, column, 1, 2 );
1492 else if ( widgetInfo.labelOnTop )
1494 QVBoxLayout *
c =
new QVBoxLayout();
1495 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1496 c->addWidget( label );
1497 c->addWidget( widgetInfo.widget );
1498 layout->addLayout(
c, row, column, 1, 2 );
1503 layout->addWidget( label, row, column++ );
1504 layout->addWidget( widgetInfo.widget, row, column++ );
1508 if ( widgDef->type() == QgsAttributeEditorElement::AttributeEditorType::AeTypeField )
1511 const int fieldIdx = fieldElement->
idx();
1512 if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
1514 const QString fieldName { mLayer->
fields().
at( fieldIdx ).
name() };
1518 if ( property.isActive() && !
property.expressionString().isEmpty() )
1520 mLabelDataDefinedProperties[ label ] = property;
1527 if ( column >= columnCount * 2 )
1533 formWidget = container;
1542 formWidget =
new QWidget(
this );
1543 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1544 formWidget->setLayout( gridLayout );
1550 scrollArea->setWidget( formWidget );
1551 scrollArea->setWidgetResizable(
true );
1552 scrollArea->setFrameShape( QFrame::NoFrame );
1553 scrollArea->setFrameShadow( QFrame::Plain );
1554 scrollArea->setFocusProxy(
this );
1555 layout->addWidget( scrollArea );
1559 layout->addWidget( formWidget );
1574 QString labelText = fieldName;
1575 labelText.replace(
'&', QLatin1String(
"&&" ) );
1579 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1585 QLabel *label =
new QLabel( labelText );
1587 QSvgWidget *i =
new QSvgWidget();
1588 i->setFixedSize( 18, 18 );
1593 if ( property.isActive() && ! property.expressionString().isEmpty() )
1595 mLabelDataDefinedProperties[ label ] = property;
1601 QWidget *w =
nullptr;
1606 mFormEditorWidgets.insert( idx, formWidget );
1607 mFormWidgets.append( formWidget );
1610 label->setBuddy( eww->
widget() );
1614 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() ) ) );
1623 addWidgetWrapper( eww );
1624 mIconMap[eww->
widget()] = i;
1629 gridLayout->addWidget( label, row++, 0, 1, 2 );
1630 gridLayout->addWidget( w, row++, 0, 1, 2 );
1631 gridLayout->addWidget( i, row++, 0, 1, 2 );
1635 gridLayout->addWidget( label, row, 0 );
1636 gridLayout->addWidget( w, row, 1 );
1637 gridLayout->addWidget( i, row++, 2 );
1649 gridLayout->addWidget( formWidget, row++, 0, 1, 2 );
1651 mWidgets.append( rww );
1652 mFormWidgets.append( formWidget );
1657 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1658 gridLayout->addItem( spacerItem, row, 0 );
1659 gridLayout->setRowStretch( row, 1 );
1664 updateDefaultValueDependencies();
1668 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1669 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1670 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1672 mButtonBox->setVisible( buttonBoxVisible );
1674 if ( !mSearchButtonBox )
1676 mSearchButtonBox =
new QWidget();
1677 QHBoxLayout *boxLayout =
new QHBoxLayout();
1678 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1679 mSearchButtonBox->setLayout( boxLayout );
1680 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1682 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
1684 boxLayout->addWidget( clearButton );
1685 boxLayout->addStretch( 1 );
1687 QPushButton *flashButton =
new QPushButton();
1688 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1689 flashButton->setText( tr(
"&Flash Features" ) );
1690 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1691 boxLayout->addWidget( flashButton );
1693 QPushButton *zoomButton =
new QPushButton();
1694 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1695 zoomButton->setText( tr(
"&Zoom to Features" ) );
1696 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
1697 boxLayout->addWidget( zoomButton );
1699 QToolButton *selectButton =
new QToolButton();
1700 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1701 selectButton->setText( tr(
"&Select Features" ) );
1703 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1704 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1705 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
1706 QMenu *selectMenu =
new QMenu( selectButton );
1707 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
1709 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
1710 selectMenu->addAction( selectAction );
1711 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
1713 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
1714 selectMenu->addAction( addSelectAction );
1715 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
1717 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
1718 selectMenu->addAction( deselectAction );
1719 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
1721 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
1722 selectMenu->addAction( filterSelectAction );
1723 selectButton->setMenu( selectMenu );
1724 boxLayout->addWidget( selectButton );
1728 QToolButton *filterButton =
new QToolButton();
1729 filterButton->setText( tr(
"Filter Features" ) );
1730 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
1731 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1732 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
1733 QMenu *filterMenu =
new QMenu( filterButton );
1734 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
1735 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
1736 filterMenu->addAction( filterAndAction );
1737 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
1738 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
1739 filterMenu->addAction( filterOrAction );
1740 filterButton->setMenu( filterMenu );
1741 boxLayout->addWidget( filterButton );
1745 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
1747 closeButton->setShortcut( Qt::Key_Escape );
1748 boxLayout->addWidget( closeButton );
1751 layout->addWidget( mSearchButtonBox );
1769 const auto constMInterfaces = mInterfaces;
1780 QApplication::restoreOverrideCursor();
1783 void QgsAttributeForm::cleanPython()
1785 if ( !mPyFormVarName.isNull() )
1787 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
1792 void QgsAttributeForm::initPython()
1809 if ( !initFilePath.isEmpty() )
1813 if ( inputFile && inputFile->open( QFile::ReadOnly ) )
1816 QTextStream inf( inputFile );
1817 initCode = inf.readAll();
1822 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
1833 if ( initCode.isEmpty() )
1835 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
1846 if ( !initCode.isEmpty() )
1852 tr(
"Python macro could not be run due to missing permissions." ),
1853 Qgis::MessageLevel::Warning,
1861 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
1863 static int sFormId = 0;
1864 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
1866 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
1867 .arg( mPyFormVarName )
1868 .arg( ( quint64 )
this );
1872 QgsDebugMsg( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
1875 if ( numArgs == QLatin1String(
"3" ) )
1883 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 ) );
1886 QString expr = QString(
"%1(%2)" )
1887 .arg( mLayer->editFormInit() )
1888 .arg( mPyFormVarName );
1889 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
1899 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 ) );
1907 WidgetInfo newWidgetInfo;
1909 switch ( widgetDef->
type() )
1919 if ( fldIdx < fields.
count() && fldIdx >= 0 )
1925 mFormEditorWidgets.insert( fldIdx, formWidget );
1926 mFormWidgets.append( formWidget );
1930 newWidgetInfo.widget = formWidget;
1931 addWidgetWrapper( eww );
1933 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
1934 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
1939 newWidgetInfo.labelText.replace(
'&', QLatin1String(
"&&" ) );
1940 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
1941 newWidgetInfo.showLabel = widgetDef->
showLabel();
1964 mWidgets.append( rww );
1965 mFormWidgets.append( formWidget );
1967 newWidgetInfo.widget = formWidget;
1968 newWidgetInfo.showLabel = relDef->
showLabel();
1969 newWidgetInfo.labelText = QString();
1970 newWidgetInfo.labelOnTop =
true;
1982 if ( columnCount <= 0 )
1986 QWidget *myContainer =
nullptr;
1989 QGroupBox *groupBox =
new QGroupBox( parent );
1990 widgetName = QStringLiteral(
"QGroupBox" );
1992 groupBox->setTitle( container->
name() );
1993 myContainer = groupBox;
1994 newWidgetInfo.widget = myContainer;
1998 myContainer =
new QWidget();
2004 scrollArea->setWidget( myContainer );
2005 scrollArea->setWidgetResizable(
true );
2006 scrollArea->setFrameShape( QFrame::NoFrame );
2007 widgetName = QStringLiteral(
"QScrollArea QWidget" );
2009 newWidgetInfo.widget = scrollArea;
2013 newWidgetInfo.widget = myContainer;
2014 widgetName = QStringLiteral(
"QWidget" );
2020 QString style {QStringLiteral(
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
2021 newWidgetInfo.widget->setStyleSheet( style );
2024 QGridLayout *gbLayout =
new QGridLayout();
2025 myContainer->setLayout( gbLayout );
2030 const QList<QgsAttributeEditorElement *> children = container->
children();
2034 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2041 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
2045 if ( widgetInfo.labelText.isNull() )
2047 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2052 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
2059 const int fldIdx = fieldDef->
idx();
2060 if ( fldIdx < fields.
count() && fldIdx >= 0 )
2062 const QString fieldName { fields.
at( fldIdx ).
name() };
2066 if ( property.isActive() && !
property.expressionString().isEmpty() )
2068 mLabelDataDefinedProperties[ mypLabel ] = property;
2074 mypLabel->setToolTip( widgetInfo.toolTip );
2075 if ( columnCount > 1 && !widgetInfo.labelOnTop )
2077 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2080 mypLabel->setBuddy( widgetInfo.widget );
2082 if ( widgetInfo.labelOnTop )
2084 QVBoxLayout *
c =
new QVBoxLayout();
2085 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2086 c->layout()->addWidget( mypLabel );
2087 c->layout()->addWidget( widgetInfo.widget );
2088 gbLayout->addLayout(
c, row, column, 1, 2 );
2093 gbLayout->addWidget( mypLabel, row, column++ );
2094 gbLayout->addWidget( widgetInfo.widget, row, column++ );
2098 if ( column >= columnCount * 2 )
2104 QWidget *spacer =
new QWidget();
2105 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2106 gbLayout->addWidget( spacer, ++row, 0 );
2107 gbLayout->setRowStretch( row, 1 );
2109 newWidgetInfo.labelText = QString();
2110 newWidgetInfo.labelOnTop =
true;
2123 mWidgets.append( qmlWrapper );
2125 newWidgetInfo.widget = qmlWrapper->
widget();
2126 newWidgetInfo.labelText = elementDef->
name();
2127 newWidgetInfo.labelOnTop =
true;
2128 newWidgetInfo.showLabel = widgetDef->
showLabel();
2140 mWidgets.append( htmlWrapper );
2142 newWidgetInfo.widget = htmlWrapper->
widget();
2143 newWidgetInfo.labelText = elementDef->
name();
2144 newWidgetInfo.labelOnTop =
true;
2145 newWidgetInfo.showLabel = widgetDef->
showLabel();
2150 QgsDebugMsg( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
2154 newWidgetInfo.showLabel = widgetDef->
showLabel();
2156 return newWidgetInfo;
2177 mWidgets.append( eww );
2180 void QgsAttributeForm::createWrappers()
2182 QList<QWidget *> myWidgets = findChildren<QWidget *>();
2183 const QList<QgsField> fields = mLayer->
fields().
toList();
2185 const auto constMyWidgets = myWidgets;
2186 for ( QWidget *myWidget : constMyWidgets )
2189 QVariant vRel = myWidget->property(
"qgisRelation" );
2190 if ( vRel.isValid() )
2200 mWidgets.append( rww );
2205 const auto constFields = fields;
2208 if (
field.
name() == myWidget->objectName() )
2213 addWidgetWrapper( eww );
2220 void QgsAttributeForm::afterWidgetInit()
2222 bool isFirstEww =
true;
2224 const auto constMWidgets = mWidgets;
2233 setFocusProxy( eww->
widget() );
2248 if ( e->type() == QEvent::KeyPress )
2250 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
2251 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
2263 QSet< int > &mixedValueFields,
2264 QHash< int, QVariant > &fieldSharedValues )
const
2266 mixedValueFields.clear();
2267 fieldSharedValues.clear();
2273 for (
int i = 0; i < mLayer->
fields().count(); ++i )
2275 if ( mixedValueFields.contains( i ) )
2280 fieldSharedValues[i] = f.
attribute( i );
2284 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
2286 fieldSharedValues.remove( i );
2287 mixedValueFields.insert( i );
2293 if ( mixedValueFields.count() == mLayer->
fields().
count() )
2302 void QgsAttributeForm::layerSelectionChanged()
2315 resetMultiEdit(
true );
2322 mIsSettingMultiEditFeatures =
true;
2323 mMultiEditFeatureIds = fids;
2325 if ( fids.isEmpty() )
2328 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2329 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2331 wIt.value()->initialize( QVariant() );
2333 mIsSettingMultiEditFeatures =
false;
2340 QSet< int > mixedValueFields;
2341 QHash< int, QVariant > fieldSharedValues;
2342 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2349 const auto constMixedValueFields = mixedValueFields;
2350 for (
int fieldIndex : qgis::as_const( mixedValueFields ) )
2354 const QStringList additionalFields = w->editorWidget()->additionalFields();
2355 QVariantList additionalFieldValues;
2356 for (
const QString &additionalField : additionalFields )
2357 additionalFieldValues << firstFeature.
attribute( additionalField );
2358 w->initialize( firstFeature.
attribute( fieldIndex ),
true, additionalFieldValues );
2361 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2362 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2367 const QStringList additionalFields = w->editorWidget()->additionalFields();
2368 for (
const QString &additionalField : additionalFields )
2371 if ( constMixedValueFields.contains( index ) )
2378 QVariantList additionalFieldValues;
2381 for (
const QString &additionalField : additionalFields )
2382 additionalFieldValues << firstFeature.
attribute( additionalField );
2383 w->initialize( firstFeature.
attribute( sharedValueIt.key() ),
true, additionalFieldValues );
2387 for (
const QString &additionalField : additionalFields )
2390 Q_ASSERT( fieldSharedValues.contains( index ) );
2391 additionalFieldValues << fieldSharedValues.value( index );
2393 w->initialize( sharedValueIt.value(),
false, additionalFieldValues );
2397 mIsSettingMultiEditFeatures =
false;
2402 if ( mOwnsMessageBar )
2404 mOwnsMessageBar =
false;
2405 mMessageBar = messageBar;
2415 QStringList filters;
2418 QString filter = widget->currentFilterExpression();
2419 if ( !filter.isNull() )
2420 filters <<
'(' + filter +
')';
2423 return filters.join( QLatin1String(
" AND " ) );
2428 mExtraContextScope.reset( extraScope );
2431 int QgsAttributeForm::messageTimeout()
2434 return settings.
value( QStringLiteral(
"qgis/messageTimeout" ), 5 ).toInt();
2439 bool newVisibility = expression.evaluate( expressionContext ).toBool();
2441 if ( newVisibility != isVisible )
2449 widget->setVisible( newVisibility );
2452 isVisible = newVisibility;
2465 if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
2468 const QString hint = tr(
"No feature joined" );
2469 const auto constInfos = infos;
2472 if ( !info->isDynamicFormEnabled() )
2477 mJoinedFeatures[info] = joinFeature;
2479 if ( info->hasSubset() )
2483 const auto constSubsetNames = subsetNames;
2484 for (
const QString &
field : constSubsetNames )
2486 QString prefixedName = info->prefixedFieldName(
field );
2488 QString hintText = hint;
2504 QString prefixedName = info->prefixedFieldName(
field );
2506 QString hintText = hint;
2520 bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const
2525 void QgsAttributeForm::updateDefaultValueDependencies()
2527 mDefaultValueDependencies.clear();
2535 const QSet<QString> referencedColumns = exp.referencedColumns();
2536 for (
const QString &referencedColumn : referencedColumns )
2542 for (
const int id : allAttributeIds )
2544 mDefaultValueDependencies.insertMulti(
id, eww );
2549 mDefaultValueDependencies.insertMulti( mLayer->
fields().
lookupField( referencedColumn ), eww );
2562 mIconMap[eww->
widget()]->hide();
2576 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
2577 const QString tooltip = tr(
"Join settings do not allow editing" );
2578 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2582 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
2583 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
2584 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2588 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
2589 const QString tooltip = tr(
"Joined layer is not toggled editable" );
2590 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2596 void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
2599 sw->setToolTip( tooltip );