46 #include <QTextStream> 49 #include <QFormLayout> 50 #include <QGridLayout> 54 #include <QPushButton> 56 #include <QMessageBox> 57 #include <QToolButton> 60 int QgsAttributeForm::sFormCounter = 0;
65 , mOwnsMessageBar( true )
67 , mFormNr( sFormCounter++ )
69 , mPreventFeatureRefresh( false )
70 , mIsSettingMultiEditFeatures( false )
71 , mUnsavedMultiEditChanges( false )
72 , mEditCommandMessage( tr(
"Attributes changed" ) )
85 updateContainersVisibility();
91 qDeleteAll( mInterfaces );
118 mInterfaces.append( iface );
134 if ( mUnsavedMultiEditChanges )
137 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
138 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
139 if ( res == QMessageBox::Yes )
144 clearMultiEditMessages();
146 mUnsavedMultiEditChanges =
false;
194 w->setContext( newContext );
200 w->setVisible( relationWidgetsVisible );
207 mSearchButtonBox->setVisible(
false );
211 synchronizeEnabledState();
212 mSearchButtonBox->setVisible(
false );
216 resetMultiEdit(
false );
217 synchronizeEnabledState();
218 mSearchButtonBox->setVisible(
false );
222 mSearchButtonBox->setVisible(
true );
227 mSearchButtonBox->setVisible(
false );
233 mSearchButtonBox->setVisible(
false );
242 const auto constMWidgets = mWidgets;
246 if ( eww && eww->
field().
name() == field )
256 mIsSettingFeature =
true;
267 synchronizeEnabledState();
269 const auto constMInterfaces = mInterfaces;
272 iface->featureChanged();
288 mIsSettingFeature =
false;
291 bool QgsAttributeForm::saveEdits()
294 bool changedLayer =
false;
300 bool doUpdate =
false;
315 QVariant dstVar = dst.at( eww->
fieldIdx() );
316 QVariant srcVar = eww->
value();
321 bool changed = ( dstVar != srcVar && !dstVar.isNull() && !srcVar.isNull() )
322 || ( dstVar.isNull() != srcVar.isNull() );
323 if ( changed && srcVar.isValid() && fieldIsEditable( eww->
fieldIdx() ) )
334 const auto constMInterfaces = mInterfaces;
337 if ( !iface->acceptChanges( updatedFeature ) )
349 bool res = mLayer->
addFeature( updatedFeature );
368 for (
int i = 0; i < dst.count(); ++i )
371 || !dst.at( i ).isValid()
372 || !fieldIsEditable( i ) )
377 QgsDebugMsg( QStringLiteral(
"Updating field %1" ).arg( i ) );
378 QgsDebugMsg( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
379 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ) );
380 QgsDebugMsg( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
381 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ) );
383 newValues[i] = dst.at( i );
384 oldValues[i] = src.at( i );
391 if ( success && n > 0 )
418 void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
423 mUnsavedMultiEditChanges =
false;
427 void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
429 clearMultiEditMessages();
430 resetMultiEdit( link == QLatin1String(
"#apply" ) );
433 void QgsAttributeForm::filterTriggered()
435 QString filter = createFilterExpression();
441 void QgsAttributeForm::searchZoomTo()
443 QString filter = createFilterExpression();
444 if ( filter.isEmpty() )
450 void QgsAttributeForm::searchFlash()
452 QString filter = createFilterExpression();
453 if ( filter.isEmpty() )
459 void QgsAttributeForm::filterAndTriggered()
461 QString filter = createFilterExpression();
462 if ( filter.isEmpty() )
470 void QgsAttributeForm::filterOrTriggered()
472 QString filter = createFilterExpression();
473 if ( filter.isEmpty() )
481 void QgsAttributeForm::pushSelectedFeaturesMessage()
487 tr(
"%n matching feature(s) selected",
"matching features", count ),
494 tr(
"No matching features found" ),
502 QString filter = createFilterExpression();
503 if ( filter.isEmpty() )
507 pushSelectedFeaturesMessage();
512 void QgsAttributeForm::searchSetSelection()
517 void QgsAttributeForm::searchAddToSelection()
522 void QgsAttributeForm::searchRemoveFromSelection()
527 void QgsAttributeForm::searchIntersectSelection()
532 bool QgsAttributeForm::saveMultiEdits()
536 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
537 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
552 newAttributeValues.insert( wIt.key(), w->
currentValue() );
555 if ( newAttributeValues.isEmpty() )
563 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
564 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
565 if ( res != QMessageBox::Ok )
576 const auto constMMultiEditFeatureIds = mMultiEditFeatureIds;
579 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
580 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
586 clearMultiEditMessages();
599 if ( !mButtonBox->isVisible() )
600 mMessageBar->
pushItem( mMultiEditMessageBarItem );
611 wrapper->notifyAboutToSave();
649 success = saveEdits();
653 success = saveMultiEdits();
658 mUnsavedMultiEditChanges =
false;
666 mValuesInitialized =
false;
667 const auto constMWidgets = mWidgets;
670 ww->setFeature( mFeature );
672 mValuesInitialized =
true;
678 const auto widgets { findChildren< QgsAttributeFormEditorWidget * >() };
685 void QgsAttributeForm::clearMultiEditMessages()
687 if ( mMultiEditUnsavedMessageBarItem )
689 if ( !mButtonBox->isVisible() )
690 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
691 mMultiEditUnsavedMessageBarItem =
nullptr;
693 if ( mMultiEditMessageBarItem )
695 if ( !mButtonBox->isVisible() )
696 mMessageBar->
popWidget( mMultiEditMessageBarItem );
697 mMultiEditMessageBarItem =
nullptr;
701 QString QgsAttributeForm::createFilterExpression()
const 706 QString filter = w->currentFilterExpression();
707 if ( !filter.isEmpty() )
711 if ( filters.isEmpty() )
714 QString filter = filters.join( QStringLiteral(
") AND (" ) ).prepend(
'(' ).append(
')' );
719 void QgsAttributeForm::onAttributeChanged(
const QVariant &value )
724 bool signalEmitted =
false;
726 if ( mValuesInitialized )
740 signalEmitted =
true;
742 updateJoinedFields( *eww );
748 if ( !mIsSettingMultiEditFeatures )
750 mUnsavedMultiEditChanges =
true;
752 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
753 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
754 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
755 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
756 clearMultiEditMessages();
759 if ( !mButtonBox->isVisible() )
760 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
770 updateConstraints( eww );
772 if ( !signalEmitted )
781 void QgsAttributeForm::updateAllConstraints()
783 const auto constMWidgets = mWidgets;
788 updateConstraints( eww );
796 if ( currentFormFeature( ft ) )
808 updateConstraint( ft, eww );
811 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
814 updateConstraint( ft, depsEww );
817 synchronizeEnabledState();
824 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
825 for ( ContainerInformation *info : infos )
827 info->apply( &mExpressionContext );
832 void QgsAttributeForm::updateContainersVisibility()
836 const QVector<ContainerInformation *> infos = mContainerVisibilityInformation;
838 for ( ContainerInformation *info : infos )
840 info->apply( &mExpressionContext );
844 updateAllConstraints();
858 if ( mJoinedFeatures.contains( info ) )
888 if ( dst.count() > eww->
fieldIdx() )
890 QVariant dstVar = dst.at( eww->
fieldIdx() );
891 QVariant srcVar = eww->
value();
894 if ( ( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() )
910 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
912 mContainerVisibilityInformation.append( info );
914 const QSet<QString> referencedColumns = info->expression.referencedColumns();
916 for (
const QString &col : referencedColumns )
918 mContainerInformationDependency[ col ].append( info );
922 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
946 void QgsAttributeForm::onAttributeAdded(
int idx )
948 mPreventFeatureRefresh =
false;
952 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
960 void QgsAttributeForm::onAttributeDeleted(
int idx )
962 mPreventFeatureRefresh =
false;
974 void QgsAttributeForm::onUpdatedFields()
976 mPreventFeatureRefresh =
false;
988 attrs[i].convert(
layer()->fields().at( i ).type() );
993 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
1003 void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1011 if ( formEditorWidget )
1017 QList<QgsEditorWidgetWrapper *> wDeps;
1029 if ( name != ewwName )
1036 for (
const QString &colName : referencedColumns )
1038 if ( name == colName )
1040 wDeps.append( eww );
1061 void QgsAttributeForm::preventFeatureRefresh()
1063 mPreventFeatureRefresh =
true;
1080 void QgsAttributeForm::synchronizeEnabledState()
1082 bool isEditable = ( mFeature.
isValid()
1098 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1099 ww->setEnabled( enabled );
1107 QStringList invalidFields, descriptions;
1108 bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
1110 isEditable = isEditable & validConstraint;
1114 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1116 okButton->setEnabled( isEditable );
1119 void QgsAttributeForm::init()
1121 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1124 QWidget *formWidget =
nullptr;
1126 bool buttonBoxVisible =
true;
1130 buttonBoxVisible = mButtonBox->isVisible();
1132 mButtonBox =
nullptr;
1135 if ( mSearchButtonBox )
1137 delete mSearchButtonBox;
1138 mSearchButtonBox =
nullptr;
1141 qDeleteAll( mWidgets );
1144 while ( QWidget *w = this->findChild<QWidget *>() )
1150 QVBoxLayout *vl =
new QVBoxLayout();
1152 vl->setContentsMargins( 0, 0, 0, 0 );
1154 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1155 vl->addWidget( mMessageBar );
1160 QGridLayout *layout =
new QGridLayout();
1161 QWidget *container =
new QWidget();
1162 container->setLayout( layout );
1163 vl->addWidget( container );
1165 mFormEditorWidgets.clear();
1166 mFormWidgets.clear();
1169 setContentsMargins( 0, 0, 0, 0 );
1178 if ( file && file->open( QFile::ReadOnly ) )
1182 QFileInfo fi( file->fileName() );
1183 loader.setWorkingDirectory( fi.dir() );
1184 formWidget = loader.load( file,
this );
1187 formWidget->setWindowFlags( Qt::Widget );
1188 layout->addWidget( formWidget );
1191 mButtonBox = findChild<QDialogButtonBox *>();
1194 formWidget->installEventFilter(
this );
1206 int columnCount = 1;
1215 if ( !containerDef )
1220 tabWidget =
nullptr;
1221 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1222 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1225 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1234 layout->addWidget( tabWidget, row, column, 1, 2 );
1238 QWidget *tabPage =
new QWidget( tabWidget );
1240 tabWidget->addTab( tabPage, widgDef->name() );
1244 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1246 QGridLayout *tabPageLayout =
new QGridLayout();
1247 tabPage->setLayout( tabPageLayout );
1249 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1250 tabPageLayout->addWidget( widgetInfo.widget );
1255 tabWidget =
nullptr;
1256 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1257 QLabel *label =
new QLabel( widgetInfo.labelText );
1258 label->setToolTip( widgetInfo.toolTip );
1259 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1261 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1264 label->setBuddy( widgetInfo.widget );
1266 if ( !widgetInfo.showLabel )
1268 QVBoxLayout *
c =
new QVBoxLayout();
1269 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1270 c->addWidget( widgetInfo.widget );
1271 layout->addLayout( c, row, column, 1, 2 );
1274 else if ( widgetInfo.labelOnTop )
1276 QVBoxLayout *
c =
new QVBoxLayout();
1277 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1278 c->addWidget( label );
1279 c->addWidget( widgetInfo.widget );
1280 layout->addLayout( c, row, column, 1, 2 );
1285 layout->addWidget( label, row, column++ );
1286 layout->addWidget( widgetInfo.widget, row, column++ );
1290 if ( column >= columnCount * 2 )
1296 formWidget = container;
1305 formWidget =
new QWidget(
this );
1306 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1307 formWidget->setLayout( gridLayout );
1313 scrollArea->setWidget( formWidget );
1314 scrollArea->setWidgetResizable(
true );
1315 scrollArea->setFrameShape( QFrame::NoFrame );
1316 scrollArea->setFrameShadow( QFrame::Plain );
1317 scrollArea->setFocusProxy(
this );
1318 layout->addWidget( scrollArea );
1322 layout->addWidget( formWidget );
1329 for (
const QgsField &field : fields )
1337 QString labelText = fieldName;
1338 labelText.replace(
'&', QStringLiteral(
"&&" ) );
1342 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1348 QLabel *l =
new QLabel( labelText );
1349 l->setToolTip( QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( fieldName, field.comment() ) );
1350 QSvgWidget *i =
new QSvgWidget();
1351 i->setFixedSize( 18, 18 );
1355 QWidget *w =
nullptr;
1360 mFormEditorWidgets.insert( idx, formWidget );
1361 mFormWidgets.append( formWidget );
1364 l->setBuddy( eww->
widget() );
1368 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() ) ) );
1373 w->setObjectName( field.name() );
1377 addWidgetWrapper( eww );
1378 mIconMap[eww->
widget()] = i;
1383 gridLayout->addWidget( l, row++, 0, 1, 2 );
1384 gridLayout->addWidget( w, row++, 0, 1, 2 );
1385 gridLayout->addWidget( i, row++, 0, 1, 2 );
1389 gridLayout->addWidget( l, row, 0 );
1390 gridLayout->addWidget( w, row, 1 );
1391 gridLayout->addWidget( i, row++, 2 );
1402 gridLayout->addWidget( formWidget, row++, 0, 1, 2 );
1404 mWidgets.append( rww );
1405 mFormWidgets.append( formWidget );
1410 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1411 gridLayout->addItem( spacerItem, row, 0 );
1412 gridLayout->setRowStretch( row, 1 );
1419 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1420 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1421 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1423 mButtonBox->setVisible( buttonBoxVisible );
1425 if ( !mSearchButtonBox )
1427 mSearchButtonBox =
new QWidget();
1428 QHBoxLayout *boxLayout =
new QHBoxLayout();
1429 boxLayout->setMargin( 0 );
1430 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1431 mSearchButtonBox->setLayout( boxLayout );
1432 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1434 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
1436 boxLayout->addWidget( clearButton );
1437 boxLayout->addStretch( 1 );
1439 QPushButton *flashButton =
new QPushButton();
1440 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1441 flashButton->setText( tr(
"&Flash Features" ) );
1442 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1443 boxLayout->addWidget( flashButton );
1445 QPushButton *zoomButton =
new QPushButton();
1446 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1447 zoomButton->setText( tr(
"&Zoom to Features" ) );
1448 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
1449 boxLayout->addWidget( zoomButton );
1451 QToolButton *selectButton =
new QToolButton();
1452 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1453 selectButton->setText( tr(
"&Select Features" ) );
1455 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1456 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1457 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
1458 QMenu *selectMenu =
new QMenu( selectButton );
1459 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
1461 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
1462 selectMenu->addAction( selectAction );
1463 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
1465 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
1466 selectMenu->addAction( addSelectAction );
1467 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
1469 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
1470 selectMenu->addAction( deselectAction );
1471 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
1473 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
1474 selectMenu->addAction( filterSelectAction );
1475 selectButton->setMenu( selectMenu );
1476 boxLayout->addWidget( selectButton );
1480 QToolButton *filterButton =
new QToolButton();
1481 filterButton->setText( tr(
"Filter Features" ) );
1482 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
1483 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1484 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
1485 QMenu *filterMenu =
new QMenu( filterButton );
1486 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
1487 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
1488 filterMenu->addAction( filterAndAction );
1489 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
1490 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
1491 filterMenu->addAction( filterOrAction );
1492 filterButton->setMenu( filterMenu );
1493 boxLayout->addWidget( filterButton );
1497 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
1499 closeButton->setShortcut( Qt::Key_Escape );
1500 boxLayout->addWidget( closeButton );
1503 layout->addWidget( mSearchButtonBox );
1521 const auto constMInterfaces = mInterfaces;
1532 QApplication::restoreOverrideCursor();
1535 void QgsAttributeForm::cleanPython()
1537 if ( !mPyFormVarName.isNull() )
1539 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
1544 void QgsAttributeForm::initPython()
1561 if ( ! initFilePath.isEmpty() )
1563 QFile inputFile( initFilePath );
1565 if ( inputFile.open( QFile::ReadOnly ) )
1568 QTextStream inf( &inputFile );
1569 initCode = inf.readAll();
1574 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
1585 if ( initCode.isEmpty() )
1587 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
1599 if ( ! initCode.isEmpty() )
1608 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
1610 static int sFormId = 0;
1611 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
1613 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
1614 .arg( mPyFormVarName )
1615 .arg( ( quint64 )
this );
1619 QgsDebugMsg( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
1622 if ( numArgs == QLatin1String(
"3" ) )
1630 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 ) );
1633 QString expr = QString(
"%1(%2)" )
1634 .arg( mLayer->editFormInit() )
1635 .arg( mPyFormVarName );
1636 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
1646 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 ) );
1654 WidgetInfo newWidgetInfo;
1656 switch ( widgetDef->
type() )
1666 if ( fldIdx < fields.
count() && fldIdx >= 0 )
1672 mFormEditorWidgets.insert( fldIdx, formWidget );
1673 mFormWidgets.append( formWidget );
1677 newWidgetInfo.widget = formWidget;
1678 addWidgetWrapper( eww );
1680 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
1681 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
1686 newWidgetInfo.labelText.replace(
'&', QStringLiteral(
"&&" ) );
1687 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
1688 newWidgetInfo.showLabel = widgetDef->
showLabel();
1706 mWidgets.append( rww );
1707 mFormWidgets.append( formWidget );
1709 newWidgetInfo.widget = formWidget;
1710 newWidgetInfo.labelText = QString();
1711 newWidgetInfo.labelOnTop =
true;
1723 if ( columnCount <= 0 )
1727 QWidget *myContainer =
nullptr;
1730 QGroupBox *groupBox =
new QGroupBox( parent );
1731 widgetName = QStringLiteral(
"QGroupBox" );
1733 groupBox->setTitle( container->
name() );
1734 myContainer = groupBox;
1735 newWidgetInfo.widget = myContainer;
1739 myContainer =
new QWidget();
1745 scrollArea->setWidget( myContainer );
1746 scrollArea->setWidgetResizable(
true );
1747 scrollArea->setFrameShape( QFrame::NoFrame );
1748 widgetName = QStringLiteral(
"QScrollArea QWidget" );
1750 newWidgetInfo.widget = scrollArea;
1754 newWidgetInfo.widget = myContainer;
1755 widgetName = QStringLiteral(
"QWidget" );
1761 QString style {QStringLiteral(
"background-color: %1;" ).arg( container->
backgroundColor().name() )};
1762 newWidgetInfo.widget->setStyleSheet( style );
1765 QGridLayout *gbLayout =
new QGridLayout();
1766 myContainer->setLayout( gbLayout );
1771 const QList<QgsAttributeEditorElement *> children = container->
children();
1775 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
1782 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1786 if ( widgetInfo.labelText.isNull() )
1788 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1793 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
1794 mypLabel->setToolTip( widgetInfo.toolTip );
1795 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1797 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1800 mypLabel->setBuddy( widgetInfo.widget );
1802 if ( widgetInfo.labelOnTop )
1804 QVBoxLayout *
c =
new QVBoxLayout();
1805 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1806 c->layout()->addWidget( mypLabel );
1807 c->layout()->addWidget( widgetInfo.widget );
1808 gbLayout->addLayout( c, row, column, 1, 2 );
1813 gbLayout->addWidget( mypLabel, row, column++ );
1814 gbLayout->addWidget( widgetInfo.widget, row, column++ );
1818 if ( column >= columnCount * 2 )
1824 QWidget *spacer =
new QWidget();
1825 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
1826 gbLayout->addWidget( spacer, ++row, 0 );
1827 gbLayout->setRowStretch( row, 1 );
1829 newWidgetInfo.labelText = QString();
1830 newWidgetInfo.labelOnTop =
true;
1844 mWidgets.append( qmlWrapper );
1846 newWidgetInfo.widget = qmlWrapper->
widget();
1847 newWidgetInfo.labelText = elementDef->
name();
1848 newWidgetInfo.labelOnTop =
true;
1849 newWidgetInfo.showLabel = widgetDef->
showLabel();
1863 mWidgets.append( htmlWrapper );
1865 newWidgetInfo.widget = htmlWrapper->
widget();
1866 newWidgetInfo.labelText = elementDef->
name();
1867 newWidgetInfo.labelOnTop =
true;
1868 newWidgetInfo.showLabel = widgetDef->
showLabel();
1873 QgsDebugMsg( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
1877 newWidgetInfo.showLabel = widgetDef->
showLabel();
1879 return newWidgetInfo;
1884 const auto constMWidgets = mWidgets;
1899 mWidgets.append( eww );
1902 void QgsAttributeForm::createWrappers()
1904 QList<QWidget *> myWidgets = findChildren<QWidget *>();
1905 const QList<QgsField> fields = mLayer->
fields().
toList();
1907 const auto constMyWidgets = myWidgets;
1908 for ( QWidget *myWidget : constMyWidgets )
1911 QVariant vRel = myWidget->property(
"qgisRelation" );
1912 if ( vRel.isValid() )
1922 mWidgets.append( rww );
1927 const auto constFields = fields;
1928 for (
const QgsField &field : constFields )
1930 if ( field.name() == myWidget->objectName() )
1935 addWidgetWrapper( eww );
1942 void QgsAttributeForm::afterWidgetInit()
1944 bool isFirstEww =
true;
1946 const auto constMWidgets = mWidgets;
1955 setFocusProxy( eww->
widget() );
1970 if ( e->type() == QEvent::KeyPress )
1972 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
1973 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
1984 void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet< int > &mixedValueFields, QHash< int, QVariant > &fieldSharedValues )
const 1986 mixedValueFields.clear();
1987 fieldSharedValues.clear();
1993 for (
int i = 0; i < mLayer->
fields().
count(); ++i )
1995 if ( mixedValueFields.contains( i ) )
2000 fieldSharedValues[i] = f.
attribute( i );
2004 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
2006 fieldSharedValues.remove( i );
2007 mixedValueFields.insert( i );
2013 if ( mixedValueFields.count() == mLayer->
fields().
count() )
2022 void QgsAttributeForm::layerSelectionChanged()
2034 resetMultiEdit(
true );
2041 mIsSettingMultiEditFeatures =
true;
2042 mMultiEditFeatureIds = fids;
2044 if ( fids.isEmpty() )
2047 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2048 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2050 wIt.value()->initialize( QVariant() );
2052 mIsSettingMultiEditFeatures =
false;
2059 QSet< int > mixedValueFields;
2060 QHash< int, QVariant > fieldSharedValues;
2061 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2068 const auto constMixedValueFields = mixedValueFields;
2069 for (
int field : constMixedValueFields )
2073 w->initialize( firstFeature.
attribute( field ), true );
2076 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2077 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2081 w->initialize( sharedValueIt.value(), false );
2084 mIsSettingMultiEditFeatures =
false;
2089 if ( mOwnsMessageBar )
2091 mOwnsMessageBar =
false;
2092 mMessageBar = messageBar;
2102 QStringList filters;
2105 QString filter = widget->currentFilterExpression();
2106 if ( !filter.isNull() )
2107 filters <<
'(' + filter +
')';
2110 return filters.join( QStringLiteral(
" AND " ) );
2113 int QgsAttributeForm::messageTimeout()
2116 return settings.
value( QStringLiteral(
"qgis/messageTimeout" ), 5 ).toInt();
2121 bool newVisibility = expression.evaluate( expressionContext ).toBool();
2123 if ( newVisibility != isVisible )
2127 tabWidget->setTabVisible( widget, newVisibility );
2131 widget->setVisible( newVisibility );
2134 isVisible = newVisibility;
2147 if ( infos.count() == 0 || !currentFormFeature( formFeature ) )
2150 const QString hint = tr(
"No feature joined" );
2151 const auto constInfos = infos;
2154 if ( !info->isDynamicFormEnabled() )
2159 mJoinedFeatures[info] = joinFeature;
2161 if ( info->hasSubset() )
2165 const auto constSubsetNames = subsetNames;
2166 for (
const QString &field : constSubsetNames )
2168 QString prefixedName = info->prefixedFieldName( field );
2170 QString hintText = hint;
2184 for (
const QgsField &field : joinFields )
2186 QString prefixedName = info->prefixedFieldName( field );
2188 QString hintText = hint;
2202 bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const 2214 editable = fieldIsEditable( *( info->
joinLayer() ), srcFieldIndex, mFeature.
id() );
2217 editable = fieldIsEditable( *mLayer, fieldIndex, mFeature.
id() );
2234 mIconMap[eww->
widget()]->hide();
2248 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
2249 const QString tooltip = tr(
"Join settings do not allow editing" );
2250 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2254 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
2255 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
2256 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2260 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
2261 const QString tooltip = tr(
"Joined layer is not toggled editable" );
2262 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2268 void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
2271 sw->setToolTip( tooltip );
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
bool isValid() const
Returns the validity of this feature.
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
Constraint was set by layer.
An attribute editor widget that will represent arbitrary QML code.
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
QSet< QgsFeatureId > QgsFeatureIds
void setAttributeFormMode(const Mode &attributeFormMode)
Set attributeFormMode for the edited form.
int size() const
Returns number of items.
This is an abstract base class for any elements of a drag and drop form.
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field's origin (value from an enumeration)
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
bool enabled() const
Check if this optional is enabled.
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
This class is a composition of two QSettings instances:
Form is in aggregate search mode, show each widget in this mode.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
bool exists(int i) const
Returns if a field index is valid.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
This class contains context information for attribute editor widgets.
ConstraintOrigin
Origin of constraints.
static void warning(const QString &msg)
Goes to qWarning.
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
Remove from current selection.
#define Q_NOWARN_DEPRECATED_PUSH
A bar for displaying non-blocking messages to the user.
Container of fields for a vector layer.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void beforeRemovingExpressionField(int idx)
Will be emitted, when an expression field is going to be deleted from this vector layer...
This element will load a field's widget onto the form.
This element will load a relation editor onto the form.
Multi edit mode, for editing fields of multiple features at once.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
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 feat...
int count() const
Returns number of items.
An attribute editor widget that will represent arbitrary HTML code.
AttributeEditorType type() const
The type of this element.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
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 isEditable() const FINAL
Returns true if the provider is in editing mode.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
bool showUnlinkButton() const
Determines if the "unlink feature" button should be shown.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted...
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet) ...
bool popWidget(QgsMessageBarItem *item)
Remove the passed widget from the bar (if previously added), then display the next one in the stack i...
QString displayName() const
Returns the name to use when displaying this field.
void beforeModifiedCheck() const
Emitted when the layer is checked for modifications. Use for last-minute additions.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Defines left outer join from our vector layer to some other vector layer.
QMap< int, QVariant > QgsAttributeMap
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
bool showLabel() const
Controls if this element should be labeled with a title (field, relation or groupname).
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void destroyEditCommand()
Destroy active command and reverts all changes in it.
QgsOptionalExpression visibilityExpression() const
The visibility expression is used in the attribute form to show or hide this container based on an ex...
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
QString qmlCode() const
The QML code that will be represented within this widget.
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
QList< QgsRelation > referencedRelations(QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
Encapsulate a field in an attribute table or data source.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::Info, int duration=5)
convenience method for pushing a message to the bar
QgsRelationManager relationManager
QgsEditFormConfig editFormConfig
Add selection to current selection.
void editingStarted()
Emitted when editing on this layer has started.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
void endEditCommand()
Finish edit command and add it to undo/redo stack.
static bool eval(const QString &command, QString &result)
Eval a Python statement.
QString htmlCode() const
The QML code that will be represented within this widget.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
Set selection, removing any existing selection.
FormMode formMode() const
Returns the form mode.
QFile * localFile(const QString &filePathOrUrl)
Returns a QFile from a local file or to a temporary file previously fetched by the registry...
#define Q_NOWARN_DEPRECATED_POP
void setValid(bool validity)
Sets the validity of the feature.
Modify current selection to include only select features which match.
SelectBehavior
Selection behavior.
void selectByExpression(const QString &expression, SelectBehavior behavior=SetSelection)
Selects matching features using an expression.
QColor backgroundColor() const
backgroundColor
void beforeAddingExpressionField(const QString &fieldName)
Will be emitted, when an expression field is going to be added to this vector layer.
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar after hiding the currently visible one and putting it in a stack...
bool allowCustomUi() const
Returns true if the attribute editor should permit use of custom UI forms.
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement.
This class manages a set of relations between layers.
int columnCount() const
Gets the number of columns in this group.
Single edit mode, for editing a single feature.
static QgsProject * instance()
Returns the QgsProject singleton instance.
T data() const
Access the payload data.
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) FINAL
Adds a single feature to the sink.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
QString attributeFormModeString() const
Returns given attributeFormMode as string.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer. ...
virtual bool isGroupBox() const
Returns if this container is going to be rendered as a group box.
bool showLinkButton() const
Determines if the "link feature" button should be shown.
bool hasUpsertOnEdit() const
Returns whether a feature created on the target layer has to impact the joined layer by creating a ne...
bool nextFeature(QgsFeature &f)
Form values are used for searching/filtering the layer.
bool qgsVariantEqual(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether they are equal, NULL values are treated as equal...
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap(), bool skipDefaultValues=false)
Changes attributes' values for a feature (but does not immediately commit the changes).
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
QString name() const
Returns the name of this element.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
bool isDynamicFormEnabled() const
Returns whether the form has to be dynamically updated with joined fields when a feature is being cre...
QgsField field(int fieldIdx) const
Gets field at particular index (must be in range 0..N-1)
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else...
Allows modification of attribute values.
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
A form was embedded as a widget on another form.