43 #include <QTextStream> 46 #include <QFormLayout> 47 #include <QGridLayout> 51 #include <QPushButton> 53 #include <QMessageBox> 54 #include <QToolButton> 57 int QgsAttributeForm::sFormCounter = 0;
62 , mOwnsMessageBar( true )
64 , mFormNr( sFormCounter++ )
66 , mPreventFeatureRefresh( false )
67 , mIsSettingMultiEditFeatures( false )
68 , mUnsavedMultiEditChanges( false )
69 , mEditCommandMessage( tr(
"Attributes changed" ) )
82 updateContainersVisibility();
88 qDeleteAll( mInterfaces );
115 mInterfaces.append( iface );
131 if ( mUnsavedMultiEditChanges )
134 int res = QMessageBox::question(
this, tr(
"Multiedit Attributes" ),
135 tr(
"Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
136 if ( res == QMessageBox::Yes )
141 clearMultiEditMessages();
143 mUnsavedMultiEditChanges =
false;
191 w->setContext( newContext );
197 w->setVisible( relationWidgetsVisible );
204 mSearchButtonBox->setVisible(
false );
208 synchronizeEnabledState();
209 mSearchButtonBox->setVisible(
false );
213 resetMultiEdit(
false );
214 synchronizeEnabledState();
215 mSearchButtonBox->setVisible(
false );
219 mSearchButtonBox->setVisible(
true );
224 mSearchButtonBox->setVisible(
false );
230 mSearchButtonBox->setVisible(
false );
242 if ( eww && eww->
field().
name() == field )
252 mIsSettingFeature =
true;
263 synchronizeEnabledState();
267 mIsSettingFeature =
false;
286 mIsSettingFeature =
false;
289 bool QgsAttributeForm::saveEdits()
292 bool changedLayer =
false;
298 bool doUpdate =
false;
313 QVariant dstVar = dst.at( eww->
fieldIdx() );
314 QVariant srcVar = eww->
value();
319 bool changed = ( dstVar != srcVar && !dstVar.isNull() && !srcVar.isNull() )
320 || ( dstVar.isNull() != srcVar.isNull() );
321 if ( changed && srcVar.isValid() && fieldIsEditable( eww->
fieldIdx() ) )
346 bool res = mLayer->
addFeature( updatedFeature );
365 for (
int i = 0; i < dst.count(); ++i )
368 || !dst.at( i ).isValid()
369 || !fieldIsEditable( i ) )
374 QgsDebugMsg( QStringLiteral(
"Updating field %1" ).arg( i ) );
375 QgsDebugMsg( QStringLiteral(
"dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
376 .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ) );
377 QgsDebugMsg( QStringLiteral(
"src:'%1' (type:%2, isNull:%3, isValid:%4)" )
378 .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ) );
380 newValues[i] = dst.at( i );
381 oldValues[i] = src.at( i );
388 if ( success && n > 0 )
415 void QgsAttributeForm::resetMultiEdit(
bool promptToSave )
420 mUnsavedMultiEditChanges =
false;
424 void QgsAttributeForm::multiEditMessageClicked(
const QString &link )
426 clearMultiEditMessages();
427 resetMultiEdit( link == QLatin1String(
"#apply" ) );
430 void QgsAttributeForm::filterTriggered()
432 QString filter = createFilterExpression();
438 void QgsAttributeForm::searchZoomTo()
440 QString filter = createFilterExpression();
441 if ( filter.isEmpty() )
447 void QgsAttributeForm::searchFlash()
449 QString filter = createFilterExpression();
450 if ( filter.isEmpty() )
456 void QgsAttributeForm::filterAndTriggered()
458 QString filter = createFilterExpression();
459 if ( filter.isEmpty() )
467 void QgsAttributeForm::filterOrTriggered()
469 QString filter = createFilterExpression();
470 if ( filter.isEmpty() )
478 void QgsAttributeForm::pushSelectedFeaturesMessage()
484 tr(
"%1 matching %2 selected" ).arg( count )
485 .arg( count == 1 ? tr(
"feature" ) : tr(
"features" ) ),
492 tr(
"No matching features found" ),
500 QString filter = createFilterExpression();
501 if ( filter.isEmpty() )
505 pushSelectedFeaturesMessage();
510 void QgsAttributeForm::searchSetSelection()
515 void QgsAttributeForm::searchAddToSelection()
520 void QgsAttributeForm::searchRemoveFromSelection()
525 void QgsAttributeForm::searchIntersectSelection()
530 bool QgsAttributeForm::saveMultiEdits()
534 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
535 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
542 || !fieldIsEditable( wIt.key() ) )
550 newAttributeValues.insert( wIt.key(), w->
currentValue() );
553 if ( newAttributeValues.isEmpty() )
561 int res = QMessageBox::information(
this, tr(
"Multiedit Attributes" ),
562 tr(
"Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
563 if ( res != QMessageBox::Ok )
576 QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
577 for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
583 clearMultiEditMessages();
596 if ( !mButtonBox->isVisible() )
597 mMessageBar->
pushItem( mMultiEditMessageBarItem );
608 wrapper->notifyAboutToSave();
646 success = saveEdits();
650 success = saveMultiEdits();
655 mUnsavedMultiEditChanges =
false;
663 mValuesInitialized =
false;
668 mValuesInitialized =
true;
680 void QgsAttributeForm::clearMultiEditMessages()
682 if ( mMultiEditUnsavedMessageBarItem )
684 if ( !mButtonBox->isVisible() )
685 mMessageBar->
popWidget( mMultiEditUnsavedMessageBarItem );
686 mMultiEditUnsavedMessageBarItem =
nullptr;
688 if ( mMultiEditMessageBarItem )
690 if ( !mButtonBox->isVisible() )
691 mMessageBar->
popWidget( mMultiEditMessageBarItem );
692 mMultiEditMessageBarItem =
nullptr;
696 QString QgsAttributeForm::createFilterExpression()
const 701 QString filter = w->currentFilterExpression();
702 if ( !filter.isEmpty() )
706 if ( filters.isEmpty() )
709 QString filter = filters.join( QStringLiteral(
") AND (" ) ).prepend(
'(' ).append(
')' );
714 void QgsAttributeForm::onAttributeChanged(
const QVariant &value )
719 bool signalEmitted =
false;
721 if ( mValuesInitialized )
735 signalEmitted =
true;
737 updateJoinedFields( *eww );
743 if ( !mIsSettingMultiEditFeatures )
745 mUnsavedMultiEditChanges =
true;
747 QLabel *msgLabel =
new QLabel( tr(
"Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
748 msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
749 msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
750 connect( msgLabel, &QLabel::linkActivated,
this, &QgsAttributeForm::multiEditMessageClicked );
751 clearMultiEditMessages();
754 if ( !mButtonBox->isVisible() )
755 mMessageBar->
pushItem( mMultiEditUnsavedMessageBarItem );
765 updateConstraints( eww );
767 if ( !signalEmitted )
776 void QgsAttributeForm::updateAllConstraints()
782 updateConstraints( eww );
790 if ( currentFormFeature( ft ) )
802 updateConstraint( ft, eww );
805 const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
808 updateConstraint( ft, depsEww );
811 synchronizeEnabledState();
818 const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->
field().
name() );
819 for ( ContainerInformation *info : infos )
821 info->apply( &mExpressionContext );
826 void QgsAttributeForm::updateContainersVisibility()
830 const QVector<ContainerInformation *> infos = mContainerVisibilityInformation;
832 for ( ContainerInformation *info : infos )
834 info->apply( &mExpressionContext );
838 updateAllConstraints();
852 if ( mJoinedFeatures.contains( info ) )
882 if ( dst.count() > eww->
fieldIdx() )
884 QVariant dstVar = dst.at( eww->
fieldIdx() );
885 QVariant srcVar = eww->
value();
888 if ( ( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() )
904 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
906 mContainerVisibilityInformation.append( info );
908 const QSet<QString> referencedColumns = info->expression.referencedColumns();
910 for (
const QString &col : referencedColumns )
912 mContainerInformationDependency[ col ].append( info );
916 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
940 void QgsAttributeForm::onAttributeAdded(
int idx )
942 mPreventFeatureRefresh =
false;
946 attrs.insert( idx, QVariant(
layer()->fields().at( idx ).type() ) );
954 void QgsAttributeForm::onAttributeDeleted(
int idx )
956 mPreventFeatureRefresh =
false;
968 void QgsAttributeForm::onUpdatedFields()
970 mPreventFeatureRefresh =
false;
982 attrs[i].convert(
layer()->fields().at( i ).type() );
987 attrs[i] = QVariant(
layer()->fields().at( i ).type() );
997 void QgsAttributeForm::onConstraintStatusChanged(
const QString &constraint,
1005 if ( formEditorWidget )
1011 QList<QgsEditorWidgetWrapper *> wDeps;
1023 if ( name != ewwName )
1030 for (
const QString &colName : referencedColumns )
1032 if ( name == colName )
1034 wDeps.append( eww );
1055 void QgsAttributeForm::preventFeatureRefresh()
1057 mPreventFeatureRefresh =
true;
1074 void QgsAttributeForm::synchronizeEnabledState()
1076 bool isEditable = ( mFeature.
isValid()
1092 bool enabled = isEditable && fieldIsEditable( eww->
fieldIdx() );
1093 ww->setEnabled( enabled );
1101 QStringList invalidFields, descriptions;
1102 bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
1104 isEditable = isEditable & validConstraint;
1108 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1110 okButton->setEnabled( isEditable );
1113 void QgsAttributeForm::init()
1115 QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1118 QWidget *formWidget =
nullptr;
1120 bool buttonBoxVisible =
true;
1124 buttonBoxVisible = mButtonBox->isVisible();
1126 mButtonBox =
nullptr;
1129 if ( mSearchButtonBox )
1131 delete mSearchButtonBox;
1132 mSearchButtonBox =
nullptr;
1135 qDeleteAll( mWidgets );
1138 while ( QWidget *w = this->findChild<QWidget *>() )
1144 QVBoxLayout *vl =
new QVBoxLayout();
1146 vl->setContentsMargins( 0, 0, 0, 0 );
1148 mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1149 vl->addWidget( mMessageBar );
1154 QGridLayout *layout =
new QGridLayout();
1155 QWidget *container =
new QWidget();
1156 container->setLayout( layout );
1157 vl->addWidget( container );
1159 mFormEditorWidgets.clear();
1160 mFormWidgets.clear();
1163 setContentsMargins( 0, 0, 0, 0 );
1172 if ( file && file->open( QFile::ReadOnly ) )
1176 QFileInfo fi( file->fileName() );
1177 loader.setWorkingDirectory( fi.dir() );
1178 formWidget = loader.load( file,
this );
1181 formWidget->setWindowFlags( Qt::Widget );
1182 layout->addWidget( formWidget );
1185 mButtonBox = findChild<QDialogButtonBox *>();
1188 formWidget->installEventFilter(
this );
1200 int columnCount = 1;
1209 if ( !containerDef )
1214 tabWidget =
nullptr;
1215 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1216 layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1219 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1228 layout->addWidget( tabWidget, row, column, 1, 2 );
1232 QWidget *tabPage =
new QWidget( tabWidget );
1234 tabWidget->addTab( tabPage, widgDef->name() );
1238 registerContainerInformation(
new ContainerInformation( tabWidget, tabPage, containerDef->
visibilityExpression().
data() ) );
1240 QGridLayout *tabPageLayout =
new QGridLayout();
1241 tabPage->setLayout( tabPageLayout );
1243 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1244 tabPageLayout->addWidget( widgetInfo.widget );
1249 tabWidget =
nullptr;
1250 WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1251 QLabel *label =
new QLabel( widgetInfo.labelText );
1252 label->setToolTip( widgetInfo.toolTip );
1253 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1255 label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1258 label->setBuddy( widgetInfo.widget );
1260 if ( !widgetInfo.showLabel )
1262 QVBoxLayout *
c =
new QVBoxLayout();
1263 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1264 c->addWidget( widgetInfo.widget );
1265 layout->addLayout( c, row, column, 1, 2 );
1268 else if ( widgetInfo.labelOnTop )
1270 QVBoxLayout *
c =
new QVBoxLayout();
1271 label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1272 c->addWidget( label );
1273 c->addWidget( widgetInfo.widget );
1274 layout->addLayout( c, row, column, 1, 2 );
1279 layout->addWidget( label, row, column++ );
1280 layout->addWidget( widgetInfo.widget, row, column++ );
1284 if ( column >= columnCount * 2 )
1290 formWidget = container;
1299 formWidget =
new QWidget(
this );
1300 QGridLayout *gridLayout =
new QGridLayout( formWidget );
1301 formWidget->setLayout( gridLayout );
1307 scrollArea->setWidget( formWidget );
1308 scrollArea->setWidgetResizable(
true );
1309 scrollArea->setFrameShape( QFrame::NoFrame );
1310 scrollArea->setFrameShadow( QFrame::Plain );
1311 scrollArea->setFocusProxy(
this );
1312 layout->addWidget( scrollArea );
1316 layout->addWidget( formWidget );
1323 for (
const QgsField &field : fields )
1331 QString labelText = fieldName;
1332 labelText.replace(
'&', QStringLiteral(
"&&" ) );
1336 if ( widgetSetup.
type() == QLatin1String(
"Hidden" ) )
1342 QLabel *l =
new QLabel( labelText );
1343 l->setToolTip( QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( fieldName, field.comment() ) );
1344 QSvgWidget *i =
new QSvgWidget();
1345 i->setFixedSize( 18, 18 );
1349 QWidget *w =
nullptr;
1354 mFormEditorWidgets.insert( idx, formWidget );
1355 mFormWidgets.append( formWidget );
1358 l->setBuddy( eww->
widget() );
1362 w =
new QLabel( QStringLiteral(
"<p style=\"color: red; font-style: italic;\">%1</p>" ).arg( tr(
"Failed to create widget with type '%1'" ).arg( widgetSetup.
type() ) ) );
1367 w->setObjectName( field.name() );
1371 addWidgetWrapper( eww );
1372 mIconMap[eww->
widget()] = i;
1377 gridLayout->addWidget( l, row++, 0, 1, 2 );
1378 gridLayout->addWidget( w, row++, 0, 1, 2 );
1379 gridLayout->addWidget( i, row++, 0, 1, 2 );
1383 gridLayout->addWidget( l, row, 0 );
1384 gridLayout->addWidget( w, row, 1 );
1385 gridLayout->addWidget( i, row++, 2 );
1395 gridLayout->addWidget( formWidget, row++, 0, 1, 2 );
1397 mWidgets.append( rww );
1398 mFormWidgets.append( formWidget );
1403 QSpacerItem *spacerItem =
new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1404 gridLayout->addItem( spacerItem, row, 0 );
1405 gridLayout->setRowStretch( row, 1 );
1412 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1413 mButtonBox->setObjectName( QStringLiteral(
"buttonBox" ) );
1414 layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1416 mButtonBox->setVisible( buttonBoxVisible );
1418 if ( !mSearchButtonBox )
1420 mSearchButtonBox =
new QWidget();
1421 QHBoxLayout *boxLayout =
new QHBoxLayout();
1422 boxLayout->setMargin( 0 );
1423 boxLayout->setContentsMargins( 0, 0, 0, 0 );
1424 mSearchButtonBox->setLayout( boxLayout );
1425 mSearchButtonBox->setObjectName( QStringLiteral(
"searchButtonBox" ) );
1427 QPushButton *clearButton =
new QPushButton( tr(
"&Reset form" ), mSearchButtonBox );
1429 boxLayout->addWidget( clearButton );
1430 boxLayout->addStretch( 1 );
1432 QPushButton *flashButton =
new QPushButton();
1433 flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1434 flashButton->setText( tr(
"&Flash features" ) );
1435 connect( flashButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchFlash );
1436 boxLayout->addWidget( flashButton );
1438 QPushButton *zoomButton =
new QPushButton();
1439 zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1440 zoomButton->setText( tr(
"&Zoom to features" ) );
1441 connect( zoomButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchZoomTo );
1442 boxLayout->addWidget( zoomButton );
1444 QToolButton *selectButton =
new QToolButton();
1445 selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1446 selectButton->setText( tr(
"&Select features" ) );
1448 selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1449 selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1450 connect( selectButton, &QToolButton::clicked,
this, &QgsAttributeForm::searchSetSelection );
1451 QMenu *selectMenu =
new QMenu( selectButton );
1452 QAction *selectAction =
new QAction( tr(
"Select Features" ), selectMenu );
1454 connect( selectAction, &QAction::triggered,
this, &QgsAttributeForm::searchSetSelection );
1455 selectMenu->addAction( selectAction );
1456 QAction *addSelectAction =
new QAction( tr(
"Add to Current Selection" ), selectMenu );
1458 connect( addSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchAddToSelection );
1459 selectMenu->addAction( addSelectAction );
1460 QAction *deselectAction =
new QAction( tr(
"Remove from Current Selection" ), selectMenu );
1462 connect( deselectAction, &QAction::triggered,
this, &QgsAttributeForm::searchRemoveFromSelection );
1463 selectMenu->addAction( deselectAction );
1464 QAction *filterSelectAction =
new QAction( tr(
"Filter Current Selection" ), selectMenu );
1466 connect( filterSelectAction, &QAction::triggered,
this, &QgsAttributeForm::searchIntersectSelection );
1467 selectMenu->addAction( filterSelectAction );
1468 selectButton->setMenu( selectMenu );
1469 boxLayout->addWidget( selectButton );
1473 QToolButton *filterButton =
new QToolButton();
1474 filterButton->setText( tr(
"Filter features" ) );
1475 filterButton->setPopupMode( QToolButton::MenuButtonPopup );
1476 filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1477 connect( filterButton, &QToolButton::clicked,
this, &QgsAttributeForm::filterTriggered );
1478 QMenu *filterMenu =
new QMenu( filterButton );
1479 QAction *filterAndAction =
new QAction( tr(
"Filter Within (\"AND\")" ), filterMenu );
1480 connect( filterAndAction, &QAction::triggered,
this, &QgsAttributeForm::filterAndTriggered );
1481 filterMenu->addAction( filterAndAction );
1482 QAction *filterOrAction =
new QAction( tr(
"Extend Filter (\"OR\")" ), filterMenu );
1483 connect( filterOrAction, &QAction::triggered,
this, &QgsAttributeForm::filterOrTriggered );
1484 filterMenu->addAction( filterOrAction );
1485 filterButton->setMenu( filterMenu );
1486 boxLayout->addWidget( filterButton );
1490 QPushButton *closeButton =
new QPushButton( tr(
"Close" ), mSearchButtonBox );
1492 closeButton->setShortcut( Qt::Key_Escape );
1493 boxLayout->addWidget( closeButton );
1496 layout->addWidget( mSearchButtonBox );
1524 QApplication::restoreOverrideCursor();
1527 void QgsAttributeForm::cleanPython()
1529 if ( !mPyFormVarName.isNull() )
1531 QString expr = QStringLiteral(
"if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
1536 void QgsAttributeForm::initPython()
1553 if ( ! initFilePath.isEmpty() )
1555 QFile inputFile( initFilePath );
1557 if ( inputFile.open( QFile::ReadOnly ) )
1560 QTextStream inf( &inputFile );
1561 initCode = inf.readAll();
1566 QgsLogger::warning( QStringLiteral(
"The external python file path %1 could not be opened!" ).arg( initFilePath ) );
1577 if ( initCode.isEmpty() )
1579 QgsLogger::warning( QStringLiteral(
"The python code provided in the dialog is empty!" ) );
1591 if ( ! initCode.isEmpty() )
1600 if (
QgsPythonRunner::eval( QStringLiteral(
"len(inspect.getargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
1602 static int sFormId = 0;
1603 mPyFormVarName = QStringLiteral(
"_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
1605 QString form = QStringLiteral(
"%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
1606 .arg( mPyFormVarName )
1607 .arg( ( quint64 )
this );
1611 QgsDebugMsg( QStringLiteral(
"running featureForm init: %1" ).arg( mPyFormVarName ) );
1614 if ( numArgs == QLatin1String(
"3" ) )
1622 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 ) );
1625 QString expr = QString(
"%1(%2)" )
1626 .arg( mLayer->editFormInit() )
1627 .arg( mPyFormVarName );
1628 QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr,
"QgsAttributeFormInterface" );
1638 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 ) );
1646 WidgetInfo newWidgetInfo;
1648 switch ( widgetDef->
type() )
1658 if ( fldIdx < fields.
count() && fldIdx >= 0 )
1664 mFormEditorWidgets.insert( fldIdx, formWidget );
1665 mFormWidgets.append( formWidget );
1669 newWidgetInfo.widget = formWidget;
1670 addWidgetWrapper( eww );
1672 newWidgetInfo.widget->setObjectName( fields.
at( fldIdx ).
name() );
1673 newWidgetInfo.hint = fields.
at( fldIdx ).
comment();
1678 newWidgetInfo.labelText.replace(
'&', QStringLiteral(
"&&" ) );
1679 newWidgetInfo.toolTip = QStringLiteral(
"<b>%1</b><p>%2</p>" ).arg( mLayer->
attributeDisplayName( fldIdx ), newWidgetInfo.hint );
1680 newWidgetInfo.showLabel = widgetDef->
showLabel();
1698 mWidgets.append( rww );
1699 mFormWidgets.append( formWidget );
1701 newWidgetInfo.widget = formWidget;
1702 newWidgetInfo.labelText = QString();
1703 newWidgetInfo.labelOnTop =
true;
1715 if ( columnCount <= 0 )
1718 QWidget *myContainer =
nullptr;
1721 QGroupBox *groupBox =
new QGroupBox( parent );
1723 groupBox->setTitle( container->
name() );
1724 myContainer = groupBox;
1725 newWidgetInfo.widget = myContainer;
1729 myContainer =
new QWidget();
1735 scrollArea->setWidget( myContainer );
1736 scrollArea->setWidgetResizable(
true );
1737 scrollArea->setFrameShape( QFrame::NoFrame );
1739 newWidgetInfo.widget = scrollArea;
1743 newWidgetInfo.widget = myContainer;
1747 QGridLayout *gbLayout =
new QGridLayout();
1748 myContainer->setLayout( gbLayout );
1753 const QList<QgsAttributeEditorElement *> children = container->
children();
1757 WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
1764 registerContainerInformation(
new ContainerInformation( widgetInfo.widget, containerDef->
visibilityExpression().
data() ) );
1768 if ( widgetInfo.labelText.isNull() )
1770 gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1775 QLabel *mypLabel =
new QLabel( widgetInfo.labelText );
1776 mypLabel->setToolTip( widgetInfo.toolTip );
1777 if ( columnCount > 1 && !widgetInfo.labelOnTop )
1779 mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1782 mypLabel->setBuddy( widgetInfo.widget );
1784 if ( widgetInfo.labelOnTop )
1786 QVBoxLayout *
c =
new QVBoxLayout();
1787 mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1788 c->layout()->addWidget( mypLabel );
1789 c->layout()->addWidget( widgetInfo.widget );
1790 gbLayout->addLayout( c, row, column, 1, 2 );
1795 gbLayout->addWidget( mypLabel, row, column++ );
1796 gbLayout->addWidget( widgetInfo.widget, row, column++ );
1800 if ( column >= columnCount * 2 )
1806 QWidget *spacer =
new QWidget();
1807 spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
1808 gbLayout->addWidget( spacer, ++row, 0 );
1809 gbLayout->setRowStretch( row, 1 );
1811 newWidgetInfo.labelText = QString();
1812 newWidgetInfo.labelOnTop =
true;
1826 mWidgets.append( qmlWrapper );
1828 newWidgetInfo.widget = qmlWrapper->
widget();
1829 newWidgetInfo.labelText = elementDef->
name();
1830 newWidgetInfo.labelOnTop =
true;
1831 newWidgetInfo.showLabel = widgetDef->
showLabel();
1836 QgsDebugMsg( QStringLiteral(
"Unknown attribute editor widget type encountered..." ) );
1840 newWidgetInfo.showLabel = widgetDef->
showLabel();
1842 return newWidgetInfo;
1861 mWidgets.append( eww );
1864 void QgsAttributeForm::createWrappers()
1866 QList<QWidget *> myWidgets = findChildren<QWidget *>();
1867 const QList<QgsField> fields = mLayer->
fields().
toList();
1869 Q_FOREACH ( QWidget *myWidget, myWidgets )
1872 QVariant vRel = myWidget->property(
"qgisRelation" );
1873 if ( vRel.isValid() )
1883 mWidgets.append( rww );
1888 Q_FOREACH (
const QgsField &field, fields )
1890 if ( field.
name() == myWidget->objectName() )
1895 addWidgetWrapper( eww );
1902 void QgsAttributeForm::afterWidgetInit()
1904 bool isFirstEww =
true;
1914 setFocusProxy( eww->
widget() );
1929 if ( e->type() == QEvent::KeyPress )
1931 QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( e );
1932 if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
1943 void QgsAttributeForm::scanForEqualAttributes(
QgsFeatureIterator &fit, QSet< int > &mixedValueFields, QHash< int, QVariant > &fieldSharedValues )
const 1945 mixedValueFields.clear();
1946 fieldSharedValues.clear();
1952 for (
int i = 0; i < mLayer->
fields().
count(); ++i )
1954 if ( mixedValueFields.contains( i ) )
1959 fieldSharedValues[i] = f.
attribute( i );
1963 if ( fieldSharedValues.value( i ) != f.
attribute( i ) )
1965 fieldSharedValues.remove( i );
1966 mixedValueFields.insert( i );
1972 if ( mixedValueFields.count() == mLayer->
fields().
count() )
1981 void QgsAttributeForm::layerSelectionChanged()
1993 resetMultiEdit(
true );
2000 mIsSettingMultiEditFeatures =
true;
2001 mMultiEditFeatureIds = fids;
2003 if ( fids.isEmpty() )
2006 QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2007 for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2009 wIt.value()->initialize( QVariant() );
2011 mIsSettingMultiEditFeatures =
false;
2018 QSet< int > mixedValueFields;
2019 QHash< int, QVariant > fieldSharedValues;
2020 scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2027 Q_FOREACH (
int field, mixedValueFields )
2031 w->initialize( firstFeature.
attribute( field ), true );
2034 QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2035 for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2039 w->initialize( sharedValueIt.value(), false );
2042 mIsSettingMultiEditFeatures =
false;
2047 if ( mOwnsMessageBar )
2049 mOwnsMessageBar =
false;
2050 mMessageBar = messageBar;
2060 QStringList filters;
2063 QString filter = widget->currentFilterExpression();
2064 if ( !filter.isNull() )
2065 filters <<
'(' + filter +
')';
2068 return filters.join( QStringLiteral(
" AND " ) );
2071 int QgsAttributeForm::messageTimeout()
2074 return settings.
value( QStringLiteral(
"qgis/messageTimeout" ), 5 ).toInt();
2079 bool newVisibility = expression.evaluate( expressionContext ).toBool();
2081 if ( newVisibility != isVisible )
2085 tabWidget->setTabVisible( widget, newVisibility );
2089 widget->setVisible( newVisibility );
2092 isVisible = newVisibility;
2105 if ( infos.count() == 0 || !currentFormFeature( formFeature ) )
2108 const QString hint = tr(
"No feature joined" );
2116 mJoinedFeatures[info] = joinFeature;
2122 Q_FOREACH (
const QString &field, subsetNames )
2126 QString hintText = hint;
2140 for (
const QgsField &field : joinFields )
2144 QString hintText = hint;
2158 bool QgsAttributeForm::fieldIsEditable(
int fieldIndex )
const 2170 editable = fieldIsEditable( *( info->
joinLayer() ), srcFieldIndex, mFeature.
id() );
2173 editable = fieldIsEditable( *mLayer, fieldIndex, mFeature.
id() );
2190 mIconMap[eww->
widget()]->hide();
2204 const QString file = QStringLiteral(
"/mIconJoinNotEditable.svg" );
2205 const QString tooltip = tr(
"Join settings do not allow editing" );
2206 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2210 const QString file = QStringLiteral(
"mIconJoinHasNotUpsertOnEdit.svg" );
2211 const QString tooltip = tr(
"Join settings do not allow upsert on edit" );
2212 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2216 const QString file = QStringLiteral(
"/mIconJoinedLayerNotEditable.svg" );
2217 const QString tooltip = tr(
"Joined layer is not toggled editable" );
2218 reloadIcon( file, tooltip, mIconMap[eww->
widget()] );
2224 void QgsAttributeForm::reloadIcon(
const QString &file,
const QString &tooltip, QSvgWidget *sw )
2227 sw->setToolTip( tooltip );
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
QString prefixedFieldName(const QgsField &field) const
Returns the prefixed name of the field.
Class for parsing and evaluation of expressions (formerly called "search strings").
QString displayName() const
Returns the name to use when displaying this field.
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.
QString attributeFormModeString() const
Returns given attributeFormMode as string.
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
QSet< QgsFeatureId > QgsFeatureIds
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
void setAttributeFormMode(const Mode &attributeFormMode)
Set attributeFormMode for the edited form.
This is an abstract base class for any elements of a drag and drop form.
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.
virtual bool isGroupBox() const
Returns if this container is going to be rendered as a group box.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
bool isValid() const
Returns the validity of this feature.
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
This class is a composition of two QSettings instances:
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
Form is in aggregate search mode, show each widget in this mode.
bool hasUpsertOnEdit() const
Returns whether a feature created on the target layer has to impact the joined layer by creating a ne...
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.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
Remove from current selection.
#define Q_NOWARN_DEPRECATED_PUSH
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
A bar for displaying non-blocking messages to the user.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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.
bool allowCustomUi() const
Returns true if the attribute editor should permit use of custom UI forms.
Multi edit mode, for editing fields of multiple features at once.
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
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...
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
QString name() const
Returns the name of this element.
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
Returns whether joined fields may be edited through the form of the target layer. ...
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
bool exists(int i) const
Returns if a field index is valid.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool isDynamicFormEnabled() const
Returns whether the form has to be dynamically updated with joined fields when a feature is being cre...
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted...
QStringList * joinFieldNamesSubset() const
Gets subset of fields to be used from joined layer.
bool popWidget(QgsMessageBarItem *item)
Remove the passed widget from the bar (if previously added), then display the next one in the stack i...
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
bool showUnlinkButton() const
Determines if the "unlink feature" button should be shown.
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.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
AttributeEditorType type() const
The type of this element.
void destroyEditCommand()
Destroy active command and reverts all changes in it.
int count() const
Returns number of items.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
This signal is emitted when selection was changed.
Encapsulate a field in an attribute table or data source.
const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::Info, int duration=5)
convenience method for pushing a message to the bar
QgsRelationManager relationManager
QgsField field(int fieldIdx) const
Gets field at particular index (must be in range 0..N-1)
QgsEditFormConfig editFormConfig
Add selection to current selection.
void editingStarted()
Is emitted, when editing on this layer has started.
FormMode formMode() const
Returns the form mode.
void endEditCommand()
Finish edit command and add it to undo/redo stack.
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else...
static bool eval(const QString &command, QString &result)
Eval a Python statement.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Set selection, removing any existing selection.
QgsOptionalExpression visibilityExpression() const
The visibility expression is used in the attribute form to show or hide this container based on an ex...
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.
bool hasSubset(bool blacklisted=true) const
Returns true if blacklisted fields is not empty or if a subset of names has been set.
void selectByExpression(const QString &expression, SelectBehavior behavior=SetSelection)
Select matching features using an expression.
bool enabled() const
Check if this optional is enabled.
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...
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
bool showLinkButton() const
Determines if the "link feature" button should be shown.
This class manages a set of relations between layers.
void beforeModifiedCheck() const
Is emitted, when layer is checked for modifications. Use for last-minute additions.
Single edit mode, for editing a single feature.
int columnCount() const
Gets the number of columns in this group.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QString qmlCode() const
The QML code that will be represented within this widget.
T data() const
Access the payload data.
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
int size() const
Returns number of items.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) FINAL
Adds a single feature to the sink.
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field's origin (value from an enumeration)
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QgsVectorLayer * joinLayer() const
Returns joined layer (may be null if the reference was set by layer ID and not resolved yet) ...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
bool showLabel() const
Controls if this element should be labeled with a title (field, relation or groupname).
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.
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).
Represents a vector layer which manages a vector based data sets.
void updatedFields()
Is emitted, whenever the fields available from this layer have been changed.
Allows modification of attribute values.
A form was embedded as a widget on another form.