42 #include <QTextStream> 45 #include <QFormLayout> 46 #include <QGridLayout> 50 #include <QPushButton> 52 #include <QMessageBox> 53 #include <QToolButton> 56 int QgsAttributeForm::sFormCounter = 0;
61 , mOwnsMessageBar( true )
63 , mFormNr( sFormCounter++ )
65 , mPreventFeatureRefresh( false )
66 , mIsSettingMultiEditFeatures( false )
67 , mUnsavedMultiEditChanges( false )
68 , mEditCommandMessage( tr(
"Attributes changed" ) )
69 , mMode( SingleEditMode )
81 updateAllConstraints();
87 qDeleteAll( mInterfaces );
114 mInterfaces.append( iface );
130 if ( mUnsavedMultiEditChanges )
133 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
134 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
135 if ( res == QMessageBox::Yes )
140 clearMultiEditMessages();
142 mUnsavedMultiEditChanges =
false;
189 w->setVisible( relationWidgetsVisible );
196 mSearchButtonBox->setVisible(
false );
200 synchronizeEnabledState();
201 mSearchButtonBox->setVisible(
false );
205 resetMultiEdit(
false );
206 synchronizeEnabledState();
207 mSearchButtonBox->setVisible(
false );
211 mSearchButtonBox->setVisible(
true );
216 mSearchButtonBox->setVisible(
false );
222 mSearchButtonBox->setVisible(
false );
234 if ( eww && eww->
field().
name() == field )
244 mIsSettingFeature =
true;
255 synchronizeEnabledState();
271 mIsSettingFeature =
false;
274 bool QgsAttributeForm::saveEdits()
277 bool changedLayer =
false;
283 bool doUpdate =
false;
298 QVariant dstVar = dst.at( eww->
fieldIdx() );
299 QVariant srcVar = eww->
value();
304 bool changed = ( dstVar != srcVar && !dstVar.isNull() && !srcVar.isNull() )
305 || ( dstVar.isNull() != srcVar.isNull() );
306 if ( changed && srcVar.isValid() && fieldIsEditable( eww->
fieldIdx() ) )
331 bool res = mLayer->
addFeature( updatedFeature );
350 for (
int i = 0; i < dst.count(); ++i )
352 if ( ( dst.at( i ) == src.at( i ) && dst.at( i ).isNull() == src.at( i ).isNull() )
353 || !dst.at( i ).isValid()
354 || !fieldIsEditable( i ) )
359 QgsDebugMsg( QString(
"Updating field %1" ).arg( i ) );
360 QgsDebugMsg( QString(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
361 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ) );
362 QgsDebugMsg( QString(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
363 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ) );
365 newValues[i] = dst.at( i );
366 oldValues[i] = src.at( i );
373 if ( success && n > 0 )
400 void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
405 mUnsavedMultiEditChanges =
false;
409 void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
411 clearMultiEditMessages();
412 resetMultiEdit( link == QLatin1String(
"#apply" ) );
415 void QgsAttributeForm::filterTriggered()
417 QString filter = createFilterExpression();
423 void QgsAttributeForm::searchZoomTo()
425 QString filter = createFilterExpression();
426 if ( filter.isEmpty() )
432 void QgsAttributeForm::searchFlash()
434 QString filter = createFilterExpression();
435 if ( filter.isEmpty() )
441 void QgsAttributeForm::filterAndTriggered()
443 QString filter = createFilterExpression();
444 if ( filter.isEmpty() )
452 void QgsAttributeForm::filterOrTriggered()
454 QString filter = createFilterExpression();
455 if ( filter.isEmpty() )
463 void QgsAttributeForm::pushSelectedFeaturesMessage()
469 tr(
"%1 matching %2 selected" ).arg( count )
470 .arg( count == 1 ? tr(
"feature" ) : tr(
"features" ) ),
477 tr(
"No matching features found" ),
485 QString filter = createFilterExpression();
486 if ( filter.isEmpty() )
490 pushSelectedFeaturesMessage();
495 void QgsAttributeForm::searchSetSelection()
500 void QgsAttributeForm::searchAddToSelection()
505 void QgsAttributeForm::searchRemoveFromSelection()
510 void QgsAttributeForm::searchIntersectSelection()
515 bool QgsAttributeForm::saveMultiEdits()
519 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
520 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
535 newAttributeValues.insert( wIt.key(), w->
currentValue() );
538 if ( newAttributeValues.isEmpty() )
546 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
547 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
548 if ( res != QMessageBox::Ok )
561 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
562 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
568 clearMultiEditMessages();
581 if ( !mButtonBox->isVisible() )
582 mMessageBar->
pushItem( mMultiEditMessageBarItem );
593 wrapper->notifyAboutToSave();
631 success = saveEdits();
635 success = saveMultiEdits();
640 mUnsavedMultiEditChanges =
false;
648 mValuesInitialized =
false;
653 mValuesInitialized =
true;
665 void QgsAttributeForm::clearMultiEditMessages()
667 if ( mMultiEditUnsavedMessageBarItem )
669 if ( !mButtonBox->isVisible() )
670 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
671 mMultiEditUnsavedMessageBarItem =
nullptr;
673 if ( mMultiEditMessageBarItem )
675 if ( !mButtonBox->isVisible() )
676 mMessageBar->
popWidget( mMultiEditMessageBarItem );
677 mMultiEditMessageBarItem =
nullptr;
681 QString QgsAttributeForm::createFilterExpression()
const 686 QString filter = w->currentFilterExpression();
687 if ( !filter.isEmpty() )
691 if ( filters.isEmpty() )
694 QString filter = filters.join( QStringLiteral(
") AND (" ) ).prepend(
'(' ).append(
')' );
699 void QgsAttributeForm::onAttributeChanged(
const QVariant &value )
704 bool signalEmitted =
false;
706 if ( mValuesInitialized )
720 signalEmitted =
true;
722 updateJoinedFields( *eww );
728 if ( !mIsSettingMultiEditFeatures )
730 mUnsavedMultiEditChanges =
true;
732 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
733 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
734 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
735 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
736 clearMultiEditMessages();
739 if ( !mButtonBox->isVisible() )
740 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
750 updateConstraints( eww );
752 if ( !signalEmitted )
761 void QgsAttributeForm::updateAllConstraints()
767 updateConstraints( eww );
775 if ( currentFormFeature( ft ) )
787 updateConstraint( ft, eww );
790 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
793 updateConstraint( ft, depsEww );
796 synchronizeEnabledState();
801 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
802 for ( ContainerInformation *info : infos )
804 info->apply( &mExpressionContext );
820 if ( mJoinedFeatures.contains( info ) )
850 if ( dst.count() > eww->
fieldIdx() )
852 QVariant dstVar = dst.at( eww->
fieldIdx() );
853 QVariant srcVar = eww->
value();
856 if ( ( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() )
872 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
874 mContainerVisibilityInformation.append( info );
876 const QSet<QString> referencedColumns = info->expression.referencedColumns();
878 for (
const QString &col : referencedColumns )
880 mContainerInformationDependency[ col ].append( info );
884 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
908 void QgsAttributeForm::onAttributeAdded(
int idx )
910 mPreventFeatureRefresh =
false;
914 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
922 void QgsAttributeForm::onAttributeDeleted(
int idx )
924 mPreventFeatureRefresh =
false;
936 void QgsAttributeForm::onUpdatedFields()
938 mPreventFeatureRefresh =
false;
950 attrs[i].convert(
layer()->fields().at( i ).type() );
955 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
965 void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
973 if ( formEditorWidget )
979 QList<QgsEditorWidgetWrapper *> wDeps;
991 if ( name != ewwName )
998 for (
const QString &colName : referencedColumns )
1000 if ( name == colName )
1002 wDeps.append( eww );
1013 void QgsAttributeForm::preventFeatureRefresh()
1015 mPreventFeatureRefresh =
true;
1032 void QgsAttributeForm::synchronizeEnabledState()
1034 bool isEditable = ( mFeature.
isValid()
1050 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1051 ww->setEnabled( enabled );
1059 QStringList invalidFields, descriptions;
1060 bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
1062 isEditable = isEditable & validConstraint;
1066 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1068 okButton->setEnabled( isEditable );
1071 void QgsAttributeForm::init()
1073 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1076 QWidget *formWidget =
nullptr;
1078 bool buttonBoxVisible =
true;
1082 buttonBoxVisible = mButtonBox->isVisible();
1084 mButtonBox =
nullptr;
1087 if ( mSearchButtonBox )
1089 delete mSearchButtonBox;
1090 mSearchButtonBox =
nullptr;
1093 qDeleteAll( mWidgets );
1096 while ( QWidget *w = this->findChild<QWidget *>() )
1102 QVBoxLayout *vl =
new QVBoxLayout();
1104 vl->setContentsMargins( 0, 0, 0, 0 );
1106 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1107 vl->addWidget( mMessageBar );
1112 QGridLayout *layout =
new QGridLayout();
1113 QWidget *container =
new QWidget();
1114 container->setLayout( layout );
1115 vl->addWidget( container );
1117 mFormEditorWidgets.clear();
1118 mFormWidgets.clear();
1121 setContentsMargins( 0, 0, 0, 0 );
1130 if ( file && file->open( QFile::ReadOnly ) )
1134 QFileInfo fi( file->fileName() );
1135 loader.setWorkingDirectory( fi.dir() );
1136 formWidget = loader.load( file,
this );
1139 formWidget->setWindowFlags( Qt::Widget );
1140 layout->addWidget( formWidget );
1143 mButtonBox = findChild<QDialogButtonBox *>();
1146 formWidget->installEventFilter(
this );
1158 int columnCount = 1;
1167 if ( !containerDef )
1172 tabWidget =
nullptr;
1173 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1174 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1175 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1183 layout->addWidget( tabWidget, row, column, 1, 2 );
1187 QWidget *tabPage =
new QWidget( tabWidget );
1189 tabWidget->addTab( tabPage, widgDef->name() );
1193 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1195 QGridLayout *tabPageLayout =
new QGridLayout();
1196 tabPage->setLayout( tabPageLayout );
1198 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1199 tabPageLayout->addWidget( widgetInfo.widget );
1204 tabWidget =
nullptr;
1205 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1206 QLabel *label =
new QLabel( widgetInfo.labelText );
1207 label->setToolTip( QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( widgetInfo.labelText, widgetInfo.hint ) );
1208 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1210 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1213 label->setBuddy( widgetInfo.widget );
1215 if ( !widgetInfo.showLabel )
1217 QVBoxLayout *
c =
new QVBoxLayout();
1218 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1219 c->addWidget( widgetInfo.widget );
1220 layout->addLayout( c, row, column, 1, 2 );
1223 else if ( widgetInfo.labelOnTop )
1225 QVBoxLayout *
c =
new QVBoxLayout();
1226 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1227 c->addWidget( label );
1228 c->addWidget( widgetInfo.widget );
1229 layout->addLayout( c, row, column, 1, 2 );
1234 layout->addWidget( label, row, column++ );
1235 layout->addWidget( widgetInfo.widget, row, column++ );
1239 if ( column >= columnCount * 2 )
1245 formWidget = container;
1254 formWidget =
new QWidget(
this );
1255 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1256 formWidget->setLayout( gridLayout );
1262 scrollArea->setWidget( formWidget );
1263 scrollArea->setWidgetResizable(
true );
1264 scrollArea->setFrameShape( QFrame::NoFrame );
1265 scrollArea->setFrameShadow( QFrame::Plain );
1266 scrollArea->setFocusProxy(
this );
1267 layout->addWidget( scrollArea );
1271 layout->addWidget( formWidget );
1278 for (
const QgsField &field : fields )
1289 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1295 QLabel *l =
new QLabel( fieldName );
1296 l->setToolTip( QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( fieldName, field.comment() ) );
1297 QSvgWidget *i =
new QSvgWidget();
1298 i->setFixedSize( 18, 18 );
1302 QWidget *w =
nullptr;
1307 mFormEditorWidgets.insert( idx, formWidget );
1308 mFormWidgets.append( formWidget );
1311 l->setBuddy( eww->
widget() );
1315 w =
new QLabel( QStringLiteral(
"<p style=\"color: red; font-style: italic;\">%1</p>" ).arg( tr(
"Failed to create widget with type '%1'" ), widgetSetup.
type() ) );
1320 w->setObjectName( field.name() );
1324 addWidgetWrapper( eww );
1325 mIconMap[eww->
widget()] = i;
1330 gridLayout->addWidget( l, row++, 0, 1, 2 );
1331 gridLayout->addWidget( w, row++, 0, 1, 2 );
1332 gridLayout->addWidget( i, row++, 0, 1, 2 );
1336 gridLayout->addWidget( l, row, 0 );
1337 gridLayout->addWidget( w, row, 1 );
1338 gridLayout->addWidget( i, row++, 2 );
1351 gridLayout->addWidget( formWidget, row++, 0, 1, 2 );
1353 mWidgets.append( rww );
1354 mFormWidgets.append( formWidget );
1359 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1360 gridLayout->addItem( spacerItem, row, 0 );
1361 gridLayout->setRowStretch( row, 1 );
1368 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1369 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1370 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1372 mButtonBox->setVisible( buttonBoxVisible );
1374 if ( !mSearchButtonBox )
1376 mSearchButtonBox =
new QWidget();
1377 QHBoxLayout *boxLayout =
new QHBoxLayout();
1378 boxLayout->setMargin( 0 );
1379 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1380 mSearchButtonBox->setLayout( boxLayout );
1381 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1383 QPushButton *clearButton =
new QPushButton( tr(
"&Reset form" ), mSearchButtonBox );
1385 boxLayout->addWidget( clearButton );
1386 boxLayout->addStretch( 1 );
1388 QPushButton *flashButton =
new QPushButton();
1389 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1390 flashButton->setText( tr(
"&Flash features" ) );
1391 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1392 boxLayout->addWidget( flashButton );
1394 QPushButton *zoomButton =
new QPushButton();
1395 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1396 zoomButton->setText( tr(
"&Zoom to features" ) );
1397 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
1398 boxLayout->addWidget( zoomButton );
1400 QToolButton *selectButton =
new QToolButton();
1401 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1402 selectButton->setText( tr(
"&Select features" ) );
1404 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1405 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1406 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
1407 QMenu *selectMenu =
new QMenu( selectButton );
1408 QAction *selectAction =
new QAction( tr(
"Select features" ), selectMenu );
1410 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
1411 selectMenu->addAction( selectAction );
1412 QAction *addSelectAction =
new QAction( tr(
"Add to current selection" ), selectMenu );
1414 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
1415 selectMenu->addAction( addSelectAction );
1416 QAction *deselectAction =
new QAction( tr(
"Remove from current selection" ), selectMenu );
1418 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
1419 selectMenu->addAction( deselectAction );
1420 QAction *filterSelectAction =
new QAction( tr(
"Filter current selection" ), selectMenu );
1422 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
1423 selectMenu->addAction( filterSelectAction );
1424 selectButton->setMenu( selectMenu );
1425 boxLayout->addWidget( selectButton );
1429 QToolButton *filterButton =
new QToolButton();
1430 filterButton->setText( tr(
"Filter features" ) );
1431 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
1432 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1433 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
1434 QMenu *filterMenu =
new QMenu( filterButton );
1435 QAction *filterAndAction =
new QAction( tr(
"Filter within (\"AND\")" ), filterMenu );
1436 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
1437 filterMenu->addAction( filterAndAction );
1438 QAction *filterOrAction =
new QAction( tr(
"Extend filter (\"OR\")" ), filterMenu );
1439 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
1440 filterMenu->addAction( filterOrAction );
1441 filterButton->setMenu( filterMenu );
1442 boxLayout->addWidget( filterButton );
1446 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
1448 closeButton->setShortcut( Qt::Key_Escape );
1449 boxLayout->addWidget( closeButton );
1452 layout->addWidget( mSearchButtonBox );
1454 mSearchButtonBox->setVisible( mMode ==
SearchMode );
1474 QApplication::restoreOverrideCursor();
1477 void QgsAttributeForm::cleanPython()
1479 if ( !mPyFormVarName.isNull() )
1481 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
1486 void QgsAttributeForm::initPython()
1503 if ( ! initFilePath.isEmpty() )
1505 QFile inputFile( initFilePath );
1507 if ( inputFile.open( QFile::ReadOnly ) )
1510 QTextStream inf( &inputFile );
1511 initCode = inf.readAll();
1516 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
1527 if ( initCode.isEmpty() )
1529 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
1541 if ( ! initCode.isEmpty() )
1550 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
1552 static int sFormId = 0;
1553 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
1555 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
1556 .arg( mPyFormVarName )
1557 .arg( ( quint64 )
this );
1561 QgsDebugMsg( QString(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
1564 if ( numArgs == QLatin1String(
"3" ) )
1572 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 ) );
1575 QString expr = QString(
"%1(%2)" )
1576 .arg( mLayer->editFormInit() )
1577 .arg( mPyFormVarName );
1578 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
1588 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 ) );
1596 WidgetInfo newWidgetInfo;
1598 switch ( widgetDef->
type() )
1608 if ( fldIdx < fields.
count() && fldIdx >= 0 )
1614 mFormEditorWidgets.insert( fldIdx, formWidget );
1615 mFormWidgets.append( formWidget );
1619 newWidgetInfo.widget = formWidget;
1620 addWidgetWrapper( eww );
1622 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
1623 newWidgetInfo.hint = fields.
at( fieldDef->
idx() ).comment();
1628 newWidgetInfo.showLabel = widgetDef->
showLabel();
1647 mWidgets.append( rww );
1648 mFormWidgets.append( formWidget );
1650 newWidgetInfo.widget = formWidget;
1651 newWidgetInfo.labelText = QString();
1652 newWidgetInfo.labelOnTop =
true;
1664 if ( columnCount <= 0 )
1667 QWidget *myContainer =
nullptr;
1670 QGroupBox *groupBox =
new QGroupBox( parent );
1672 groupBox->setTitle( container->
name() );
1673 myContainer = groupBox;
1674 newWidgetInfo.widget = myContainer;
1678 myContainer =
new QWidget();
1684 scrollArea->setWidget( myContainer );
1685 scrollArea->setWidgetResizable(
true );
1686 scrollArea->setFrameShape( QFrame::NoFrame );
1688 newWidgetInfo.widget = scrollArea;
1692 newWidgetInfo.widget = myContainer;
1696 QGridLayout *gbLayout =
new QGridLayout();
1697 myContainer->setLayout( gbLayout );
1702 QList<QgsAttributeEditorElement *> children = container->
children();
1706 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
1711 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1714 if ( widgetInfo.labelText.isNull() )
1716 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1721 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
1722 mypLabel->setToolTip( QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( widgetInfo.labelText, widgetInfo.hint ) );
1723 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1725 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1728 mypLabel->setBuddy( widgetInfo.widget );
1730 if ( widgetInfo.labelOnTop )
1732 QVBoxLayout *
c =
new QVBoxLayout();
1733 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1734 c->layout()->addWidget( mypLabel );
1735 c->layout()->addWidget( widgetInfo.widget );
1736 gbLayout->addLayout( c, row, column, 1, 2 );
1741 gbLayout->addWidget( mypLabel, row, column++ );
1742 gbLayout->addWidget( widgetInfo.widget, row, column++ );
1746 if ( column >= columnCount * 2 )
1752 QWidget *spacer =
new QWidget();
1753 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
1754 gbLayout->addWidget( spacer, ++row, 0 );
1755 gbLayout->setRowStretch( row, 1 );
1757 newWidgetInfo.labelText = QString();
1758 newWidgetInfo.labelOnTop =
true;
1763 QgsDebugMsg(
"Unknown attribute editor widget type encountered..." );
1767 newWidgetInfo.showLabel = widgetDef->
showLabel();
1769 return newWidgetInfo;
1788 mWidgets.append( eww );
1791 void QgsAttributeForm::createWrappers()
1793 QList<QWidget *> myWidgets = findChildren<QWidget *>();
1794 const QList<QgsField> fields = mLayer->
fields().
toList();
1796 Q_FOREACH ( QWidget *myWidget, myWidgets )
1799 QVariant vRel = myWidget->property(
"qgisRelation" );
1800 if ( vRel.isValid() )
1810 mWidgets.append( rww );
1815 Q_FOREACH (
const QgsField &field, fields )
1817 if ( field.
name() == myWidget->objectName() )
1822 addWidgetWrapper( eww );
1829 void QgsAttributeForm::afterWidgetInit()
1831 bool isFirstEww =
true;
1841 setFocusProxy( eww->
widget() );
1856 if ( e->type() == QEvent::KeyPress )
1858 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
1859 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
1870 void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet< int > &mixedValueFields, QHash< int, QVariant > &fieldSharedValues )
const 1872 mixedValueFields.clear();
1873 fieldSharedValues.clear();
1879 for (
int i = 0; i < mLayer->
fields().
count(); ++i )
1881 if ( mixedValueFields.contains( i ) )
1886 fieldSharedValues[i] = f.
attribute( i );
1890 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
1892 fieldSharedValues.remove( i );
1893 mixedValueFields.insert( i );
1899 if ( mixedValueFields.count() == mLayer->
fields().
count() )
1908 void QgsAttributeForm::layerSelectionChanged()
1920 resetMultiEdit(
true );
1927 mIsSettingMultiEditFeatures =
true;
1928 mMultiEditFeatureIds = fids;
1930 if ( fids.isEmpty() )
1933 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
1934 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
1936 wIt.value()->initialize( QVariant() );
1938 mIsSettingMultiEditFeatures =
false;
1945 QSet< int > mixedValueFields;
1946 QHash< int, QVariant > fieldSharedValues;
1947 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
1954 Q_FOREACH (
int field, mixedValueFields )
1958 w->initialize( firstFeature.
attribute( field ), true );
1961 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
1962 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
1966 w->initialize( sharedValueIt.value(), false );
1969 mIsSettingMultiEditFeatures =
false;
1974 if ( mOwnsMessageBar )
1976 mOwnsMessageBar =
false;
1977 mMessageBar = messageBar;
1987 QStringList filters;
1990 QString filter = widget->currentFilterExpression();
1991 if ( !filter.isNull() )
1992 filters <<
'(' + filter +
')';
1995 return filters.join( QStringLiteral(
" AND " ) );
1998 int QgsAttributeForm::messageTimeout()
2001 return settings.
value( QStringLiteral(
"qgis/messageTimeout" ), 5 ).toInt();
2006 bool newVisibility = expression.evaluate( expressionContext ).toBool();
2008 if ( newVisibility != isVisible )
2012 tabWidget->setTabVisible( widget, newVisibility );
2016 widget->setVisible( newVisibility );
2019 isVisible = newVisibility;
2032 if ( infos.count() == 0 || !currentFormFeature( formFeature ) )
2035 const QString hint = tr(
"No feature joined" );
2043 mJoinedFeatures[info] = joinFeature;
2049 Q_FOREACH (
const QString &field, subsetNames )
2053 QString hintText = hint;
2067 for (
const QgsField &field : joinFields )
2071 QString hintText = hint;
2085 bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const 2097 editable = fieldIsEditable( *( info->
joinLayer() ), srcFieldIndex, mFeature.
id() );
2100 editable = fieldIsEditable( *mLayer, fieldIndex, mFeature.
id() );
2117 mIconMap[eww->
widget()]->hide();
2131 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
2132 const QString tooltip = tr(
"Join settings do not allow editing" );
2133 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2137 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
2138 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
2139 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2143 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
2144 const QString tooltip = tr(
"Joined layer is not toggled editable" );
2145 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2151 void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
2154 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.
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.
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
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:
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.
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
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.
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...
bool isEditable() const override
Returns true if the provider is in editing mode.
int count() const
Returns number of items.
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
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.
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 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...
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...
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
This signal is emitted when selection was changed.
int idx() const
Returns 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
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.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns 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.
QFile * localFile(const QString &filePathOrUrl)
Returns a QFile from a local file or to a temporary file previously fetched by the registry...
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.
int columnCount() const
Gets 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.
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.
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
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()
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
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.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) override
Adds a single feature to the sink.
QStringList * joinFieldNamesSubset() const
Gets subset of fields to be used from joined layer.
A form was embedded as a widget on another form.