45 #include <QTextStream> 48 #include <QFormLayout> 49 #include <QGridLayout> 53 #include <QPushButton> 55 #include <QMessageBox> 56 #include <QToolButton> 59 int QgsAttributeForm::sFormCounter = 0;
64 , mOwnsMessageBar( true )
66 , mFormNr( sFormCounter++ )
68 , mPreventFeatureRefresh( false )
69 , mIsSettingMultiEditFeatures( false )
70 , mUnsavedMultiEditChanges( false )
71 , mEditCommandMessage( tr(
"Attributes changed" ) )
84 updateContainersVisibility();
90 qDeleteAll( mInterfaces );
117 mInterfaces.append( iface );
133 if ( mUnsavedMultiEditChanges )
136 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
137 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
138 if ( res == QMessageBox::Yes )
143 clearMultiEditMessages();
145 mUnsavedMultiEditChanges =
false;
193 w->setContext( newContext );
199 w->setVisible( relationWidgetsVisible );
206 mSearchButtonBox->setVisible(
false );
210 synchronizeEnabledState();
211 mSearchButtonBox->setVisible(
false );
215 resetMultiEdit(
false );
216 synchronizeEnabledState();
217 mSearchButtonBox->setVisible(
false );
221 mSearchButtonBox->setVisible(
true );
226 mSearchButtonBox->setVisible(
false );
232 mSearchButtonBox->setVisible(
false );
244 if ( eww && eww->
field().
name() == field )
254 mIsSettingFeature =
true;
265 synchronizeEnabledState();
285 mIsSettingFeature =
false;
288 bool QgsAttributeForm::saveEdits()
291 bool changedLayer =
false;
297 bool doUpdate =
false;
312 QVariant dstVar = dst.at( eww->
fieldIdx() );
313 QVariant srcVar = eww->
value();
318 bool changed = ( dstVar != srcVar && !dstVar.isNull() && !srcVar.isNull() )
319 || ( dstVar.isNull() != srcVar.isNull() );
320 if ( changed && srcVar.isValid() && fieldIsEditable( eww->
fieldIdx() ) )
345 bool res = mLayer->
addFeature( updatedFeature );
364 for (
int i = 0; i < dst.count(); ++i )
367 || !dst.at( i ).isValid()
368 || !fieldIsEditable( i ) )
373 QgsDebugMsg( QStringLiteral(
"Updating field %1" ).arg( i ) );
374 QgsDebugMsg( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
375 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ) );
376 QgsDebugMsg( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
377 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ) );
379 newValues[i] = dst.at( i );
380 oldValues[i] = src.at( i );
387 if ( success && n > 0 )
414 void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
419 mUnsavedMultiEditChanges =
false;
423 void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
425 clearMultiEditMessages();
426 resetMultiEdit( link == QLatin1String(
"#apply" ) );
429 void QgsAttributeForm::filterTriggered()
431 QString filter = createFilterExpression();
437 void QgsAttributeForm::searchZoomTo()
439 QString filter = createFilterExpression();
440 if ( filter.isEmpty() )
446 void QgsAttributeForm::searchFlash()
448 QString filter = createFilterExpression();
449 if ( filter.isEmpty() )
455 void QgsAttributeForm::filterAndTriggered()
457 QString filter = createFilterExpression();
458 if ( filter.isEmpty() )
466 void QgsAttributeForm::filterOrTriggered()
468 QString filter = createFilterExpression();
469 if ( filter.isEmpty() )
477 void QgsAttributeForm::pushSelectedFeaturesMessage()
483 tr(
"%n matching feature(s) selected",
"matching features", count ),
490 tr(
"No matching features found" ),
498 QString filter = createFilterExpression();
499 if ( filter.isEmpty() )
503 pushSelectedFeaturesMessage();
508 void QgsAttributeForm::searchSetSelection()
513 void QgsAttributeForm::searchAddToSelection()
518 void QgsAttributeForm::searchRemoveFromSelection()
523 void QgsAttributeForm::searchIntersectSelection()
528 bool QgsAttributeForm::saveMultiEdits()
532 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
533 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
548 newAttributeValues.insert( wIt.key(), w->
currentValue() );
551 if ( newAttributeValues.isEmpty() )
559 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
560 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
561 if ( res != QMessageBox::Ok )
574 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
575 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
581 clearMultiEditMessages();
594 if ( !mButtonBox->isVisible() )
595 mMessageBar->
pushItem( mMultiEditMessageBarItem );
606 wrapper->notifyAboutToSave();
644 success = saveEdits();
648 success = saveMultiEdits();
653 mUnsavedMultiEditChanges =
false;
661 mValuesInitialized =
false;
666 mValuesInitialized =
true;
678 void QgsAttributeForm::clearMultiEditMessages()
680 if ( mMultiEditUnsavedMessageBarItem )
682 if ( !mButtonBox->isVisible() )
683 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
684 mMultiEditUnsavedMessageBarItem =
nullptr;
686 if ( mMultiEditMessageBarItem )
688 if ( !mButtonBox->isVisible() )
689 mMessageBar->
popWidget( mMultiEditMessageBarItem );
690 mMultiEditMessageBarItem =
nullptr;
694 QString QgsAttributeForm::createFilterExpression()
const 699 QString filter = w->currentFilterExpression();
700 if ( !filter.isEmpty() )
704 if ( filters.isEmpty() )
707 QString filter = filters.join( QStringLiteral(
") AND (" ) ).prepend(
'(' ).append(
')' );
712 void QgsAttributeForm::onAttributeChanged(
const QVariant &value )
717 bool signalEmitted =
false;
719 if ( mValuesInitialized )
733 signalEmitted =
true;
735 updateJoinedFields( *eww );
741 if ( !mIsSettingMultiEditFeatures )
743 mUnsavedMultiEditChanges =
true;
745 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
746 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
747 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
748 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
749 clearMultiEditMessages();
752 if ( !mButtonBox->isVisible() )
753 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
763 updateConstraints( eww );
765 if ( !signalEmitted )
774 void QgsAttributeForm::updateAllConstraints()
780 updateConstraints( eww );
788 if ( currentFormFeature( ft ) )
800 updateConstraint( ft, eww );
803 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
806 updateConstraint( ft, depsEww );
809 synchronizeEnabledState();
816 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
817 for ( ContainerInformation *info : infos )
819 info->apply( &mExpressionContext );
824 void QgsAttributeForm::updateContainersVisibility()
828 const QVector<ContainerInformation *> infos = mContainerVisibilityInformation;
830 for ( ContainerInformation *info : infos )
832 info->apply( &mExpressionContext );
836 updateAllConstraints();
850 if ( mJoinedFeatures.contains( info ) )
880 if ( dst.count() > eww->
fieldIdx() )
882 QVariant dstVar = dst.at( eww->
fieldIdx() );
883 QVariant srcVar = eww->
value();
886 if ( ( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() )
902 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
904 mContainerVisibilityInformation.append( info );
906 const QSet<QString> referencedColumns = info->expression.referencedColumns();
908 for (
const QString &col : referencedColumns )
910 mContainerInformationDependency[ col ].append( info );
914 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
938 void QgsAttributeForm::onAttributeAdded(
int idx )
940 mPreventFeatureRefresh =
false;
944 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
952 void QgsAttributeForm::onAttributeDeleted(
int idx )
954 mPreventFeatureRefresh =
false;
966 void QgsAttributeForm::onUpdatedFields()
968 mPreventFeatureRefresh =
false;
980 attrs[i].convert(
layer()->fields().at( i ).type() );
985 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
995 void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1003 if ( formEditorWidget )
1009 QList<QgsEditorWidgetWrapper *> wDeps;
1021 if ( name != ewwName )
1028 for (
const QString &colName : referencedColumns )
1030 if ( name == colName )
1032 wDeps.append( eww );
1043 void QgsAttributeForm::preventFeatureRefresh()
1045 mPreventFeatureRefresh =
true;
1062 void QgsAttributeForm::synchronizeEnabledState()
1064 bool isEditable = ( mFeature.
isValid()
1080 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1081 ww->setEnabled( enabled );
1089 QStringList invalidFields, descriptions;
1090 bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
1092 isEditable = isEditable & validConstraint;
1096 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1098 okButton->setEnabled( isEditable );
1101 void QgsAttributeForm::init()
1103 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1106 QWidget *formWidget =
nullptr;
1108 bool buttonBoxVisible =
true;
1112 buttonBoxVisible = mButtonBox->isVisible();
1114 mButtonBox =
nullptr;
1117 if ( mSearchButtonBox )
1119 delete mSearchButtonBox;
1120 mSearchButtonBox =
nullptr;
1123 qDeleteAll( mWidgets );
1126 while ( QWidget *w = this->findChild<QWidget *>() )
1132 QVBoxLayout *vl =
new QVBoxLayout();
1134 vl->setContentsMargins( 0, 0, 0, 0 );
1136 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1137 vl->addWidget( mMessageBar );
1142 QGridLayout *layout =
new QGridLayout();
1143 QWidget *container =
new QWidget();
1144 container->setLayout( layout );
1145 vl->addWidget( container );
1147 mFormEditorWidgets.clear();
1148 mFormWidgets.clear();
1151 setContentsMargins( 0, 0, 0, 0 );
1160 if ( file && file->open( QFile::ReadOnly ) )
1164 QFileInfo fi( file->fileName() );
1165 loader.setWorkingDirectory( fi.dir() );
1166 formWidget = loader.load( file,
this );
1169 formWidget->setWindowFlags( Qt::Widget );
1170 layout->addWidget( formWidget );
1173 mButtonBox = findChild<QDialogButtonBox *>();
1176 formWidget->installEventFilter(
this );
1188 int columnCount = 1;
1197 if ( !containerDef )
1202 tabWidget =
nullptr;
1203 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1204 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1207 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1216 layout->addWidget( tabWidget, row, column, 1, 2 );
1220 QWidget *tabPage =
new QWidget( tabWidget );
1222 tabWidget->addTab( tabPage, widgDef->name() );
1226 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1228 QGridLayout *tabPageLayout =
new QGridLayout();
1229 tabPage->setLayout( tabPageLayout );
1231 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1232 tabPageLayout->addWidget( widgetInfo.widget );
1237 tabWidget =
nullptr;
1238 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1239 QLabel *label =
new QLabel( widgetInfo.labelText );
1240 label->setToolTip( widgetInfo.toolTip );
1241 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1243 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1246 label->setBuddy( widgetInfo.widget );
1248 if ( !widgetInfo.showLabel )
1250 QVBoxLayout *
c =
new QVBoxLayout();
1251 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1252 c->addWidget( widgetInfo.widget );
1253 layout->addLayout( c, row, column, 1, 2 );
1256 else if ( widgetInfo.labelOnTop )
1258 QVBoxLayout *
c =
new QVBoxLayout();
1259 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1260 c->addWidget( label );
1261 c->addWidget( widgetInfo.widget );
1262 layout->addLayout( c, row, column, 1, 2 );
1267 layout->addWidget( label, row, column++ );
1268 layout->addWidget( widgetInfo.widget, row, column++ );
1272 if ( column >= columnCount * 2 )
1278 formWidget = container;
1287 formWidget =
new QWidget(
this );
1288 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1289 formWidget->setLayout( gridLayout );
1295 scrollArea->setWidget( formWidget );
1296 scrollArea->setWidgetResizable(
true );
1297 scrollArea->setFrameShape( QFrame::NoFrame );
1298 scrollArea->setFrameShadow( QFrame::Plain );
1299 scrollArea->setFocusProxy(
this );
1300 layout->addWidget( scrollArea );
1304 layout->addWidget( formWidget );
1311 for (
const QgsField &field : fields )
1319 QString labelText = fieldName;
1320 labelText.replace(
'&', QStringLiteral(
"&&" ) );
1324 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1330 QLabel *l =
new QLabel( labelText );
1331 l->setToolTip( QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( fieldName, field.comment() ) );
1332 QSvgWidget *i =
new QSvgWidget();
1333 i->setFixedSize( 18, 18 );
1337 QWidget *w =
nullptr;
1342 mFormEditorWidgets.insert( idx, formWidget );
1343 mFormWidgets.append( formWidget );
1346 l->setBuddy( eww->
widget() );
1350 w =
new QLabel( QStringLiteral(
"<p style=\"color: red; font-style: italic;\">%1</p>" ).arg( tr(
"Failed to create widget with type '%1'" ), widgetSetup.
type() ) );
1355 w->setObjectName( field.name() );
1359 addWidgetWrapper( eww );
1360 mIconMap[eww->
widget()] = i;
1365 gridLayout->addWidget( l, row++, 0, 1, 2 );
1366 gridLayout->addWidget( w, row++, 0, 1, 2 );
1367 gridLayout->addWidget( i, row++, 0, 1, 2 );
1371 gridLayout->addWidget( l, row, 0 );
1372 gridLayout->addWidget( w, row, 1 );
1373 gridLayout->addWidget( i, row++, 2 );
1386 gridLayout->addWidget( formWidget, row++, 0, 1, 2 );
1388 mWidgets.append( rww );
1389 mFormWidgets.append( formWidget );
1394 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1395 gridLayout->addItem( spacerItem, row, 0 );
1396 gridLayout->setRowStretch( row, 1 );
1403 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1404 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1405 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1407 mButtonBox->setVisible( buttonBoxVisible );
1409 if ( !mSearchButtonBox )
1411 mSearchButtonBox =
new QWidget();
1412 QHBoxLayout *boxLayout =
new QHBoxLayout();
1413 boxLayout->setMargin( 0 );
1414 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1415 mSearchButtonBox->setLayout( boxLayout );
1416 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1418 QPushButton *clearButton =
new QPushButton( tr(
"&Reset Form" ), mSearchButtonBox );
1420 boxLayout->addWidget( clearButton );
1421 boxLayout->addStretch( 1 );
1423 QPushButton *flashButton =
new QPushButton();
1424 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1425 flashButton->setText( tr(
"&Flash Features" ) );
1426 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1427 boxLayout->addWidget( flashButton );
1429 QPushButton *zoomButton =
new QPushButton();
1430 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1431 zoomButton->setText( tr(
"&Zoom to Features" ) );
1432 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
1433 boxLayout->addWidget( zoomButton );
1435 QToolButton *selectButton =
new QToolButton();
1436 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1437 selectButton->setText( tr(
"&Select Features" ) );
1439 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1440 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1441 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
1442 QMenu *selectMenu =
new QMenu( selectButton );
1443 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
1445 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
1446 selectMenu->addAction( selectAction );
1447 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
1449 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
1450 selectMenu->addAction( addSelectAction );
1451 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
1453 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
1454 selectMenu->addAction( deselectAction );
1455 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
1457 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
1458 selectMenu->addAction( filterSelectAction );
1459 selectButton->setMenu( selectMenu );
1460 boxLayout->addWidget( selectButton );
1464 QToolButton *filterButton =
new QToolButton();
1465 filterButton->setText( tr(
"Filter features" ) );
1466 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
1467 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1468 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
1469 QMenu *filterMenu =
new QMenu( filterButton );
1470 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
1471 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
1472 filterMenu->addAction( filterAndAction );
1473 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
1474 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
1475 filterMenu->addAction( filterOrAction );
1476 filterButton->setMenu( filterMenu );
1477 boxLayout->addWidget( filterButton );
1481 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
1483 closeButton->setShortcut( Qt::Key_Escape );
1484 boxLayout->addWidget( closeButton );
1487 layout->addWidget( mSearchButtonBox );
1515 QApplication::restoreOverrideCursor();
1518 void QgsAttributeForm::cleanPython()
1520 if ( !mPyFormVarName.isNull() )
1522 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
1527 void QgsAttributeForm::initPython()
1544 if ( ! initFilePath.isEmpty() )
1546 QFile inputFile( initFilePath );
1548 if ( inputFile.open( QFile::ReadOnly ) )
1551 QTextStream inf( &inputFile );
1552 initCode = inf.readAll();
1557 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
1568 if ( initCode.isEmpty() )
1570 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
1582 if ( ! initCode.isEmpty() )
1591 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
1593 static int sFormId = 0;
1594 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
1596 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
1597 .arg( mPyFormVarName )
1598 .arg( ( quint64 )
this );
1602 QgsDebugMsg( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
1605 if ( numArgs == QLatin1String(
"3" ) )
1613 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 ) );
1616 QString expr = QString(
"%1(%2)" )
1617 .arg( mLayer->editFormInit() )
1618 .arg( mPyFormVarName );
1619 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
1629 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 ) );
1637 WidgetInfo newWidgetInfo;
1639 switch ( widgetDef->
type() )
1649 if ( fldIdx < fields.
count() && fldIdx >= 0 )
1655 mFormEditorWidgets.insert( fldIdx, formWidget );
1656 mFormWidgets.append( formWidget );
1660 newWidgetInfo.widget = formWidget;
1661 addWidgetWrapper( eww );
1663 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
1664 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
1669 newWidgetInfo.labelText.replace(
'&', QStringLiteral(
"&&" ) );
1670 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
1671 newWidgetInfo.showLabel = widgetDef->
showLabel();
1690 mWidgets.append( rww );
1691 mFormWidgets.append( formWidget );
1693 newWidgetInfo.widget = formWidget;
1694 newWidgetInfo.labelText = QString();
1695 newWidgetInfo.labelOnTop =
true;
1707 if ( columnCount <= 0 )
1710 QWidget *myContainer =
nullptr;
1713 QGroupBox *groupBox =
new QGroupBox( parent );
1715 groupBox->setTitle( container->
name() );
1716 myContainer = groupBox;
1717 newWidgetInfo.widget = myContainer;
1721 myContainer =
new QWidget();
1727 scrollArea->setWidget( myContainer );
1728 scrollArea->setWidgetResizable(
true );
1729 scrollArea->setFrameShape( QFrame::NoFrame );
1731 newWidgetInfo.widget = scrollArea;
1735 newWidgetInfo.widget = myContainer;
1739 QGridLayout *gbLayout =
new QGridLayout();
1740 myContainer->setLayout( gbLayout );
1745 const QList<QgsAttributeEditorElement *> children = container->
children();
1749 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
1756 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1760 if ( widgetInfo.labelText.isNull() )
1762 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1767 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
1768 mypLabel->setToolTip( widgetInfo.toolTip );
1769 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1771 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1774 mypLabel->setBuddy( widgetInfo.widget );
1776 if ( widgetInfo.labelOnTop )
1778 QVBoxLayout *
c =
new QVBoxLayout();
1779 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1780 c->layout()->addWidget( mypLabel );
1781 c->layout()->addWidget( widgetInfo.widget );
1782 gbLayout->addLayout( c, row, column, 1, 2 );
1787 gbLayout->addWidget( mypLabel, row, column++ );
1788 gbLayout->addWidget( widgetInfo.widget, row, column++ );
1792 if ( column >= columnCount * 2 )
1798 QWidget *spacer =
new QWidget();
1799 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
1800 gbLayout->addWidget( spacer, ++row, 0 );
1801 gbLayout->setRowStretch( row, 1 );
1803 newWidgetInfo.labelText = QString();
1804 newWidgetInfo.labelOnTop =
true;
1818 mWidgets.append( qmlWrapper );
1820 newWidgetInfo.widget = qmlWrapper->
widget();
1821 newWidgetInfo.labelText = elementDef->
name();
1822 newWidgetInfo.labelOnTop =
true;
1823 newWidgetInfo.showLabel = widgetDef->
showLabel();
1828 QgsDebugMsg( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
1832 newWidgetInfo.showLabel = widgetDef->
showLabel();
1834 return newWidgetInfo;
1853 mWidgets.append( eww );
1856 void QgsAttributeForm::createWrappers()
1858 QList<QWidget *> myWidgets = findChildren<QWidget *>();
1859 const QList<QgsField> fields = mLayer->
fields().
toList();
1861 Q_FOREACH ( QWidget *myWidget, myWidgets )
1864 QVariant vRel = myWidget->property(
"qgisRelation" );
1865 if ( vRel.isValid() )
1875 mWidgets.append( rww );
1880 Q_FOREACH (
const QgsField &field, fields )
1882 if ( field.
name() == myWidget->objectName() )
1887 addWidgetWrapper( eww );
1894 void QgsAttributeForm::afterWidgetInit()
1896 bool isFirstEww =
true;
1906 setFocusProxy( eww->
widget() );
1921 if ( e->type() == QEvent::KeyPress )
1923 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
1924 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
1935 void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet< int > &mixedValueFields, QHash< int, QVariant > &fieldSharedValues )
const 1937 mixedValueFields.clear();
1938 fieldSharedValues.clear();
1944 for (
int i = 0; i < mLayer->
fields().
count(); ++i )
1946 if ( mixedValueFields.contains( i ) )
1951 fieldSharedValues[i] = f.
attribute( i );
1955 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
1957 fieldSharedValues.remove( i );
1958 mixedValueFields.insert( i );
1964 if ( mixedValueFields.count() == mLayer->
fields().
count() )
1973 void QgsAttributeForm::layerSelectionChanged()
1985 resetMultiEdit(
true );
1992 mIsSettingMultiEditFeatures =
true;
1993 mMultiEditFeatureIds = fids;
1995 if ( fids.isEmpty() )
1998 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
1999 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2001 wIt.value()->initialize( QVariant() );
2003 mIsSettingMultiEditFeatures =
false;
2010 QSet< int > mixedValueFields;
2011 QHash< int, QVariant > fieldSharedValues;
2012 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2019 Q_FOREACH (
int field, mixedValueFields )
2023 w->initialize( firstFeature.
attribute( field ), true );
2026 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2027 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2031 w->initialize( sharedValueIt.value(), false );
2034 mIsSettingMultiEditFeatures =
false;
2039 if ( mOwnsMessageBar )
2041 mOwnsMessageBar =
false;
2042 mMessageBar = messageBar;
2052 QStringList filters;
2055 QString filter = widget->currentFilterExpression();
2056 if ( !filter.isNull() )
2057 filters <<
'(' + filter +
')';
2060 return filters.join( QStringLiteral(
" AND " ) );
2063 int QgsAttributeForm::messageTimeout()
2066 return settings.
value( QStringLiteral(
"qgis/messageTimeout" ), 5 ).toInt();
2071 bool newVisibility = expression.evaluate( expressionContext ).toBool();
2073 if ( newVisibility != isVisible )
2077 tabWidget->setTabVisible( widget, newVisibility );
2081 widget->setVisible( newVisibility );
2084 isVisible = newVisibility;
2097 if ( infos.count() == 0 || !currentFormFeature( formFeature ) )
2100 const QString hint = tr(
"No feature joined" );
2108 mJoinedFeatures[info] = joinFeature;
2114 Q_FOREACH (
const QString &field, subsetNames )
2118 QString hintText = hint;
2132 for (
const QgsField &field : joinFields )
2136 QString hintText = hint;
2150 bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const 2162 editable = fieldIsEditable( *( info->
joinLayer() ), srcFieldIndex, mFeature.
id() );
2165 editable = fieldIsEditable( *mLayer, fieldIndex, mFeature.
id() );
2182 mIconMap[eww->
widget()]->hide();
2196 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
2197 const QString tooltip = tr(
"Join settings do not allow editing" );
2198 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2202 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
2203 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
2204 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2208 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
2209 const QString tooltip = tr(
"Joined layer is not toggled editable" );
2210 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2216 void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
2219 sw->setToolTip( tooltip );
int lookupField(const QString &fieldName) const
Looks 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.
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.
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 null 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
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.
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.
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.
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.
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
Query 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 null.
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()
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.
QStringList * joinFieldNamesSubset() const
Gets subset of fields to be used from joined layer.
A form was embedded as a widget on another form.