41 #include <QTextStream> 44 #include <QFormLayout> 45 #include <QGridLayout> 49 #include <QPushButton> 51 #include <QMessageBox> 52 #include <QToolButton> 55 int QgsAttributeForm::sFormCounter = 0;
60 , mOwnsMessageBar( true )
62 , mFormNr( sFormCounter++ )
64 , mPreventFeatureRefresh( false )
65 , mIsSettingMultiEditFeatures( false )
66 , mUnsavedMultiEditChanges( false )
67 , mEditCommandMessage( tr(
"Attributes changed" ) )
68 , mMode( SingleEditMode )
80 updateAllConstraints();
86 qDeleteAll( mInterfaces );
113 mInterfaces.append( iface );
129 if ( mUnsavedMultiEditChanges )
132 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
133 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
134 if ( res == QMessageBox::Yes )
139 clearMultiEditMessages();
141 mUnsavedMultiEditChanges =
false;
188 w->setVisible( relationWidgetsVisible );
195 mSearchButtonBox->setVisible(
false );
199 synchronizeEnabledState();
200 mSearchButtonBox->setVisible(
false );
204 resetMultiEdit(
false );
205 synchronizeEnabledState();
206 mSearchButtonBox->setVisible(
false );
210 mSearchButtonBox->setVisible(
true );
215 mSearchButtonBox->setVisible(
false );
221 mSearchButtonBox->setVisible(
false );
233 if ( eww && eww->
field().
name() == field )
243 mIsSettingFeature =
true;
254 synchronizeEnabledState();
270 mIsSettingFeature =
false;
273 bool QgsAttributeForm::saveEdits()
276 bool changedLayer =
false;
282 bool doUpdate =
false;
297 QVariant dstVar = dst.at( eww->
fieldIdx() );
298 QVariant srcVar = eww->
value();
303 bool changed = ( dstVar != srcVar && !dstVar.isNull() && !srcVar.isNull() )
304 || ( dstVar.isNull() != srcVar.isNull() );
305 if ( changed && srcVar.isValid() && fieldIsEditable( eww->
fieldIdx() ) )
330 bool res = mLayer->
addFeature( updatedFeature );
349 for (
int i = 0; i < dst.count(); ++i )
351 if ( ( dst.at( i ) == src.at( i ) && dst.at( i ).isNull() == src.at( i ).isNull() )
352 || !dst.at( i ).isValid()
353 || !fieldIsEditable( i ) )
358 QgsDebugMsg( QString(
"Updating field %1" ).arg( i ) );
359 QgsDebugMsg( QString(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
360 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ) );
361 QgsDebugMsg( QString(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
362 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ) );
364 newValues[i] = dst.at( i );
365 oldValues[i] = src.at( i );
372 if ( success && n > 0 )
399 void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
404 mUnsavedMultiEditChanges =
false;
408 void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
410 clearMultiEditMessages();
411 resetMultiEdit( link == QLatin1String(
"#apply" ) );
414 void QgsAttributeForm::filterTriggered()
416 QString filter = createFilterExpression();
422 void QgsAttributeForm::searchZoomTo()
424 QString filter = createFilterExpression();
425 if ( filter.isEmpty() )
431 void QgsAttributeForm::searchFlash()
433 QString filter = createFilterExpression();
434 if ( filter.isEmpty() )
440 void QgsAttributeForm::filterAndTriggered()
442 QString filter = createFilterExpression();
443 if ( filter.isEmpty() )
451 void QgsAttributeForm::filterOrTriggered()
453 QString filter = createFilterExpression();
454 if ( filter.isEmpty() )
462 void QgsAttributeForm::pushSelectedFeaturesMessage()
468 tr(
"%1 matching %2 selected" ).arg( count )
469 .arg( count == 1 ? tr(
"feature" ) : tr(
"features" ) ),
476 tr(
"No matching features found" ),
484 QString filter = createFilterExpression();
485 if ( filter.isEmpty() )
489 pushSelectedFeaturesMessage();
494 void QgsAttributeForm::searchSetSelection()
499 void QgsAttributeForm::searchAddToSelection()
504 void QgsAttributeForm::searchRemoveFromSelection()
509 void QgsAttributeForm::searchIntersectSelection()
514 bool QgsAttributeForm::saveMultiEdits()
518 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
519 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
534 newAttributeValues.insert( wIt.key(), w->
currentValue() );
537 if ( newAttributeValues.isEmpty() )
545 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
546 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
547 if ( res != QMessageBox::Ok )
560 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
561 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
567 clearMultiEditMessages();
580 if ( !mButtonBox->isVisible() )
581 mMessageBar->
pushItem( mMultiEditMessageBarItem );
625 success = saveEdits();
629 success = saveMultiEdits();
634 mUnsavedMultiEditChanges =
false;
642 mValuesInitialized =
false;
647 mValuesInitialized =
true;
659 void QgsAttributeForm::clearMultiEditMessages()
661 if ( mMultiEditUnsavedMessageBarItem )
663 if ( !mButtonBox->isVisible() )
664 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
665 mMultiEditUnsavedMessageBarItem =
nullptr;
667 if ( mMultiEditMessageBarItem )
669 if ( !mButtonBox->isVisible() )
670 mMessageBar->
popWidget( mMultiEditMessageBarItem );
671 mMultiEditMessageBarItem =
nullptr;
675 QString QgsAttributeForm::createFilterExpression()
const 680 QString filter = w->currentFilterExpression();
681 if ( !filter.isEmpty() )
685 if ( filters.isEmpty() )
688 QString filter = filters.join( QStringLiteral(
") AND (" ) ).prepend(
'(' ).append(
')' );
692 void QgsAttributeForm::onAttributeChanged(
const QVariant &value )
697 bool signalEmitted =
false;
699 if ( mValuesInitialized )
713 signalEmitted =
true;
715 updateJoinedFields( *eww );
721 if ( !mIsSettingMultiEditFeatures )
723 mUnsavedMultiEditChanges =
true;
725 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
726 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
727 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
728 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
729 clearMultiEditMessages();
732 if ( !mButtonBox->isVisible() )
733 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
743 updateConstraints( eww );
745 if ( !signalEmitted )
754 void QgsAttributeForm::updateAllConstraints()
760 updateConstraints( eww );
768 if ( currentFormFeature( ft ) )
780 updateConstraint( ft, eww );
783 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
786 updateConstraint( ft, depsEww );
789 synchronizeEnabledState();
794 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
795 for ( ContainerInformation *info : infos )
797 info->apply( &mExpressionContext );
813 if ( mJoinedFeatures.contains( info ) )
843 if ( dst.count() > eww->
fieldIdx() )
845 QVariant dstVar = dst.at( eww->
fieldIdx() );
846 QVariant srcVar = eww->
value();
849 if ( ( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() )
865 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
867 mContainerVisibilityInformation.append( info );
869 const QSet<QString> referencedColumns = info->expression.referencedColumns();
871 for (
const QString &col : referencedColumns )
873 mContainerInformationDependency[ col ].append( info );
877 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
901 void QgsAttributeForm::onAttributeAdded(
int idx )
903 mPreventFeatureRefresh =
false;
907 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
915 void QgsAttributeForm::onAttributeDeleted(
int idx )
917 mPreventFeatureRefresh =
false;
929 void QgsAttributeForm::onUpdatedFields()
931 mPreventFeatureRefresh =
false;
943 attrs[i].convert(
layer()->fields().at( i ).type() );
948 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
958 void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
966 if ( formEditorWidget )
972 QList<QgsEditorWidgetWrapper *> wDeps;
984 if ( name != ewwName )
987 QgsExpression expr = eww->
layer()->
fields().
at( eww->
fieldIdx() ).constraints().constraintExpression();
989 const auto referencedColumns = expr.referencedColumns();
991 for (
const QString &colName : referencedColumns )
993 if ( name == colName )
1006 void QgsAttributeForm::preventFeatureRefresh()
1008 mPreventFeatureRefresh =
true;
1025 void QgsAttributeForm::synchronizeEnabledState()
1027 bool isEditable = ( mFeature.
isValid()
1043 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1044 ww->setEnabled( enabled );
1052 QStringList invalidFields, descriptions;
1053 bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
1055 isEditable = isEditable & validConstraint;
1059 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1061 okButton->setEnabled( isEditable );
1064 void QgsAttributeForm::init()
1066 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1069 QWidget *formWidget =
nullptr;
1071 bool buttonBoxVisible =
true;
1075 buttonBoxVisible = mButtonBox->isVisible();
1077 mButtonBox =
nullptr;
1080 if ( mSearchButtonBox )
1082 delete mSearchButtonBox;
1083 mSearchButtonBox =
nullptr;
1086 qDeleteAll( mWidgets );
1089 while ( QWidget *w = this->findChild<QWidget *>() )
1095 QVBoxLayout *vl =
new QVBoxLayout();
1097 vl->setContentsMargins( 0, 0, 0, 0 );
1099 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1100 vl->addWidget( mMessageBar );
1105 QGridLayout *layout =
new QGridLayout();
1106 QWidget *container =
new QWidget();
1107 container->setLayout( layout );
1108 vl->addWidget( container );
1110 mFormEditorWidgets.clear();
1111 mFormWidgets.clear();
1114 setContentsMargins( 0, 0, 0, 0 );
1122 if ( file.open( QFile::ReadOnly ) )
1127 loader.setWorkingDirectory( fi.dir() );
1128 formWidget = loader.load( &file,
this );
1129 formWidget->setWindowFlags( Qt::Widget );
1130 layout->addWidget( formWidget );
1133 mButtonBox = findChild<QDialogButtonBox *>();
1136 formWidget->installEventFilter(
this );
1147 int columnCount = 1;
1156 if ( !containerDef )
1161 tabWidget =
nullptr;
1162 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1163 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1164 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1172 layout->addWidget( tabWidget, row, column, 1, 2 );
1176 QWidget *tabPage =
new QWidget( tabWidget );
1178 tabWidget->addTab( tabPage, widgDef->name() );
1182 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1184 QGridLayout *tabPageLayout =
new QGridLayout();
1185 tabPage->setLayout( tabPageLayout );
1187 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1188 tabPageLayout->addWidget( widgetInfo.widget );
1193 tabWidget =
nullptr;
1194 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1195 QLabel *label =
new QLabel( widgetInfo.labelText );
1196 label->setToolTip( QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( widgetInfo.labelText, widgetInfo.hint ) );
1197 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1199 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1202 label->setBuddy( widgetInfo.widget );
1204 if ( !widgetInfo.showLabel )
1206 QVBoxLayout *c =
new QVBoxLayout();
1207 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1208 c->addWidget( widgetInfo.widget );
1209 layout->addLayout( c, row, column, 1, 2 );
1212 else if ( widgetInfo.labelOnTop )
1214 QVBoxLayout *c =
new QVBoxLayout();
1215 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1216 c->addWidget( label );
1217 c->addWidget( widgetInfo.widget );
1218 layout->addLayout( c, row, column, 1, 2 );
1223 layout->addWidget( label, row, column++ );
1224 layout->addWidget( widgetInfo.widget, row, column++ );
1228 if ( column >= columnCount * 2 )
1234 formWidget = container;
1243 formWidget =
new QWidget(
this );
1244 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1245 formWidget->setLayout( gridLayout );
1251 scrollArea->setWidget( formWidget );
1252 scrollArea->setWidgetResizable(
true );
1253 scrollArea->setFrameShape( QFrame::NoFrame );
1254 scrollArea->setFrameShadow( QFrame::Plain );
1255 scrollArea->setFocusProxy(
this );
1256 layout->addWidget( scrollArea );
1260 layout->addWidget( formWidget );
1267 for (
const QgsField &field : fields )
1278 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1284 QLabel *l =
new QLabel( fieldName );
1285 l->setToolTip( QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( fieldName, field.comment() ) );
1286 QSvgWidget *i =
new QSvgWidget();
1287 i->setFixedSize( 18, 18 );
1291 QWidget *w =
nullptr;
1296 mFormEditorWidgets.insert( idx, formWidget );
1297 mFormWidgets.append( formWidget );
1300 l->setBuddy( eww->
widget() );
1304 w =
new QLabel( QStringLiteral(
"<p style=\"color: red; font-style: italic;\">%1</p>" ).arg( tr(
"Failed to create widget with type '%1'" ), widgetSetup.
type() ) );
1309 w->setObjectName( field.name() );
1313 addWidgetWrapper( eww );
1314 mIconMap[eww->
widget()] = i;
1319 gridLayout->addWidget( l, row++, 0, 1, 2 );
1320 gridLayout->addWidget( w, row++, 0, 1, 2 );
1321 gridLayout->addWidget( i, row++, 0, 1, 2 );
1325 gridLayout->addWidget( l, row, 0 );
1326 gridLayout->addWidget( w, row, 1 );
1327 gridLayout->addWidget( i, row++, 2 );
1340 gridLayout->addWidget( formWidget, row++, 0, 1, 2 );
1342 mWidgets.append( rww );
1343 mFormWidgets.append( formWidget );
1348 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1349 gridLayout->addItem( spacerItem, row, 0 );
1350 gridLayout->setRowStretch( row, 1 );
1357 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1358 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1359 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1361 mButtonBox->setVisible( buttonBoxVisible );
1363 if ( !mSearchButtonBox )
1365 mSearchButtonBox =
new QWidget();
1366 QHBoxLayout *boxLayout =
new QHBoxLayout();
1367 boxLayout->setMargin( 0 );
1368 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1369 mSearchButtonBox->setLayout( boxLayout );
1370 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1372 QPushButton *clearButton =
new QPushButton( tr(
"&Reset form" ), mSearchButtonBox );
1374 boxLayout->addWidget( clearButton );
1375 boxLayout->addStretch( 1 );
1377 QPushButton *flashButton =
new QPushButton();
1378 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1379 flashButton->setText( tr(
"&Flash features" ) );
1380 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1381 boxLayout->addWidget( flashButton );
1383 QPushButton *zoomButton =
new QPushButton();
1384 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1385 zoomButton->setText( tr(
"&Zoom to features" ) );
1386 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
1387 boxLayout->addWidget( zoomButton );
1389 QToolButton *selectButton =
new QToolButton();
1390 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1391 selectButton->setText( tr(
"&Select features" ) );
1393 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1394 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1395 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
1396 QMenu *selectMenu =
new QMenu( selectButton );
1397 QAction *selectAction =
new QAction( tr(
"Select features" ), selectMenu );
1399 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
1400 selectMenu->addAction( selectAction );
1401 QAction *addSelectAction =
new QAction( tr(
"Add to current selection" ), selectMenu );
1403 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
1404 selectMenu->addAction( addSelectAction );
1405 QAction *deselectAction =
new QAction( tr(
"Remove from current selection" ), selectMenu );
1407 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
1408 selectMenu->addAction( deselectAction );
1409 QAction *filterSelectAction =
new QAction( tr(
"Filter current selection" ), selectMenu );
1411 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
1412 selectMenu->addAction( filterSelectAction );
1413 selectButton->setMenu( selectMenu );
1414 boxLayout->addWidget( selectButton );
1418 QToolButton *filterButton =
new QToolButton();
1419 filterButton->setText( tr(
"Filter features" ) );
1420 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
1421 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1422 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
1423 QMenu *filterMenu =
new QMenu( filterButton );
1424 QAction *filterAndAction =
new QAction( tr(
"Filter within (\"AND\")" ), filterMenu );
1425 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
1426 filterMenu->addAction( filterAndAction );
1427 QAction *filterOrAction =
new QAction( tr(
"Extend filter (\"OR\")" ), filterMenu );
1428 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
1429 filterMenu->addAction( filterOrAction );
1430 filterButton->setMenu( filterMenu );
1431 boxLayout->addWidget( filterButton );
1435 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
1437 closeButton->setShortcut( Qt::Key_Escape );
1438 boxLayout->addWidget( closeButton );
1441 layout->addWidget( mSearchButtonBox );
1443 mSearchButtonBox->setVisible( mMode ==
SearchMode );
1463 QApplication::restoreOverrideCursor();
1466 void QgsAttributeForm::cleanPython()
1468 if ( !mPyFormVarName.isNull() )
1470 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
1475 void QgsAttributeForm::initPython()
1492 if ( ! initFilePath.isEmpty() )
1494 QFile inputFile( initFilePath );
1496 if ( inputFile.open( QFile::ReadOnly ) )
1499 QTextStream inf( &inputFile );
1500 initCode = inf.readAll();
1505 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
1516 if ( initCode.isEmpty() )
1518 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
1530 if ( ! initCode.isEmpty() )
1539 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
1541 static int sFormId = 0;
1542 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
1544 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
1545 .arg( mPyFormVarName )
1546 .arg( ( quint64 )
this );
1550 QgsDebugMsg( QString(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
1553 if ( numArgs == QLatin1String(
"3" ) )
1561 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 ) );
1564 QString expr = QString(
"%1(%2)" )
1565 .arg( mLayer->editFormInit() )
1566 .arg( mPyFormVarName );
1567 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
1577 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 ) );
1585 WidgetInfo newWidgetInfo;
1587 switch ( widgetDef->
type() )
1597 if ( fldIdx < fields.
count() && fldIdx >= 0 )
1603 mFormEditorWidgets.insert( fldIdx, formWidget );
1604 mFormWidgets.append( formWidget );
1608 newWidgetInfo.widget = formWidget;
1609 addWidgetWrapper( eww );
1611 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
1612 newWidgetInfo.hint = fields.
at( fieldDef->
idx() ).comment();
1617 newWidgetInfo.showLabel = widgetDef->
showLabel();
1636 mWidgets.append( rww );
1637 mFormWidgets.append( formWidget );
1639 newWidgetInfo.widget = formWidget;
1640 newWidgetInfo.labelText = QString();
1641 newWidgetInfo.labelOnTop =
true;
1653 if ( columnCount <= 0 )
1656 QWidget *myContainer =
nullptr;
1659 QGroupBox *groupBox =
new QGroupBox( parent );
1661 groupBox->setTitle( container->
name() );
1662 myContainer = groupBox;
1663 newWidgetInfo.widget = myContainer;
1667 myContainer =
new QWidget();
1673 scrollArea->setWidget( myContainer );
1674 scrollArea->setWidgetResizable(
true );
1675 scrollArea->setFrameShape( QFrame::NoFrame );
1677 newWidgetInfo.widget = scrollArea;
1681 newWidgetInfo.widget = myContainer;
1685 QGridLayout *gbLayout =
new QGridLayout();
1686 myContainer->setLayout( gbLayout );
1691 QList<QgsAttributeEditorElement *> children = container->
children();
1695 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
1700 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1703 if ( widgetInfo.labelText.isNull() )
1705 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1710 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
1711 mypLabel->setToolTip( QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( widgetInfo.labelText, widgetInfo.hint ) );
1712 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1714 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1717 mypLabel->setBuddy( widgetInfo.widget );
1719 if ( widgetInfo.labelOnTop )
1721 QVBoxLayout *c =
new QVBoxLayout();
1722 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1723 c->layout()->addWidget( mypLabel );
1724 c->layout()->addWidget( widgetInfo.widget );
1725 gbLayout->addLayout( c, row, column, 1, 2 );
1730 gbLayout->addWidget( mypLabel, row, column++ );
1731 gbLayout->addWidget( widgetInfo.widget, row, column++ );
1735 if ( column >= columnCount * 2 )
1741 QWidget *spacer =
new QWidget();
1742 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
1743 gbLayout->addWidget( spacer, ++row, 0 );
1744 gbLayout->setRowStretch( row, 1 );
1746 newWidgetInfo.labelText = QString();
1747 newWidgetInfo.labelOnTop =
true;
1752 QgsDebugMsg(
"Unknown attribute editor widget type encountered..." );
1756 newWidgetInfo.showLabel = widgetDef->
showLabel();
1758 return newWidgetInfo;
1777 mWidgets.append( eww );
1780 void QgsAttributeForm::createWrappers()
1782 QList<QWidget *> myWidgets = findChildren<QWidget *>();
1783 const QList<QgsField> fields = mLayer->
fields().
toList();
1785 Q_FOREACH ( QWidget *myWidget, myWidgets )
1788 QVariant vRel = myWidget->property(
"qgisRelation" );
1789 if ( vRel.isValid() )
1799 mWidgets.append( rww );
1804 Q_FOREACH (
const QgsField &field, fields )
1806 if ( field.
name() == myWidget->objectName() )
1811 addWidgetWrapper( eww );
1818 void QgsAttributeForm::afterWidgetInit()
1820 bool isFirstEww =
true;
1830 setFocusProxy( eww->
widget() );
1845 if ( e->type() == QEvent::KeyPress )
1847 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
1848 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
1859 void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet< int > &mixedValueFields, QHash< int, QVariant > &fieldSharedValues )
const 1861 mixedValueFields.clear();
1862 fieldSharedValues.clear();
1868 for (
int i = 0; i < mLayer->
fields().
count(); ++i )
1870 if ( mixedValueFields.contains( i ) )
1875 fieldSharedValues[i] = f.
attribute( i );
1879 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
1881 fieldSharedValues.remove( i );
1882 mixedValueFields.insert( i );
1888 if ( mixedValueFields.count() == mLayer->
fields().
count() )
1897 void QgsAttributeForm::layerSelectionChanged()
1909 resetMultiEdit(
true );
1916 mIsSettingMultiEditFeatures =
true;
1917 mMultiEditFeatureIds = fids;
1919 if ( fids.isEmpty() )
1922 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
1923 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
1925 wIt.value()->initialize( QVariant() );
1927 mIsSettingMultiEditFeatures =
false;
1934 QSet< int > mixedValueFields;
1935 QHash< int, QVariant > fieldSharedValues;
1936 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
1943 Q_FOREACH (
int field, mixedValueFields )
1947 w->initialize( firstFeature.
attribute( field ), true );
1950 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
1951 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
1955 w->initialize( sharedValueIt.value(), false );
1958 mIsSettingMultiEditFeatures =
false;
1963 if ( mOwnsMessageBar )
1965 mOwnsMessageBar =
false;
1966 mMessageBar = messageBar;
1976 QStringList filters;
1979 QString filter = widget->currentFilterExpression();
1980 if ( !filter.isNull() )
1981 filters <<
'(' + filter +
')';
1984 return filters.join( QStringLiteral(
" AND " ) );
1987 int QgsAttributeForm::messageTimeout()
1990 return settings.
value( QStringLiteral(
"qgis/messageTimeout" ), 5 ).toInt();
1995 bool newVisibility = expression.evaluate( expressionContext ).toBool();
1997 if ( newVisibility != isVisible )
2001 tabWidget->setTabVisible( widget, newVisibility );
2005 widget->setVisible( newVisibility );
2008 isVisible = newVisibility;
2018 if ( infos.count() == 0 || !currentFormFeature( formFeature ) )
2021 const QString hint = tr(
"No feature joined" );
2029 mJoinedFeatures[info] = joinFeature;
2035 Q_FOREACH (
const QString &field, subsetNames )
2039 QString hintText = hint;
2053 for (
const QgsField &field : joinFields )
2057 QString hintText = hint;
2071 bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const 2083 editable = fieldIsEditable( *( info->
joinLayer() ), srcFieldIndex, mFeature.
id() );
2086 editable = fieldIsEditable( *mLayer, fieldIndex, mFeature.
id() );
2103 mIconMap[eww->
widget()]->hide();
2117 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
2118 const QString tooltip = tr(
"Join settings do not allow editing" );
2119 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2123 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
2124 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
2125 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2129 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
2130 const QString tooltip = tr(
"Joined layer is not toggled editable" );
2131 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2137 void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
2140 sw->setToolTip( tooltip );
int lookupField(const QString &fieldName) const
Look up field's index from the field name.
bool hasSubset(bool blacklisted=true) const
Returns true if blacklisted fields is not empty or if a subset of names has been set.
bool isValid() const
Returns the validity of this feature.
Wrapper for iterator of features from vector data provider or vector layer.
Constraint was set by layer.
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
int size() const
Return number of items.
This is an abstract base class for any elements of a drag and drop form.
FieldOrigin fieldOrigin(int fieldIdx) const
Get 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.
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:
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QSet< QgsFeatureId > QgsFeatureIds
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
Get the id of the relation which shall be embedded.
int selectedFeatureCount() const
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.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
bool isEditable() const override
Returns true if the provider is in editing mode.
int count() const
Return number of items.
AttributeEditorType type() const
The type of this element.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
int indexFromName(const QString &fieldName) const
Get the field index from the field name.
bool showUnlinkButton() const
Determines if the "unlink feature" button should be shown.
const QgsFeatureIds & selectedFeatureIds() const
Return reference to identifiers of selected features.
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 null if the reference was set by layer ID and not resolved yet) ...
QgsFields fields() const override
Returns the list of fields of this layer.
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
Is emitted, when 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...
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, const bool clearAndSelect)
This signal is emitted when selection was changed.
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()
Is emitted, when edited changes successfully have been written to the data provider.
bool showLabel() const
Controls if this element should be labeled with a title (field, relation or groupname).
QString prefixedFieldName(const QgsField &field) const
Returns the prefixed name of the field.
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...
int idx() const
Return the index of the field.
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
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()
Is emitted, when editing on this layer has started.
QList< QgsAttributeEditorElement * > children() const
Get 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.
QgsVectorLayerJoinBuffer * joinBuffer()
Accessor to the join buffer object.
Set selection, removing any existing selection.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
FormMode formMode() const
Returns the form mode.
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)
Select matching features using an expression.
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.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
int columnCount() const
Get the number of columns in this group.
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.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
QgsVectorDataProvider * dataProvider() override
Returns the layer's data provider.
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.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
bool 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)
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
Get 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
Return the name of this element.
void updatedFields()
Is 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
Get 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.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) override
Adds a single feature to the sink.
QStringList * joinFieldNamesSubset() const
Get subset of fields to be used from joined layer.
A form was embedded as a widget on another form.