QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsattributeform.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsattributeform.cpp
3  --------------------------------------
4  Date : 3.5.2014
5  Copyright : (C) 2014 Matthias Kuhn
6  Email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsattributeform.h"
17 
22 #include "qgsfeatureiterator.h"
23 #include "qgsgui.h"
24 #include "qgsproject.h"
25 #include "qgspythonrunner.h"
27 #include "qgsvectordataprovider.h"
29 #include "qgsmessagebar.h"
30 #include "qgsmessagebaritem.h"
32 #include "qgseditorwidgetwrapper.h"
33 #include "qgsrelationmanager.h"
34 #include "qgslogger.h"
35 #include "qgstabwidget.h"
36 #include "qgssettings.h"
37 #include "qgsscrollarea.h"
39 #include "qgsvectorlayerutils.h"
40 #include "qgsqmlwidgetwrapper.h"
41 #include "qgshtmlwidgetwrapper.h"
42 #include "qgsapplication.h"
44 #include "qgsfeaturerequest.h"
45 #include "qgstexteditwrapper.h"
46 #include "qgsfieldmodel.h"
47 
48 #include <QDir>
49 #include <QTextStream>
50 #include <QFileInfo>
51 #include <QFile>
52 #include <QFormLayout>
53 #include <QGridLayout>
54 #include <QGroupBox>
55 #include <QKeyEvent>
56 #include <QLabel>
57 #include <QPushButton>
58 #include <QUiLoader>
59 #include <QMessageBox>
60 #include <QToolButton>
61 #include <QMenu>
62 
63 int QgsAttributeForm::sFormCounter = 0;
64 
65 QgsAttributeForm::QgsAttributeForm( QgsVectorLayer *vl, const QgsFeature &feature, const QgsAttributeEditorContext &context, QWidget *parent )
66  : QWidget( parent )
67  , mLayer( vl )
68  , mOwnsMessageBar( true )
69  , mContext( context )
70  , mFormNr( sFormCounter++ )
71  , mIsSaving( false )
72  , mPreventFeatureRefresh( false )
73  , mIsSettingMultiEditFeatures( false )
74  , mUnsavedMultiEditChanges( false )
75  , mEditCommandMessage( tr( "Attributes changed" ) )
76  , mMode( QgsAttributeEditorContext::SingleEditMode )
77 {
78  init();
79  initPython();
81 
82  connect( vl, &QgsVectorLayer::updatedFields, this, &QgsAttributeForm::onUpdatedFields );
83  connect( vl, &QgsVectorLayer::beforeAddingExpressionField, this, &QgsAttributeForm::preventFeatureRefresh );
84  connect( vl, &QgsVectorLayer::beforeRemovingExpressionField, this, &QgsAttributeForm::preventFeatureRefresh );
85  connect( vl, &QgsVectorLayer::selectionChanged, this, &QgsAttributeForm::layerSelectionChanged );
86  connect( this, &QgsAttributeForm::modeChanged, this, &QgsAttributeForm::updateContainersVisibility );
87 
88  updateContainersVisibility();
89  updateLabels();
90 
91 }
92 
94 {
95  cleanPython();
96  qDeleteAll( mInterfaces );
97 }
98 
100 {
101  mButtonBox->hide();
102 
103  // Make sure that changes are taken into account if somebody tries to figure out if there have been some
106 }
107 
109 {
110  mButtonBox->show();
111 
112  disconnect( mLayer, &QgsVectorLayer::beforeModifiedCheck, this, &QgsAttributeForm::save );
113 }
114 
116 {
117  disconnect( mButtonBox, &QDialogButtonBox::accepted, this, &QgsAttributeForm::save );
118  disconnect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsAttributeForm::resetValues );
119 }
120 
122 {
123  mInterfaces.append( iface );
124 }
125 
127 {
128  return mFeature.isValid() && mLayer->isEditable();
129 }
130 
132 {
133  if ( mode == mMode )
134  return;
135 
137  {
138  //switching out of multi edit mode triggers a save
139  if ( mUnsavedMultiEditChanges )
140  {
141  // prompt for save
142  int res = QMessageBox::question( this, tr( "Multiedit Attributes" ),
143  tr( "Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
144  if ( res == QMessageBox::Yes )
145  {
146  save();
147  }
148  }
149  clearMultiEditMessages();
150  }
151  mUnsavedMultiEditChanges = false;
152 
153  mMode = mode;
154 
155  if ( mButtonBox->isVisible() && mMode == QgsAttributeEditorContext::SingleEditMode )
156  {
158  }
159  else
160  {
161  disconnect( mLayer, &QgsVectorLayer::beforeModifiedCheck, this, &QgsAttributeForm::save );
162  }
163 
164  //update all form editor widget modes to match
165  for ( QgsAttributeFormWidget *w : qgis::as_const( mFormWidgets ) )
166  {
167  switch ( mode )
168  {
171  break;
172 
175  break;
176 
179  break;
180 
183  break;
184 
187  break;
188 
191  break;
192 
195  break;
196  }
197  }
198  //update all form editor widget modes to match
199  for ( QgsWidgetWrapper *w : qgis::as_const( mWidgets ) )
200  {
201  QgsAttributeEditorContext newContext = w->context();
202  newContext.setAttributeFormMode( mMode );
203  w->setContext( newContext );
204  }
205 
206  bool relationWidgetsVisible = ( mMode != QgsAttributeEditorContext::MultiEditMode && mMode != QgsAttributeEditorContext::AggregateSearchMode );
207  for ( QgsAttributeFormRelationEditorWidget *w : findChildren< QgsAttributeFormRelationEditorWidget * >() )
208  {
209  w->setVisible( relationWidgetsVisible );
210  }
211 
212  switch ( mode )
213  {
215  setFeature( mFeature );
216  mSearchButtonBox->setVisible( false );
217  break;
218 
220  synchronizeEnabledState();
221  mSearchButtonBox->setVisible( false );
222  break;
223 
225  synchronizeEnabledState();
226  mSearchButtonBox->setVisible( false );
227  break;
228 
230  resetMultiEdit( false );
231  synchronizeEnabledState();
232  mSearchButtonBox->setVisible( false );
233  break;
234 
236  mSearchButtonBox->setVisible( true );
237  hideButtonBox();
238  break;
239 
241  mSearchButtonBox->setVisible( false );
242  hideButtonBox();
243  break;
244 
246  setFeature( mFeature );
247  mSearchButtonBox->setVisible( false );
248  break;
249  }
250 
251  emit modeChanged( mMode );
252 }
253 
254 void QgsAttributeForm::changeAttribute( const QString &field, const QVariant &value, const QString &hintText )
255 {
256  const auto constMWidgets = mWidgets;
257  for ( QgsWidgetWrapper *ww : constMWidgets )
258  {
259  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
260  if ( eww )
261  {
262  if ( eww->field().name() == field )
263  {
264  eww->setValues( value, QVariantList() );
265  eww->setHint( hintText );
266  }
267  // see if the field is present in additional fields of the editor widget
268  int index = eww->additionalFields().indexOf( field );
269  if ( index >= 0 )
270  {
271  QVariant mainValue = eww->value();
272  QVariantList additionalFieldValues = eww->additionalFieldValues();
273  additionalFieldValues[index] = value;
274  eww->setValues( mainValue, additionalFieldValues );
275  eww->setHint( hintText );
276  }
277  }
278  }
279 }
280 
282 {
283  mIsSettingFeature = true;
284  mFeature = feature;
285  mCurrentFormFeature = feature;
286 
287  switch ( mMode )
288  {
293  {
294  resetValues();
295 
296  synchronizeEnabledState();
297 
298  // Settings of feature is done when we trigger the attribute form interface
299  // Issue https://github.com/qgis/QGIS/issues/29667
300  mIsSettingFeature = false;
301  const auto constMInterfaces = mInterfaces;
302  for ( QgsAttributeFormInterface *iface : constMInterfaces )
303  {
304  iface->featureChanged();
305  }
306  break;
307  }
310  {
311  resetValues();
312  break;
313  }
315  {
316  //ignore setFeature
317  break;
318  }
319  }
320  mIsSettingFeature = false;
321 }
322 
323 bool QgsAttributeForm::saveEdits()
324 {
325  bool success = true;
326  bool changedLayer = false;
327 
328  QgsFeature updatedFeature = QgsFeature( mFeature );
329  if ( mFeature.isValid() || mMode == QgsAttributeEditorContext::AddFeatureMode )
330  {
331  bool doUpdate = false;
332 
333  // An add dialog should perform an action by default
334  // and not only if attributes have "changed"
336  doUpdate = true;
337 
338  QgsAttributes src = mFeature.attributes();
339  QgsAttributes dst = mFeature.attributes();
340 
341  for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
342  {
343  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
344  if ( eww )
345  {
346  // check for invalid JSON values
347  QgsTextEditWrapper *text_edit = qobject_cast<QgsTextEditWrapper *>( eww );
348  if ( text_edit && text_edit->isInvalidJSON() )
349  {
350  return false;
351  }
352  QVariantList dstVars = QVariantList() << dst.at( eww->fieldIdx() );
353  QVariantList srcVars = QVariantList() << eww->value();
354  QList<int> fieldIndexes = QList<int>() << eww->fieldIdx();
355 
356  // append additional fields
357  const QStringList additionalFields = eww->additionalFields();
358  for ( const QString &fieldName : additionalFields )
359  {
360  int idx = eww->layer()->fields().lookupField( fieldName );
361  fieldIndexes << idx;
362  dstVars << dst.at( idx );
363  }
364  srcVars.append( eww->additionalFieldValues() );
365 
366  Q_ASSERT( dstVars.count() == srcVars.count() );
367 
368  for ( int i = 0; i < dstVars.count(); i++ )
369  {
370 
371  if ( !qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() )
372  {
373  dst[fieldIndexes[i]] = srcVars[i];
374 
375  doUpdate = true;
376  }
377  }
378  }
379  }
380 
381  updatedFeature.setAttributes( dst );
382 
383  const auto constMInterfaces = mInterfaces;
384  for ( QgsAttributeFormInterface *iface : constMInterfaces )
385  {
386  if ( !iface->acceptChanges( updatedFeature ) )
387  {
388  doUpdate = false;
389  }
390  }
391 
392  if ( doUpdate )
393  {
395  {
396  mFeature = updatedFeature;
397  }
398  else if ( mMode == QgsAttributeEditorContext::AddFeatureMode )
399  {
400  mFeature.setValid( true );
401  mLayer->beginEditCommand( mEditCommandMessage );
402  bool res = mLayer->addFeature( updatedFeature );
403  if ( res )
404  {
405  mFeature.setAttributes( updatedFeature.attributes() );
406  mLayer->endEditCommand();
408  changedLayer = true;
409  }
410  else
411  mLayer->destroyEditCommand();
412  }
413  else
414  {
415  mLayer->beginEditCommand( mEditCommandMessage );
416 
417  QgsAttributeMap newValues;
418  QgsAttributeMap oldValues;
419 
420  int n = 0;
421  for ( int i = 0; i < dst.count(); ++i )
422  {
423  if ( qgsVariantEqual( dst.at( i ), src.at( i ) ) // If field is not changed...
424  || !dst.at( i ).isValid() // or the widget returns invalid (== do not change)
425  || !fieldIsEditable( i ) ) // or the field cannot be edited ...
426  {
427  continue;
428  }
429 
430  QgsDebugMsgLevel( QStringLiteral( "Updating field %1" ).arg( i ), 2 );
431  QgsDebugMsgLevel( QStringLiteral( "dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
432  .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ), 2 );
433  QgsDebugMsgLevel( QStringLiteral( "src:'%1' (type:%2, isNull:%3, isValid:%4)" )
434  .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ), 2 );
435 
436  newValues[i] = dst.at( i );
437  oldValues[i] = src.at( i );
438 
439  n++;
440  }
441 
442  success = mLayer->changeAttributeValues( mFeature.id(), newValues, oldValues );
443 
444  if ( success && n > 0 )
445  {
446  mLayer->endEditCommand();
447  mFeature.setAttributes( dst );
448  changedLayer = true;
449  }
450  else
451  {
452  mLayer->destroyEditCommand();
453  }
454  }
455  }
456  }
457 
458  emit featureSaved( updatedFeature );
459 
460  // [MD] Refresh canvas only when absolutely necessary - it interferes with other stuff (#11361).
461  // This code should be revisited - and the signals should be fired (+ layer repainted)
462  // only when actually doing any changes. I am unsure if it is actually a good idea
463  // to call save() whenever some code asks for vector layer's modified status
464  // (which is the case when attribute table is open)
465  if ( changedLayer )
466  mLayer->triggerRepaint();
467 
468  return success;
469 }
470 
471 bool QgsAttributeForm::updateDefaultValues( const int originIdx )
472 {
473 
474  // Synchronize
475  updateDefaultValueDependencies();
476 
477  if ( !mDefaultValueDependencies.contains( originIdx ) )
478  return false;
479 
480  // create updated Feature
481  QgsFeature updatedFeature = QgsFeature( mFeature );
482  if ( mFeature.isValid() || mMode == QgsAttributeEditorContext::AddFeatureMode )
483  {
484  QgsAttributes dst = mFeature.attributes();
485  for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
486  {
487  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
488  if ( eww )
489  {
490  QVariantList dstVars = QVariantList() << dst.at( eww->fieldIdx() );
491  QVariantList srcVars = QVariantList() << eww->value();
492  QList<int> fieldIndexes = QList<int>() << eww->fieldIdx();
493 
494  // append additional fields
495  const QStringList additionalFields = eww->additionalFields();
496  for ( const QString &fieldName : additionalFields )
497  {
498  int idx = eww->layer()->fields().lookupField( fieldName );
499  fieldIndexes << idx;
500  dstVars << dst.at( idx );
501  }
502  srcVars.append( eww->additionalFieldValues() );
503 
504  Q_ASSERT( dstVars.count() == srcVars.count() );
505 
506  for ( int i = 0; i < dstVars.count(); i++ )
507  {
508 
509  if ( !qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() && fieldIsEditable( fieldIndexes[i] ) )
510  {
511  dst[fieldIndexes[i]] = srcVars[i];
512  }
513  }
514  }
515  }
516  updatedFeature.setAttributes( dst );
517 
518  // go through depending fields and update the fields with defaultexpression
519  QList<QgsWidgetWrapper *> relevantWidgets = mDefaultValueDependencies.values( originIdx );
520  for ( QgsWidgetWrapper *ww : qgis::as_const( relevantWidgets ) )
521  {
522  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
523  if ( eww )
524  {
525  //do not update when when mMode is not AddFeatureMode and it's not applyOnUpdate
527  {
528  continue;
529  }
530 
531  //do not update when this widget is already updating (avoid recursions)
532  if ( mAlreadyUpdatedFields.contains( eww->fieldIdx() ) )
533  continue;
534 
535  QgsExpressionContext context = createExpressionContext( updatedFeature );
536  QString value = mLayer->defaultValue( eww->fieldIdx(), updatedFeature, &context ).toString();
537  eww->setValue( value );
538  }
539  }
540  }
541  return true;
542 }
543 
544 void QgsAttributeForm::resetMultiEdit( bool promptToSave )
545 {
546  if ( promptToSave )
547  save();
548 
549  mUnsavedMultiEditChanges = false;
551 }
552 
553 void QgsAttributeForm::multiEditMessageClicked( const QString &link )
554 {
555  clearMultiEditMessages();
556  resetMultiEdit( link == QLatin1String( "#apply" ) );
557 }
558 
559 void QgsAttributeForm::filterTriggered()
560 {
561  QString filter = createFilterExpression();
562  emit filterExpressionSet( filter, ReplaceFilter );
563  if ( mContext.formMode() == QgsAttributeEditorContext::Embed )
565 }
566 
567 void QgsAttributeForm::searchZoomTo()
568 {
569  QString filter = createFilterExpression();
570  if ( filter.isEmpty() )
571  return;
572 
573  emit zoomToFeatures( filter );
574 }
575 
576 void QgsAttributeForm::searchFlash()
577 {
578  QString filter = createFilterExpression();
579  if ( filter.isEmpty() )
580  return;
581 
582  emit flashFeatures( filter );
583 }
584 
585 void QgsAttributeForm::filterAndTriggered()
586 {
587  QString filter = createFilterExpression();
588  if ( filter.isEmpty() )
589  return;
590 
591  if ( mContext.formMode() == QgsAttributeEditorContext::Embed )
593  emit filterExpressionSet( filter, FilterAnd );
594 }
595 
596 void QgsAttributeForm::filterOrTriggered()
597 {
598  QString filter = createFilterExpression();
599  if ( filter.isEmpty() )
600  return;
601 
602  if ( mContext.formMode() == QgsAttributeEditorContext::Embed )
604  emit filterExpressionSet( filter, FilterOr );
605 }
606 
607 void QgsAttributeForm::pushSelectedFeaturesMessage()
608 {
609  int count = mLayer->selectedFeatureCount();
610  if ( count > 0 )
611  {
612  mMessageBar->pushMessage( QString(),
613  tr( "%n matching feature(s) selected", "matching features", count ),
614  Qgis::Info,
615  messageTimeout() );
616  }
617  else
618  {
619  mMessageBar->pushMessage( QString(),
620  tr( "No matching features found" ),
622  messageTimeout() );
623  }
624 }
625 
626 void QgsAttributeForm::displayWarning( const QString &message )
627 {
628  mMessageBar->pushMessage( QString(),
629  message,
631  messageTimeout() );
632 }
633 
634 void QgsAttributeForm::runSearchSelect( QgsVectorLayer::SelectBehavior behavior )
635 {
636  QString filter = createFilterExpression();
637  if ( filter.isEmpty() )
638  return;
639 
640  mLayer->selectByExpression( filter, behavior );
641  pushSelectedFeaturesMessage();
642  if ( mContext.formMode() == QgsAttributeEditorContext::Embed )
644 }
645 
646 void QgsAttributeForm::searchSetSelection()
647 {
648  runSearchSelect( QgsVectorLayer::SetSelection );
649 }
650 
651 void QgsAttributeForm::searchAddToSelection()
652 {
653  runSearchSelect( QgsVectorLayer::AddToSelection );
654 }
655 
656 void QgsAttributeForm::searchRemoveFromSelection()
657 {
658  runSearchSelect( QgsVectorLayer::RemoveFromSelection );
659 }
660 
661 void QgsAttributeForm::searchIntersectSelection()
662 {
663  runSearchSelect( QgsVectorLayer::IntersectSelection );
664 }
665 
666 bool QgsAttributeForm::saveMultiEdits()
667 {
668  //find changed attributes
669  QgsAttributeMap newAttributeValues;
670  QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
671  for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
672  {
673  QgsAttributeFormEditorWidget *w = wIt.value();
674  if ( !w->hasChanged() )
675  continue;
676 
677  if ( !w->currentValue().isValid() // if the widget returns invalid (== do not change)
678  || !fieldIsEditable( wIt.key() ) ) // or the field cannot be edited ...
679  {
680  continue;
681  }
682 
683  // let editor know we've accepted the changes
684  w->changesCommitted();
685 
686  newAttributeValues.insert( wIt.key(), w->currentValue() );
687  }
688 
689  if ( newAttributeValues.isEmpty() )
690  {
691  //nothing to change
692  return true;
693  }
694 
695 #if 0
696  // prompt for save
697  int res = QMessageBox::information( this, tr( "Multiedit Attributes" ),
698  tr( "Edits will be applied to all selected features." ), QMessageBox::Ok | QMessageBox::Cancel );
699  if ( res != QMessageBox::Ok )
700  {
701  resetMultiEdit();
702  return false;
703  }
704 #endif
705 
706  mLayer->beginEditCommand( tr( "Updated multiple feature attributes" ) );
707 
708  bool success = true;
709 
710  const auto constMMultiEditFeatureIds = mMultiEditFeatureIds;
711  for ( QgsFeatureId fid : constMMultiEditFeatureIds )
712  {
713  QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
714  for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
715  {
716  success &= mLayer->changeAttributeValue( fid, aIt.key(), aIt.value() );
717  }
718  }
719 
720  clearMultiEditMessages();
721  if ( success )
722  {
723  mLayer->endEditCommand();
724  mLayer->triggerRepaint();
725  mMultiEditMessageBarItem = new QgsMessageBarItem( tr( "Attribute changes for multiple features applied." ), Qgis::Success, messageTimeout() );
726  }
727  else
728  {
729  mLayer->destroyEditCommand();
730  mMultiEditMessageBarItem = new QgsMessageBarItem( tr( "Changes could not be applied." ), Qgis::Warning, messageTimeout() );
731  }
732 
733  if ( !mButtonBox->isVisible() )
734  mMessageBar->pushItem( mMultiEditMessageBarItem );
735  return success;
736 }
737 
739 {
740  if ( mIsSaving )
741  return true;
742 
743  for ( QgsWidgetWrapper *wrapper : qgis::as_const( mWidgets ) )
744  {
745  wrapper->notifyAboutToSave();
746  }
747 
748  // only do the dirty checks when editing an existing feature - for new
749  // features we need to add them even if the attributes are unchanged from the initial
750  // default values
751  switch ( mMode )
752  {
757  if ( !mDirty )
758  return true;
759  break;
760 
764  break;
765  }
766 
767  mIsSaving = true;
768 
769  bool success = true;
770 
771  emit beforeSave( success );
772 
773  // Somebody wants to prevent this form from saving
774  if ( !success )
775  return false;
776 
777  switch ( mMode )
778  {
785  success = saveEdits();
786  break;
787 
789  success = saveMultiEdits();
790  break;
791  }
792 
793  mIsSaving = false;
794  mUnsavedMultiEditChanges = false;
795  mDirty = false;
796 
797  return success;
798 }
799 
801 {
802  mValuesInitialized = false;
803  const auto constMWidgets = mWidgets;
804  for ( QgsWidgetWrapper *ww : constMWidgets )
805  {
806  ww->setFeature( mFeature );
807  }
808  mValuesInitialized = true;
809  mDirty = false;
810 }
811 
813 {
814  const auto widgets { findChildren< QgsAttributeFormEditorWidget * >() };
815  for ( QgsAttributeFormEditorWidget *w : widgets )
816  {
817  w->resetSearch();
818  }
819 }
820 
821 void QgsAttributeForm::clearMultiEditMessages()
822 {
823  if ( mMultiEditUnsavedMessageBarItem )
824  {
825  if ( !mButtonBox->isVisible() )
826  mMessageBar->popWidget( mMultiEditUnsavedMessageBarItem );
827  mMultiEditUnsavedMessageBarItem = nullptr;
828  }
829  if ( mMultiEditMessageBarItem )
830  {
831  if ( !mButtonBox->isVisible() )
832  mMessageBar->popWidget( mMultiEditMessageBarItem );
833  mMultiEditMessageBarItem = nullptr;
834  }
835 }
836 
837 QString QgsAttributeForm::createFilterExpression() const
838 {
839  QStringList filters;
840  for ( QgsAttributeFormWidget *w : qgis::as_const( mFormWidgets ) )
841  {
842  QString filter = w->currentFilterExpression();
843  if ( !filter.isEmpty() )
844  filters << filter;
845  }
846 
847  if ( filters.isEmpty() )
848  return QString();
849 
850  QString filter = filters.join( QLatin1String( ") AND (" ) ).prepend( '(' ).append( ')' );
851  return filter;
852 }
853 
854 QgsExpressionContext QgsAttributeForm::createExpressionContext( const QgsFeature &feature ) const
855 {
856  QgsExpressionContext context;
859  if ( mExtraContextScope )
860  context.appendScope( new QgsExpressionContextScope( *mExtraContextScope.get() ) );
861  context.setFeature( feature );
862  return context;
863 }
864 
865 
866 void QgsAttributeForm::onAttributeChanged( const QVariant &value, const QVariantList &additionalFieldValues )
867 {
868  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( sender() );
869  Q_ASSERT( eww );
870 
871  bool signalEmitted = false;
872 
873  if ( mValuesInitialized )
874  mDirty = true;
875 
876  mCurrentFormFeature.setAttribute( eww->field().name(), value );
877 
878  switch ( mMode )
879  {
884  {
886  emit attributeChanged( eww->field().name(), value );
888  emit widgetValueChanged( eww->field().name(), value, !mIsSettingFeature );
889 
890  // also emit the signal for additional values
891  const QStringList additionalFields = eww->additionalFields();
892  for ( int i = 0; i < additionalFields.count(); i++ )
893  {
894  const QString fieldName = additionalFields.at( i );
895  const QVariant value = additionalFieldValues.at( i );
896  emit widgetValueChanged( fieldName, value, !mIsSettingFeature );
897  }
898 
899  signalEmitted = true;
900 
901  updateJoinedFields( *eww );
902 
903  break;
904  }
906  {
907  if ( !mIsSettingMultiEditFeatures )
908  {
909  mUnsavedMultiEditChanges = true;
910 
911  QLabel *msgLabel = new QLabel( tr( "Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
912  msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
913  msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
914  connect( msgLabel, &QLabel::linkActivated, this, &QgsAttributeForm::multiEditMessageClicked );
915  clearMultiEditMessages();
916 
917  mMultiEditUnsavedMessageBarItem = new QgsMessageBarItem( msgLabel, Qgis::Warning );
918  if ( !mButtonBox->isVisible() )
919  mMessageBar->pushItem( mMultiEditUnsavedMessageBarItem );
920  }
921  break;
922  }
925  //nothing to do
926  break;
927  }
928 
929  updateConstraints( eww );
930 
931  //append field index here, so it's not updated recursive
932  mAlreadyUpdatedFields.append( eww->fieldIdx() );
933  updateDefaultValues( eww->fieldIdx() );
934  mAlreadyUpdatedFields.removeAll( eww->fieldIdx() );
935 
936  // Updates expression controlled labels
937  updateLabels();
938 
939  if ( !signalEmitted )
940  {
942  emit attributeChanged( eww->field().name(), value );
944  emit widgetValueChanged( eww->field().name(), value, !mIsSettingFeature );
945  }
946 }
947 
948 void QgsAttributeForm::updateAllConstraints()
949 {
950  const auto constMWidgets = mWidgets;
951  for ( QgsWidgetWrapper *ww : constMWidgets )
952  {
953  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
954  if ( eww )
955  updateConstraints( eww );
956  }
957 }
958 
959 void QgsAttributeForm::updateConstraints( QgsEditorWidgetWrapper *eww )
960 {
961  // get the current feature set in the form
962  QgsFeature ft;
963  if ( currentFormValuesFeature( ft ) )
964  {
965  // if the layer is NOT being edited then we only check layer based constraints, and not
966  // any constraints enforced by the provider. Because:
967  // 1. we want to keep browsing features nice and responsive. It's nice to give feedback as to whether
968  // the value checks out, but not if it's too slow to do so. Some constraints (e.g., unique) can be
969  // expensive to test. A user can freely remove a layer-based constraint if it proves to be too slow
970  // to test, but they are unlikely to have any control over provider-side constraints
971  // 2. the provider has already accepted the value, so presumably it doesn't violate the constraint
972  // and there's no point rechecking!
973 
974  // update eww constraint
975  updateConstraint( ft, eww );
976 
977  // update eww dependencies constraint
978  const QList<QgsEditorWidgetWrapper *> deps = constraintDependencies( eww );
979 
980  for ( QgsEditorWidgetWrapper *depsEww : deps )
981  updateConstraint( ft, depsEww );
982 
983  // sync OK button status
984  synchronizeEnabledState();
985 
986  QgsExpressionContext context = createExpressionContext( ft );
987 
988  // Recheck visibility for all containers which are controlled by this value
989  const QVector<ContainerInformation *> infos = mContainerInformationDependency.value( eww->field().name() );
990  for ( ContainerInformation *info : infos )
991  {
992  info->apply( &context );
993  }
994  }
995 }
996 
997 void QgsAttributeForm::updateContainersVisibility()
998 {
999  QgsExpressionContext context = createExpressionContext( mFeature );
1000 
1001  const QVector<ContainerInformation *> infos = mContainerVisibilityInformation;
1002 
1003  for ( ContainerInformation *info : infos )
1004  {
1005  info->apply( &context );
1006  }
1007 
1008  //and update the constraints
1009  updateAllConstraints();
1010 }
1011 
1012 void QgsAttributeForm::updateConstraint( const QgsFeature &ft, QgsEditorWidgetWrapper *eww )
1013 {
1015 
1016  if ( eww->layer()->fields().fieldOrigin( eww->fieldIdx() ) == QgsFields::OriginJoin )
1017  {
1018  int srcFieldIdx;
1019  const QgsVectorLayerJoinInfo *info = eww->layer()->joinBuffer()->joinForFieldIndex( eww->fieldIdx(), eww->layer()->fields(), srcFieldIdx );
1020 
1021  if ( info && info->joinLayer() && info->isDynamicFormEnabled() )
1022  {
1023  if ( mJoinedFeatures.contains( info ) )
1024  {
1025  eww->updateConstraint( info->joinLayer(), srcFieldIdx, mJoinedFeatures[info], constraintOrigin );
1026  return;
1027  }
1028  else // if we are here, it means there's not joined field for this feature
1029  {
1030  eww->updateConstraint( QgsFeature() );
1031  return;
1032  }
1033  }
1034  }
1035 
1036  // default constraint update
1037  eww->updateConstraint( ft, constraintOrigin );
1038 }
1039 
1040 void QgsAttributeForm::updateLabels()
1041 {
1042  if ( ! mLabelDataDefinedProperties.isEmpty() )
1043  {
1044  QgsFeature currentFeature;
1045  if ( currentFormValuesFeature( currentFeature ) )
1046  {
1047  QgsExpressionContext context = createExpressionContext( currentFeature );
1048 
1049  for ( auto it = mLabelDataDefinedProperties.constBegin() ; it != mLabelDataDefinedProperties.constEnd(); ++it )
1050  {
1051  QLabel *label { it.key() };
1052  bool ok;
1053  const QString value { it->valueAsString( context, QString(), &ok ) };
1054  if ( ok && ! value.isEmpty() )
1055  {
1056  label->setText( value );
1057  }
1058  }
1059  }
1060  }
1061 }
1062 
1063 bool QgsAttributeForm::currentFormValuesFeature( QgsFeature &feature )
1064 {
1065  bool rc = true;
1066  feature = QgsFeature( mFeature );
1068 
1069  for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
1070  {
1071  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1072 
1073  if ( !eww )
1074  continue;
1075 
1076  if ( dst.count() > eww->fieldIdx() )
1077  {
1078  QVariantList dstVars = QVariantList() << dst.at( eww->fieldIdx() );
1079  QVariantList srcVars = QVariantList() << eww->value();
1080  QList<int> fieldIndexes = QList<int>() << eww->fieldIdx();
1081 
1082  // append additional fields
1083  const QStringList additionalFields = eww->additionalFields();
1084  for ( const QString &fieldName : additionalFields )
1085  {
1086  int idx = eww->layer()->fields().lookupField( fieldName );
1087  fieldIndexes << idx;
1088  dstVars << dst.at( idx );
1089  }
1090  srcVars.append( eww->additionalFieldValues() );
1091 
1092  Q_ASSERT( dstVars.count() == srcVars.count() );
1093 
1094  for ( int i = 0; i < dstVars.count(); i++ )
1095  {
1096  // need to check dstVar.isNull() != srcVar.isNull()
1097  // otherwise if dstVar=NULL and scrVar=0, then dstVar = srcVar
1098  if ( ( !qgsVariantEqual( dstVars[i], srcVars[i] ) || dstVars[i].isNull() != srcVars[i].isNull() ) && srcVars[i].isValid() )
1099  {
1100  dst[fieldIndexes[i]] = srcVars[i];
1101  }
1102  }
1103  }
1104  else
1105  {
1106  rc = false;
1107  break;
1108  }
1109  }
1110 
1111  feature.setAttributes( dst );
1112 
1113  return rc;
1114 }
1115 
1116 
1117 void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info )
1118 {
1119  mContainerVisibilityInformation.append( info );
1120 
1121  const QSet<QString> referencedColumns = info->expression.referencedColumns();
1122 
1123  for ( const QString &col : referencedColumns )
1124  {
1125  mContainerInformationDependency[ col ].append( info );
1126  }
1127 }
1128 
1129 bool QgsAttributeForm::currentFormValidConstraints( QStringList &invalidFields, QStringList &descriptions )
1130 {
1131  bool valid( true );
1132 
1133  for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
1134  {
1135  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1136  if ( eww )
1137  {
1138  if ( ! eww->isValidConstraint() )
1139  {
1140  invalidFields.append( eww->field().displayName() );
1141 
1142  descriptions.append( eww->constraintFailureReason() );
1143 
1144  if ( eww->isBlockingCommit() )
1145  valid = false; // continue to get all invalid fields
1146  }
1147  }
1148  }
1149 
1150  return valid;
1151 }
1152 
1153 void QgsAttributeForm::onAttributeAdded( int idx )
1154 {
1155  mPreventFeatureRefresh = false;
1156  if ( mFeature.isValid() )
1157  {
1158  QgsAttributes attrs = mFeature.attributes();
1159  attrs.insert( idx, QVariant( layer()->fields().at( idx ).type() ) );
1160  mFeature.setFields( layer()->fields() );
1161  mFeature.setAttributes( attrs );
1162  }
1163  init();
1164  setFeature( mFeature );
1165 }
1166 
1167 void QgsAttributeForm::onAttributeDeleted( int idx )
1168 {
1169  mPreventFeatureRefresh = false;
1170  if ( mFeature.isValid() )
1171  {
1172  QgsAttributes attrs = mFeature.attributes();
1173  attrs.remove( idx );
1174  mFeature.setFields( layer()->fields() );
1175  mFeature.setAttributes( attrs );
1176  }
1177  init();
1178  setFeature( mFeature );
1179 }
1180 
1181 void QgsAttributeForm::onUpdatedFields()
1182 {
1183  mPreventFeatureRefresh = false;
1184  if ( mFeature.isValid() )
1185  {
1186  QgsAttributes attrs( layer()->fields().size() );
1187  for ( int i = 0; i < layer()->fields().size(); i++ )
1188  {
1189  int idx = mFeature.fields().indexFromName( layer()->fields().at( i ).name() );
1190  if ( idx != -1 )
1191  {
1192  attrs[i] = mFeature.attributes().at( idx );
1193  if ( mFeature.attributes().at( idx ).type() != layer()->fields().at( i ).type() )
1194  {
1195  attrs[i].convert( layer()->fields().at( i ).type() );
1196  }
1197  }
1198  else
1199  {
1200  attrs[i] = QVariant( layer()->fields().at( i ).type() );
1201  }
1202  }
1203  mFeature.setFields( layer()->fields() );
1204  mFeature.setAttributes( attrs );
1205  }
1206  init();
1207  setFeature( mFeature );
1208 }
1209 
1210 void QgsAttributeForm::onConstraintStatusChanged( const QString &constraint,
1211  const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result )
1212 {
1213  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( sender() );
1214  Q_ASSERT( eww );
1215 
1216  QgsAttributeFormEditorWidget *formEditorWidget = mFormEditorWidgets.value( eww->fieldIdx() );
1217 
1218  if ( formEditorWidget )
1219  formEditorWidget->setConstraintStatus( constraint, description, err, result );
1220 }
1221 
1222 QList<QgsEditorWidgetWrapper *> QgsAttributeForm::constraintDependencies( QgsEditorWidgetWrapper *w )
1223 {
1224  QList<QgsEditorWidgetWrapper *> wDeps;
1225  QString name = w->field().name();
1226 
1227  // for each widget in the current form
1228  for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
1229  {
1230  // get the wrapper
1231  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1232  if ( eww )
1233  {
1234  // compare name to not compare w to itself
1235  QString ewwName = eww->field().name();
1236  if ( name != ewwName )
1237  {
1238  // get expression and referencedColumns
1239  QgsExpression expr = eww->layer()->fields().at( eww->fieldIdx() ).constraints().constraintExpression();
1240 
1241  const auto referencedColumns = expr.referencedColumns();
1242 
1243  for ( const QString &colName : referencedColumns )
1244  {
1245  if ( name == colName )
1246  {
1247  wDeps.append( eww );
1248  break;
1249  }
1250  }
1251  }
1252  }
1253  }
1254 
1255  return wDeps;
1256 }
1257 
1258 QgsRelationWidgetWrapper *QgsAttributeForm::setupRelationWidgetWrapper( const QgsRelation &rel, const QgsAttributeEditorContext &context )
1259 {
1260  QgsRelationWidgetWrapper *rww = new QgsRelationWidgetWrapper( mLayer, rel, nullptr, this );
1261  const QVariantMap config = mLayer->editFormConfig().widgetConfig( rel.id() );
1262  rww->setConfig( config );
1263  rww->setContext( context );
1264 
1265  return rww;
1266 }
1267 
1268 void QgsAttributeForm::preventFeatureRefresh()
1269 {
1270  mPreventFeatureRefresh = true;
1271 }
1272 
1274 {
1275  if ( mPreventFeatureRefresh || mLayer->isEditable() || !mFeature.isValid() )
1276  return;
1277 
1278  // reload feature if layer changed although not editable
1279  // (datasource probably changed bypassing QgsVectorLayer)
1280  if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( mFeature ) )
1281  return;
1282 
1283  init();
1284  setFeature( mFeature );
1285 }
1286 
1287 void QgsAttributeForm::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
1288 {
1289  for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
1290  {
1291  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1292  if ( eww )
1293  {
1294  eww->parentFormValueChanged( attribute, newValue );
1295  }
1296  }
1297 }
1298 
1299 void QgsAttributeForm::synchronizeEnabledState()
1300 {
1301  bool isEditable = ( mFeature.isValid()
1303  || mMode == QgsAttributeEditorContext::MultiEditMode ) && mLayer->isEditable();
1304 
1305  for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
1306  {
1307  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
1308  if ( eww )
1309  {
1310  QgsAttributeFormEditorWidget *formWidget = mFormEditorWidgets.value( eww->fieldIdx() );
1311 
1312  if ( formWidget )
1313  formWidget->setConstraintResultVisible( isEditable );
1314 
1315  eww->setConstraintResultVisible( isEditable );
1316 
1317  bool enabled = isEditable && fieldIsEditable( eww->fieldIdx() );
1318  ww->setEnabled( enabled );
1319 
1320  updateIcon( eww );
1321  }
1322  }
1323 
1325  {
1326  QStringList invalidFields, descriptions;
1327  bool validConstraint = currentFormValidConstraints( invalidFields, descriptions );
1328 
1329  isEditable = isEditable & validConstraint;
1330  }
1331 
1332  // change OK button status
1333  QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
1334  if ( okButton )
1335  okButton->setEnabled( isEditable );
1336 }
1337 
1338 void QgsAttributeForm::init()
1339 {
1340  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1341 
1342  // Cleanup of any previously shown widget, we start from scratch
1343  QWidget *formWidget = nullptr;
1344 
1345  bool buttonBoxVisible = true;
1346  // Cleanup button box but preserve visibility
1347  if ( mButtonBox )
1348  {
1349  buttonBoxVisible = mButtonBox->isVisible();
1350  delete mButtonBox;
1351  mButtonBox = nullptr;
1352  }
1353 
1354  if ( mSearchButtonBox )
1355  {
1356  delete mSearchButtonBox;
1357  mSearchButtonBox = nullptr;
1358  }
1359 
1360  qDeleteAll( mWidgets );
1361  mWidgets.clear();
1362 
1363  while ( QWidget *w = this->findChild<QWidget *>() )
1364  {
1365  delete w;
1366  }
1367  delete layout();
1368 
1369  QVBoxLayout *vl = new QVBoxLayout();
1370  vl->setContentsMargins( 0, 0, 0, 0 );
1371  mMessageBar = new QgsMessageBar( this );
1372  mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1373  vl->addWidget( mMessageBar );
1374 
1375  setLayout( vl );
1376 
1377  // Get a layout
1378  QGridLayout *layout = new QGridLayout();
1379  QWidget *container = new QWidget();
1380  container->setLayout( layout );
1381  vl->addWidget( container );
1382 
1383  mFormEditorWidgets.clear();
1384  mFormWidgets.clear();
1385 
1386  // a bar to warn the user with non-blocking messages
1387  setContentsMargins( 0, 0, 0, 0 );
1388 
1389  // Try to load Ui-File for layout
1390  if ( mContext.allowCustomUi() && mLayer->editFormConfig().layout() == QgsEditFormConfig::UiFileLayout &&
1391  !mLayer->editFormConfig().uiForm().isEmpty() )
1392  {
1393  QgsDebugMsg( QStringLiteral( "loading form: %1" ).arg( mLayer->editFormConfig().uiForm() ) );
1394  const QString path = mLayer->editFormConfig().uiForm();
1396  if ( file && file->open( QFile::ReadOnly ) )
1397  {
1398  QUiLoader loader;
1399 
1400  QFileInfo fi( file->fileName() );
1401  loader.setWorkingDirectory( fi.dir() );
1402  formWidget = loader.load( file, this );
1403  if ( formWidget )
1404  {
1405  formWidget->setWindowFlags( Qt::Widget );
1406  layout->addWidget( formWidget );
1407  formWidget->show();
1408  file->close();
1409  mButtonBox = findChild<QDialogButtonBox *>();
1410  createWrappers();
1411 
1412  formWidget->installEventFilter( this );
1413  }
1414  }
1415  }
1416 
1417  QgsTabWidget *tabWidget = nullptr;
1418 
1419  // Tab layout
1420  if ( !formWidget && mLayer->editFormConfig().layout() == QgsEditFormConfig::TabLayout )
1421  {
1422  int row = 0;
1423  int column = 0;
1424  int columnCount = 1;
1425 
1426  const QList<QgsAttributeEditorElement *> tabs = mLayer->editFormConfig().tabs();
1427 
1428  for ( QgsAttributeEditorElement *widgDef : tabs )
1429  {
1430  if ( widgDef->type() == QgsAttributeEditorElement::AeTypeContainer )
1431  {
1432  QgsAttributeEditorContainer *containerDef = dynamic_cast<QgsAttributeEditorContainer *>( widgDef );
1433  if ( !containerDef )
1434  continue;
1435 
1436  if ( containerDef->isGroupBox() )
1437  {
1438  tabWidget = nullptr;
1439  WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext );
1440  layout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1441  if ( containerDef->visibilityExpression().enabled() )
1442  {
1443  registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
1444  }
1445  column += 2;
1446  }
1447  else
1448  {
1449  if ( !tabWidget )
1450  {
1451  tabWidget = new QgsTabWidget();
1452  layout->addWidget( tabWidget, row, column, 1, 2 );
1453  column += 2;
1454  }
1455 
1456  QWidget *tabPage = new QWidget( tabWidget );
1457 
1458  tabWidget->addTab( tabPage, widgDef->name() );
1459 
1460  if ( containerDef->visibilityExpression().enabled() )
1461  {
1462  registerContainerInformation( new ContainerInformation( tabWidget, tabPage, containerDef->visibilityExpression().data() ) );
1463  }
1464  QGridLayout *tabPageLayout = new QGridLayout();
1465  tabPage->setLayout( tabPageLayout );
1466 
1467  WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
1468  tabPageLayout->addWidget( widgetInfo.widget );
1469  }
1470  }
1471  else
1472  {
1473  tabWidget = nullptr;
1474  WidgetInfo widgetInfo = createWidgetFromDef( widgDef, container, mLayer, mContext );
1475  QLabel *label = new QLabel( widgetInfo.labelText );
1476  label->setToolTip( widgetInfo.toolTip );
1477  if ( columnCount > 1 && !widgetInfo.labelOnTop )
1478  {
1479  label->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1480  }
1481 
1482  label->setBuddy( widgetInfo.widget );
1483 
1484  if ( !widgetInfo.showLabel )
1485  {
1486  QVBoxLayout *c = new QVBoxLayout();
1487  label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1488  c->addWidget( widgetInfo.widget );
1489  layout->addLayout( c, row, column, 1, 2 );
1490  column += 2;
1491  }
1492  else if ( widgetInfo.labelOnTop )
1493  {
1494  QVBoxLayout *c = new QVBoxLayout();
1495  label->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1496  c->addWidget( label );
1497  c->addWidget( widgetInfo.widget );
1498  layout->addLayout( c, row, column, 1, 2 );
1499  column += 2;
1500  }
1501  else
1502  {
1503  layout->addWidget( label, row, column++ );
1504  layout->addWidget( widgetInfo.widget, row, column++ );
1505  }
1506 
1507  // Alias DD overrides
1508  if ( widgDef->type() == QgsAttributeEditorElement::AttributeEditorType::AeTypeField )
1509  {
1510  const QgsAttributeEditorField *fieldElement { static_cast<QgsAttributeEditorField *>( widgDef ) };
1511  const int fieldIdx = fieldElement->idx();
1512  if ( fieldIdx >= 0 && fieldIdx < mLayer->fields().count() )
1513  {
1514  const QString fieldName { mLayer->fields().at( fieldIdx ).name() };
1515  if ( mLayer->editFormConfig().dataDefinedFieldProperties( fieldName ).hasProperty( QgsEditFormConfig::DataDefinedProperty::Alias ) )
1516  {
1517  const QgsProperty property { mLayer->editFormConfig().dataDefinedFieldProperties( fieldName ).property( QgsEditFormConfig::DataDefinedProperty::Alias ) };
1518  if ( property.isActive() && ! property.expressionString().isEmpty() )
1519  {
1520  mLabelDataDefinedProperties[ label ] = property;
1521  }
1522  }
1523  }
1524  }
1525  }
1526 
1527  if ( column >= columnCount * 2 )
1528  {
1529  column = 0;
1530  row += 1;
1531  }
1532  }
1533  formWidget = container;
1534  }
1535 
1536  // Autogenerate Layout
1537  // If there is still no layout loaded (defined as autogenerate or other methods failed)
1538  mIconMap.clear();
1539 
1540  if ( !formWidget )
1541  {
1542  formWidget = new QWidget( this );
1543  QGridLayout *gridLayout = new QGridLayout( formWidget );
1544  formWidget->setLayout( gridLayout );
1545 
1546  if ( mContext.formMode() != QgsAttributeEditorContext::Embed )
1547  {
1548  // put the form into a scroll area to nicely handle cases with lots of attributes
1549  QgsScrollArea *scrollArea = new QgsScrollArea( this );
1550  scrollArea->setWidget( formWidget );
1551  scrollArea->setWidgetResizable( true );
1552  scrollArea->setFrameShape( QFrame::NoFrame );
1553  scrollArea->setFrameShadow( QFrame::Plain );
1554  scrollArea->setFocusProxy( this );
1555  layout->addWidget( scrollArea );
1556  }
1557  else
1558  {
1559  layout->addWidget( formWidget );
1560  }
1561 
1562  int row = 0;
1563 
1564  const QgsFields fields = mLayer->fields();
1565 
1566  for ( const QgsField &field : fields )
1567  {
1568  int idx = fields.lookupField( field.name() );
1569  if ( idx < 0 )
1570  continue;
1571 
1572  //show attribute alias if available
1573  QString fieldName = mLayer->attributeDisplayName( idx );
1574  QString labelText = fieldName;
1575  labelText.replace( '&', QLatin1String( "&&" ) ); // need to escape '&' or they'll be replace by _ in the label text
1576 
1577  const QgsEditorWidgetSetup widgetSetup = QgsGui::editorWidgetRegistry()->findBest( mLayer, field.name() );
1578 
1579  if ( widgetSetup.type() == QLatin1String( "Hidden" ) )
1580  continue;
1581 
1582  bool labelOnTop = mLayer->editFormConfig().labelOnTop( idx );
1583 
1584  // This will also create the widget
1585  QLabel *label = new QLabel( labelText );
1586  label->setToolTip( QgsFieldModel::fieldToolTipExtended( field, mLayer ) );
1587  QSvgWidget *i = new QSvgWidget();
1588  i->setFixedSize( 18, 18 );
1589 
1590  if ( mLayer->editFormConfig().dataDefinedFieldProperties( fieldName ).hasProperty( QgsEditFormConfig::DataDefinedProperty::Alias ) )
1591  {
1592  const QgsProperty property { mLayer->editFormConfig().dataDefinedFieldProperties( fieldName ).property( QgsEditFormConfig::DataDefinedProperty::Alias ) };
1593  if ( property.isActive() && ! property.expressionString().isEmpty() )
1594  {
1595  mLabelDataDefinedProperties[ label ] = property;
1596  }
1597  }
1598 
1599  QgsEditorWidgetWrapper *eww = QgsGui::editorWidgetRegistry()->create( widgetSetup.type(), mLayer, idx, widgetSetup.config(), nullptr, this, mContext );
1600 
1601  QWidget *w = nullptr;
1602  if ( eww )
1603  {
1604  QgsAttributeFormEditorWidget *formWidget = new QgsAttributeFormEditorWidget( eww, widgetSetup.type(), this );
1605  w = formWidget;
1606  mFormEditorWidgets.insert( idx, formWidget );
1607  mFormWidgets.append( formWidget );
1608  formWidget->createSearchWidgetWrappers( mContext );
1609 
1610  label->setBuddy( eww->widget() );
1611  }
1612  else
1613  {
1614  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() ) ) );
1615  }
1616 
1617 
1618  if ( w )
1619  w->setObjectName( field.name() );
1620 
1621  if ( eww )
1622  {
1623  addWidgetWrapper( eww );
1624  mIconMap[eww->widget()] = i;
1625  }
1626 
1627  if ( labelOnTop )
1628  {
1629  gridLayout->addWidget( label, row++, 0, 1, 2 );
1630  gridLayout->addWidget( w, row++, 0, 1, 2 );
1631  gridLayout->addWidget( i, row++, 0, 1, 2 );
1632  }
1633  else
1634  {
1635  gridLayout->addWidget( label, row, 0 );
1636  gridLayout->addWidget( w, row, 1 );
1637  gridLayout->addWidget( i, row++, 2 );
1638  }
1639 
1640  }
1641 
1642  const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mLayer );
1643  for ( const QgsRelation &rel : relations )
1644  {
1645  QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( rel, mContext );
1646 
1648  formWidget->createSearchWidgetWrappers( mContext );
1649  gridLayout->addWidget( formWidget, row++, 0, 1, 2 );
1650 
1651  mWidgets.append( rww );
1652  mFormWidgets.append( formWidget );
1653  }
1654 
1655  if ( QgsProject::instance()->relationManager()->referencedRelations( mLayer ).isEmpty() )
1656  {
1657  QSpacerItem *spacerItem = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
1658  gridLayout->addItem( spacerItem, row, 0 );
1659  gridLayout->setRowStretch( row, 1 );
1660  row++;
1661  }
1662  }
1663 
1664  updateDefaultValueDependencies();
1665 
1666  if ( !mButtonBox )
1667  {
1668  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1669  mButtonBox->setObjectName( QStringLiteral( "buttonBox" ) );
1670  layout->addWidget( mButtonBox, layout->rowCount(), 0, 1, layout->columnCount() );
1671  }
1672  mButtonBox->setVisible( buttonBoxVisible );
1673 
1674  if ( !mSearchButtonBox )
1675  {
1676  mSearchButtonBox = new QWidget();
1677  QHBoxLayout *boxLayout = new QHBoxLayout();
1678  boxLayout->setContentsMargins( 0, 0, 0, 0 );
1679  mSearchButtonBox->setLayout( boxLayout );
1680  mSearchButtonBox->setObjectName( QStringLiteral( "searchButtonBox" ) );
1681 
1682  QPushButton *clearButton = new QPushButton( tr( "&Reset Form" ), mSearchButtonBox );
1683  connect( clearButton, &QPushButton::clicked, this, &QgsAttributeForm::resetSearch );
1684  boxLayout->addWidget( clearButton );
1685  boxLayout->addStretch( 1 );
1686 
1687  QPushButton *flashButton = new QPushButton();
1688  flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1689  flashButton->setText( tr( "&Flash Features" ) );
1690  connect( flashButton, &QToolButton::clicked, this, &QgsAttributeForm::searchFlash );
1691  boxLayout->addWidget( flashButton );
1692 
1693  QPushButton *zoomButton = new QPushButton();
1694  zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1695  zoomButton->setText( tr( "&Zoom to Features" ) );
1696  connect( zoomButton, &QToolButton::clicked, this, &QgsAttributeForm::searchZoomTo );
1697  boxLayout->addWidget( zoomButton );
1698 
1699  QToolButton *selectButton = new QToolButton();
1700  selectButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1701  selectButton->setText( tr( "&Select Features" ) );
1702  selectButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFormSelect.svg" ) ) );
1703  selectButton->setPopupMode( QToolButton::MenuButtonPopup );
1704  selectButton->setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
1705  connect( selectButton, &QToolButton::clicked, this, &QgsAttributeForm::searchSetSelection );
1706  QMenu *selectMenu = new QMenu( selectButton );
1707  QAction *selectAction = new QAction( tr( "Select Features" ), selectMenu );
1708  selectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFormSelect.svg" ) ) );
1709  connect( selectAction, &QAction::triggered, this, &QgsAttributeForm::searchSetSelection );
1710  selectMenu->addAction( selectAction );
1711  QAction *addSelectAction = new QAction( tr( "Add to Current Selection" ), selectMenu );
1712  addSelectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconSelectAdd.svg" ) ) );
1713  connect( addSelectAction, &QAction::triggered, this, &QgsAttributeForm::searchAddToSelection );
1714  selectMenu->addAction( addSelectAction );
1715  QAction *deselectAction = new QAction( tr( "Remove from Current Selection" ), selectMenu );
1716  deselectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconSelectRemove.svg" ) ) );
1717  connect( deselectAction, &QAction::triggered, this, &QgsAttributeForm::searchRemoveFromSelection );
1718  selectMenu->addAction( deselectAction );
1719  QAction *filterSelectAction = new QAction( tr( "Filter Current Selection" ), selectMenu );
1720  filterSelectAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconSelectIntersect.svg" ) ) );
1721  connect( filterSelectAction, &QAction::triggered, this, &QgsAttributeForm::searchIntersectSelection );
1722  selectMenu->addAction( filterSelectAction );
1723  selectButton->setMenu( selectMenu );
1724  boxLayout->addWidget( selectButton );
1725 
1726  if ( mContext.formMode() == QgsAttributeEditorContext::Embed )
1727  {
1728  QToolButton *filterButton = new QToolButton();
1729  filterButton->setText( tr( "Filter Features" ) );
1730  filterButton->setPopupMode( QToolButton::MenuButtonPopup );
1731  filterButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
1732  connect( filterButton, &QToolButton::clicked, this, &QgsAttributeForm::filterTriggered );
1733  QMenu *filterMenu = new QMenu( filterButton );
1734  QAction *filterAndAction = new QAction( tr( "Filter Within (\"AND\")" ), filterMenu );
1735  connect( filterAndAction, &QAction::triggered, this, &QgsAttributeForm::filterAndTriggered );
1736  filterMenu->addAction( filterAndAction );
1737  QAction *filterOrAction = new QAction( tr( "Extend Filter (\"OR\")" ), filterMenu );
1738  connect( filterOrAction, &QAction::triggered, this, &QgsAttributeForm::filterOrTriggered );
1739  filterMenu->addAction( filterOrAction );
1740  filterButton->setMenu( filterMenu );
1741  boxLayout->addWidget( filterButton );
1742  }
1743  else
1744  {
1745  QPushButton *closeButton = new QPushButton( tr( "Close" ), mSearchButtonBox );
1746  connect( closeButton, &QPushButton::clicked, this, &QgsAttributeForm::closed );
1747  closeButton->setShortcut( Qt::Key_Escape );
1748  boxLayout->addWidget( closeButton );
1749  }
1750 
1751  layout->addWidget( mSearchButtonBox );
1752  }
1753  mSearchButtonBox->setVisible( mMode == QgsAttributeEditorContext::SearchMode );
1754 
1755  afterWidgetInit();
1756 
1757  connect( mButtonBox, &QDialogButtonBox::accepted, this, &QgsAttributeForm::save );
1758  connect( mButtonBox, &QDialogButtonBox::rejected, this, &QgsAttributeForm::resetValues );
1759 
1760  connect( mLayer, &QgsVectorLayer::editingStarted, this, &QgsAttributeForm::synchronizeEnabledState );
1761  connect( mLayer, &QgsVectorLayer::editingStopped, this, &QgsAttributeForm::synchronizeEnabledState );
1762 
1763  // This triggers a refresh of the form widget and gives a chance to re-format the
1764  // value to those widgets that have a different representation when in edit mode
1767 
1768 
1769  const auto constMInterfaces = mInterfaces;
1770  for ( QgsAttributeFormInterface *iface : constMInterfaces )
1771  {
1772  iface->initForm();
1773  }
1774 
1776  {
1777  hideButtonBox();
1778  }
1779 
1780  QApplication::restoreOverrideCursor();
1781 }
1782 
1783 void QgsAttributeForm::cleanPython()
1784 {
1785  if ( !mPyFormVarName.isNull() )
1786  {
1787  QString expr = QStringLiteral( "if '%1' in locals(): del %1\n" ).arg( mPyFormVarName );
1788  QgsPythonRunner::run( expr );
1789  }
1790 }
1791 
1792 void QgsAttributeForm::initPython()
1793 {
1794  cleanPython();
1795 
1796  // Init Python, if init function is not empty and the combo indicates
1797  // the source for the function code
1798  if ( !mLayer->editFormConfig().initFunction().isEmpty()
1800  {
1801 
1802  QString initFunction = mLayer->editFormConfig().initFunction();
1803  QString initFilePath = mLayer->editFormConfig().initFilePath();
1804  QString initCode;
1805 
1806  switch ( mLayer->editFormConfig().initCodeSource() )
1807  {
1809  if ( !initFilePath.isEmpty() )
1810  {
1811  QFile *inputFile = QgsApplication::instance()->networkContentFetcherRegistry()->localFile( initFilePath );
1812 
1813  if ( inputFile && inputFile->open( QFile::ReadOnly ) )
1814  {
1815  // Read it into a string
1816  QTextStream inf( inputFile );
1817  initCode = inf.readAll();
1818  inputFile->close();
1819  }
1820  else // The file couldn't be opened
1821  {
1822  QgsLogger::warning( QStringLiteral( "The external python file path %1 could not be opened!" ).arg( initFilePath ) );
1823  }
1824  }
1825  else
1826  {
1827  QgsLogger::warning( QStringLiteral( "The external python file path is empty!" ) );
1828  }
1829  break;
1830 
1832  initCode = mLayer->editFormConfig().initCode();
1833  if ( initCode.isEmpty() )
1834  {
1835  QgsLogger::warning( QStringLiteral( "The python code provided in the dialog is empty!" ) );
1836  }
1837  break;
1838 
1841  // Nothing to do: the function code should be already in the environment
1842  break;
1843  }
1844 
1845  // If we have a function code, run it
1846  if ( !initCode.isEmpty() )
1847  {
1849  QgsPythonRunner::run( initCode );
1850  else
1851  mMessageBar->pushMessage( QString(),
1852  tr( "Python macro could not be run due to missing permissions." ),
1853  Qgis::MessageLevel::Warning,
1854  messageTimeout() );
1855  }
1856 
1857  QgsPythonRunner::run( QStringLiteral( "import inspect" ) );
1858  QString numArgs;
1859 
1860  // Check for eval result
1861  if ( QgsPythonRunner::eval( QStringLiteral( "len(inspect.getfullargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
1862  {
1863  static int sFormId = 0;
1864  mPyFormVarName = QStringLiteral( "_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
1865 
1866  QString form = QStringLiteral( "%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
1867  .arg( mPyFormVarName )
1868  .arg( ( quint64 ) this );
1869 
1870  QgsPythonRunner::run( form );
1871 
1872  QgsDebugMsg( QStringLiteral( "running featureForm init: %1" ).arg( mPyFormVarName ) );
1873 
1874  // Legacy
1875  if ( numArgs == QLatin1String( "3" ) )
1876  {
1877  addInterface( new QgsAttributeFormLegacyInterface( initFunction, mPyFormVarName, this ) );
1878  }
1879  else
1880  {
1881  // If we get here, it means that the function doesn't accept three arguments
1882  QMessageBox msgBox;
1883  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 ) );
1884  msgBox.exec();
1885 #if 0
1886  QString expr = QString( "%1(%2)" )
1887  .arg( mLayer->editFormInit() )
1888  .arg( mPyFormVarName );
1889  QgsAttributeFormInterface *iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface *>( expr, "QgsAttributeFormInterface" );
1890  if ( iface )
1891  addInterface( iface );
1892 #endif
1893  }
1894  }
1895  else
1896  {
1897  // If we get here, it means that inspect couldn't find the function
1898  QMessageBox msgBox;
1899  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 ) );
1900  msgBox.exec();
1901  }
1902  }
1903 }
1904 
1905 QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAttributeEditorElement *widgetDef, QWidget *parent, QgsVectorLayer *vl, QgsAttributeEditorContext &context )
1906 {
1907  WidgetInfo newWidgetInfo;
1908 
1909  switch ( widgetDef->type() )
1910  {
1912  {
1913  const QgsAttributeEditorField *fieldDef = dynamic_cast<const QgsAttributeEditorField *>( widgetDef );
1914  if ( !fieldDef )
1915  break;
1916 
1917  const QgsFields fields = vl->fields();
1918  int fldIdx = fields.lookupField( fieldDef->name() );
1919  if ( fldIdx < fields.count() && fldIdx >= 0 )
1920  {
1921  const QgsEditorWidgetSetup widgetSetup = QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldDef->name() );
1922 
1923  QgsEditorWidgetWrapper *eww = QgsGui::editorWidgetRegistry()->create( widgetSetup.type(), mLayer, fldIdx, widgetSetup.config(), nullptr, this, mContext );
1924  QgsAttributeFormEditorWidget *formWidget = new QgsAttributeFormEditorWidget( eww, widgetSetup.type(), this );
1925  mFormEditorWidgets.insert( fldIdx, formWidget );
1926  mFormWidgets.append( formWidget );
1927 
1928  formWidget->createSearchWidgetWrappers( mContext );
1929 
1930  newWidgetInfo.widget = formWidget;
1931  addWidgetWrapper( eww );
1932 
1933  newWidgetInfo.widget->setObjectName( fields.at( fldIdx ).name() );
1934  newWidgetInfo.hint = fields.at( fldIdx ).comment();
1935  }
1936 
1937  newWidgetInfo.labelOnTop = mLayer->editFormConfig().labelOnTop( fldIdx );
1938  newWidgetInfo.labelText = mLayer->attributeDisplayName( fldIdx );
1939  newWidgetInfo.labelText.replace( '&', QLatin1String( "&&" ) ); // need to escape '&' or they'll be replace by _ in the label text
1940  newWidgetInfo.toolTip = QStringLiteral( "<b>%1</b><p>%2</p>" ).arg( mLayer->attributeDisplayName( fldIdx ), newWidgetInfo.hint );
1941  newWidgetInfo.showLabel = widgetDef->showLabel();
1942 
1943  break;
1944  }
1945 
1947  {
1948  const QgsAttributeEditorRelation *relDef = static_cast<const QgsAttributeEditorRelation *>( widgetDef );
1949 
1950  QgsRelationWidgetWrapper *rww = setupRelationWidgetWrapper( relDef->relation(), context );
1951 
1953  formWidget->createSearchWidgetWrappers( mContext );
1954 
1955  // This needs to be after QgsAttributeFormRelationEditorWidget creation, because the widget
1956  // does not exists yet until QgsAttributeFormRelationEditorWidget is created and the setters
1957  // below directly alter the widget and check for it.
1958  rww->setVisibleButtons( relDef->visibleButtons() );
1959  rww->setShowLabel( relDef->showLabel() );
1960  rww->setNmRelationId( relDef->nmRelationId() );
1962  rww->setLabel( relDef->label() );
1963 
1964  mWidgets.append( rww );
1965  mFormWidgets.append( formWidget );
1966 
1967  newWidgetInfo.widget = formWidget;
1968  newWidgetInfo.showLabel = relDef->showLabel();
1969  newWidgetInfo.labelText = QString();
1970  newWidgetInfo.labelOnTop = true;
1971  break;
1972  }
1973 
1975  {
1976  const QgsAttributeEditorContainer *container = dynamic_cast<const QgsAttributeEditorContainer *>( widgetDef );
1977  if ( !container )
1978  break;
1979 
1980  int columnCount = container->columnCount();
1981 
1982  if ( columnCount <= 0 )
1983  columnCount = 1;
1984 
1985  QString widgetName;
1986  QWidget *myContainer = nullptr;
1987  if ( container->isGroupBox() )
1988  {
1989  QGroupBox *groupBox = new QGroupBox( parent );
1990  widgetName = QStringLiteral( "QGroupBox" );
1991  if ( container->showLabel() )
1992  groupBox->setTitle( container->name() );
1993  myContainer = groupBox;
1994  newWidgetInfo.widget = myContainer;
1995  }
1996  else
1997  {
1998  myContainer = new QWidget();
1999 
2000  if ( context.formMode() != QgsAttributeEditorContext::Embed )
2001  {
2002  QgsScrollArea *scrollArea = new QgsScrollArea( parent );
2003 
2004  scrollArea->setWidget( myContainer );
2005  scrollArea->setWidgetResizable( true );
2006  scrollArea->setFrameShape( QFrame::NoFrame );
2007  widgetName = QStringLiteral( "QScrollArea QWidget" );
2008 
2009  newWidgetInfo.widget = scrollArea;
2010  }
2011  else
2012  {
2013  newWidgetInfo.widget = myContainer;
2014  widgetName = QStringLiteral( "QWidget" );
2015  }
2016  }
2017 
2018  if ( container->backgroundColor().isValid() )
2019  {
2020  QString style {QStringLiteral( "background-color: %1;" ).arg( container->backgroundColor().name() )};
2021  newWidgetInfo.widget->setStyleSheet( style );
2022  }
2023 
2024  QGridLayout *gbLayout = new QGridLayout();
2025  myContainer->setLayout( gbLayout );
2026 
2027  int row = 0;
2028  int column = 0;
2029 
2030  const QList<QgsAttributeEditorElement *> children = container->children();
2031 
2032  for ( QgsAttributeEditorElement *childDef : children )
2033  {
2034  WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
2035 
2036  if ( childDef->type() == QgsAttributeEditorElement::AeTypeContainer )
2037  {
2038  QgsAttributeEditorContainer *containerDef = static_cast<QgsAttributeEditorContainer *>( childDef );
2039  if ( containerDef->visibilityExpression().enabled() )
2040  {
2041  registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) );
2042  }
2043  }
2044 
2045  if ( widgetInfo.labelText.isNull() )
2046  {
2047  gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
2048  column += 2;
2049  }
2050  else
2051  {
2052  QLabel *mypLabel = new QLabel( widgetInfo.labelText );
2053 
2054  // Alias DD overrides
2055  if ( childDef->type() == QgsAttributeEditorElement::AeTypeField )
2056  {
2057  const QgsAttributeEditorField *fieldDef { static_cast<QgsAttributeEditorField *>( childDef ) };
2058  const QgsFields fields = vl->fields();
2059  const int fldIdx = fieldDef->idx();
2060  if ( fldIdx < fields.count() && fldIdx >= 0 )
2061  {
2062  const QString fieldName { fields.at( fldIdx ).name() };
2063  if ( mLayer->editFormConfig().dataDefinedFieldProperties( fieldName ).hasProperty( QgsEditFormConfig::DataDefinedProperty::Alias ) )
2064  {
2065  const QgsProperty property { mLayer->editFormConfig().dataDefinedFieldProperties( fieldName ).property( QgsEditFormConfig::DataDefinedProperty::Alias ) };
2066  if ( property.isActive() && ! property.expressionString().isEmpty() )
2067  {
2068  mLabelDataDefinedProperties[ mypLabel ] = property;
2069  }
2070  }
2071  }
2072  }
2073 
2074  mypLabel->setToolTip( widgetInfo.toolTip );
2075  if ( columnCount > 1 && !widgetInfo.labelOnTop )
2076  {
2077  mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
2078  }
2079 
2080  mypLabel->setBuddy( widgetInfo.widget );
2081 
2082  if ( widgetInfo.labelOnTop )
2083  {
2084  QVBoxLayout *c = new QVBoxLayout();
2085  mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
2086  c->layout()->addWidget( mypLabel );
2087  c->layout()->addWidget( widgetInfo.widget );
2088  gbLayout->addLayout( c, row, column, 1, 2 );
2089  column += 2;
2090  }
2091  else
2092  {
2093  gbLayout->addWidget( mypLabel, row, column++ );
2094  gbLayout->addWidget( widgetInfo.widget, row, column++ );
2095  }
2096  }
2097 
2098  if ( column >= columnCount * 2 )
2099  {
2100  column = 0;
2101  row += 1;
2102  }
2103  }
2104  QWidget *spacer = new QWidget();
2105  spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
2106  gbLayout->addWidget( spacer, ++row, 0 );
2107  gbLayout->setRowStretch( row, 1 );
2108 
2109  newWidgetInfo.labelText = QString();
2110  newWidgetInfo.labelOnTop = true;
2111  break;
2112  }
2113 
2115  {
2116  const QgsAttributeEditorQmlElement *elementDef = static_cast<const QgsAttributeEditorQmlElement *>( widgetDef );
2117 
2118  QgsQmlWidgetWrapper *qmlWrapper = new QgsQmlWidgetWrapper( mLayer, nullptr, this );
2119  qmlWrapper->setQmlCode( elementDef->qmlCode() );
2120  context.setAttributeFormMode( mMode );
2121  qmlWrapper->setContext( context );
2122 
2123  mWidgets.append( qmlWrapper );
2124 
2125  newWidgetInfo.widget = qmlWrapper->widget();
2126  newWidgetInfo.labelText = elementDef->name();
2127  newWidgetInfo.labelOnTop = true;
2128  newWidgetInfo.showLabel = widgetDef->showLabel();
2129  break;
2130  }
2131 
2133  {
2134  const QgsAttributeEditorHtmlElement *elementDef = static_cast<const QgsAttributeEditorHtmlElement *>( widgetDef );
2135 
2136  QgsHtmlWidgetWrapper *htmlWrapper = new QgsHtmlWidgetWrapper( mLayer, nullptr, this );
2137  context.setAttributeFormMode( mMode );
2138  htmlWrapper->setHtmlCode( elementDef->htmlCode() );
2139  htmlWrapper->reinitWidget();
2140  mWidgets.append( htmlWrapper );
2141 
2142  newWidgetInfo.widget = htmlWrapper->widget();
2143  newWidgetInfo.labelText = elementDef->name();
2144  newWidgetInfo.labelOnTop = true;
2145  newWidgetInfo.showLabel = widgetDef->showLabel();
2146  break;
2147  }
2148 
2149  default:
2150  QgsDebugMsg( QStringLiteral( "Unknown attribute editor widget type encountered..." ) );
2151  break;
2152  }
2153 
2154  newWidgetInfo.showLabel = widgetDef->showLabel();
2155 
2156  return newWidgetInfo;
2157 }
2158 
2159 void QgsAttributeForm::addWidgetWrapper( QgsEditorWidgetWrapper *eww )
2160 {
2161  for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
2162  {
2163  QgsEditorWidgetWrapper *meww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
2164  if ( meww )
2165  {
2166  // if another widget wrapper exists for the same field
2167  // synchronise them
2168  if ( meww->field() == eww->field() )
2169  {
2172  break;
2173  }
2174  }
2175  }
2176 
2177  mWidgets.append( eww );
2178 }
2179 
2180 void QgsAttributeForm::createWrappers()
2181 {
2182  QList<QWidget *> myWidgets = findChildren<QWidget *>();
2183  const QList<QgsField> fields = mLayer->fields().toList();
2184 
2185  const auto constMyWidgets = myWidgets;
2186  for ( QWidget *myWidget : constMyWidgets )
2187  {
2188  // Check the widget's properties for a relation definition
2189  QVariant vRel = myWidget->property( "qgisRelation" );
2190  if ( vRel.isValid() )
2191  {
2193  QgsRelation relation = relMgr->relation( vRel.toString() );
2194  if ( relation.isValid() )
2195  {
2196  QgsRelationWidgetWrapper *rww = new QgsRelationWidgetWrapper( mLayer, relation, myWidget, this );
2197  rww->setConfig( mLayer->editFormConfig().widgetConfig( relation.id() ) );
2198  rww->setContext( mContext );
2199  rww->widget(); // Will initialize the widget
2200  mWidgets.append( rww );
2201  }
2202  }
2203  else
2204  {
2205  const auto constFields = fields;
2206  for ( const QgsField &field : constFields )
2207  {
2208  if ( field.name() == myWidget->objectName() )
2209  {
2210  int idx = mLayer->fields().lookupField( field.name() );
2211 
2212  QgsEditorWidgetWrapper *eww = QgsGui::editorWidgetRegistry()->create( mLayer, idx, myWidget, this, mContext );
2213  addWidgetWrapper( eww );
2214  }
2215  }
2216  }
2217  }
2218 }
2219 
2220 void QgsAttributeForm::afterWidgetInit()
2221 {
2222  bool isFirstEww = true;
2223 
2224  const auto constMWidgets = mWidgets;
2225  for ( QgsWidgetWrapper *ww : constMWidgets )
2226  {
2227  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
2228 
2229  if ( eww )
2230  {
2231  if ( isFirstEww )
2232  {
2233  setFocusProxy( eww->widget() );
2234  isFirstEww = false;
2235  }
2236 
2237  connect( eww, &QgsEditorWidgetWrapper::valuesChanged, this, &QgsAttributeForm::onAttributeChanged );
2238  connect( eww, &QgsEditorWidgetWrapper::constraintStatusChanged, this, &QgsAttributeForm::onConstraintStatusChanged );
2239  }
2240  }
2241 }
2242 
2243 
2244 bool QgsAttributeForm::eventFilter( QObject *object, QEvent *e )
2245 {
2246  Q_UNUSED( object )
2247 
2248  if ( e->type() == QEvent::KeyPress )
2249  {
2250  QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>( e );
2251  if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
2252  {
2253  // Re-emit to this form so it will be forwarded to parent
2254  event( e );
2255  return true;
2256  }
2257  }
2258 
2259  return false;
2260 }
2261 
2262 void QgsAttributeForm::scanForEqualAttributes( QgsFeatureIterator &fit,
2263  QSet< int > &mixedValueFields,
2264  QHash< int, QVariant > &fieldSharedValues ) const
2265 {
2266  mixedValueFields.clear();
2267  fieldSharedValues.clear();
2268 
2269  QgsFeature f;
2270  bool first = true;
2271  while ( fit.nextFeature( f ) )
2272  {
2273  for ( int i = 0; i < mLayer->fields().count(); ++i )
2274  {
2275  if ( mixedValueFields.contains( i ) )
2276  continue;
2277 
2278  if ( first )
2279  {
2280  fieldSharedValues[i] = f.attribute( i );
2281  }
2282  else
2283  {
2284  if ( fieldSharedValues.value( i ) != f.attribute( i ) )
2285  {
2286  fieldSharedValues.remove( i );
2287  mixedValueFields.insert( i );
2288  }
2289  }
2290  }
2291  first = false;
2292 
2293  if ( mixedValueFields.count() == mLayer->fields().count() )
2294  {
2295  // all attributes are mixed, no need to keep scanning
2296  break;
2297  }
2298  }
2299 }
2300 
2301 
2302 void QgsAttributeForm::layerSelectionChanged()
2303 {
2304  switch ( mMode )
2305  {
2312  break;
2313 
2315  resetMultiEdit( true );
2316  break;
2317  }
2318 }
2319 
2321 {
2322  mIsSettingMultiEditFeatures = true;
2323  mMultiEditFeatureIds = fids;
2324 
2325  if ( fids.isEmpty() )
2326  {
2327  // no selected features
2328  QMap< int, QgsAttributeFormEditorWidget * >::const_iterator wIt = mFormEditorWidgets.constBegin();
2329  for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
2330  {
2331  wIt.value()->initialize( QVariant() );
2332  }
2333  mIsSettingMultiEditFeatures = false;
2334  return;
2335  }
2336 
2337  QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest().setFilterFids( fids ) );
2338 
2339  // Scan through all features to determine which attributes are initially the same
2340  QSet< int > mixedValueFields;
2341  QHash< int, QVariant > fieldSharedValues;
2342  scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
2343 
2344  // also fetch just first feature
2345  fit = mLayer->getFeatures( QgsFeatureRequest().setFilterFid( *fids.constBegin() ) );
2346  QgsFeature firstFeature;
2347  fit.nextFeature( firstFeature );
2348 
2349  const auto constMixedValueFields = mixedValueFields;
2350  for ( int fieldIndex : qgis::as_const( mixedValueFields ) )
2351  {
2352  if ( QgsAttributeFormEditorWidget *w = mFormEditorWidgets.value( fieldIndex, nullptr ) )
2353  {
2354  const QStringList additionalFields = w->editorWidget()->additionalFields();
2355  QVariantList additionalFieldValues;
2356  for ( const QString &additionalField : additionalFields )
2357  additionalFieldValues << firstFeature.attribute( additionalField );
2358  w->initialize( firstFeature.attribute( fieldIndex ), true, additionalFieldValues );
2359  }
2360  }
2361  QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
2362  for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
2363  {
2364  if ( QgsAttributeFormEditorWidget *w = mFormEditorWidgets.value( sharedValueIt.key(), nullptr ) )
2365  {
2366  bool mixed = false;
2367  const QStringList additionalFields = w->editorWidget()->additionalFields();
2368  for ( const QString &additionalField : additionalFields )
2369  {
2370  int index = mLayer->fields().indexFromName( additionalField );
2371  if ( constMixedValueFields.contains( index ) )
2372  {
2373  // if additional field are mixed, it is considered as mixed
2374  mixed = true;
2375  break;
2376  }
2377  }
2378  QVariantList additionalFieldValues;
2379  if ( mixed )
2380  {
2381  for ( const QString &additionalField : additionalFields )
2382  additionalFieldValues << firstFeature.attribute( additionalField );
2383  w->initialize( firstFeature.attribute( sharedValueIt.key() ), true, additionalFieldValues );
2384  }
2385  else
2386  {
2387  for ( const QString &additionalField : additionalFields )
2388  {
2389  int index = mLayer->fields().indexFromName( additionalField );
2390  Q_ASSERT( fieldSharedValues.contains( index ) );
2391  additionalFieldValues << fieldSharedValues.value( index );
2392  }
2393  w->initialize( sharedValueIt.value(), false, additionalFieldValues );
2394  }
2395  }
2396  }
2397  mIsSettingMultiEditFeatures = false;
2398 }
2399 
2401 {
2402  if ( mOwnsMessageBar )
2403  delete mMessageBar;
2404  mOwnsMessageBar = false;
2405  mMessageBar = messageBar;
2406 }
2407 
2409 {
2411  {
2412  Q_ASSERT( false );
2413  }
2414 
2415  QStringList filters;
2416  for ( QgsAttributeFormWidget *widget : mFormWidgets )
2417  {
2418  QString filter = widget->currentFilterExpression();
2419  if ( !filter.isNull() )
2420  filters << '(' + filter + ')';
2421  }
2422 
2423  return filters.join( QLatin1String( " AND " ) );
2424 }
2425 
2427 {
2428  mExtraContextScope.reset( extraScope );
2429 }
2430 
2431 int QgsAttributeForm::messageTimeout()
2432 {
2433  QgsSettings settings;
2434  return settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
2435 }
2436 
2437 void QgsAttributeForm::ContainerInformation::apply( QgsExpressionContext *expressionContext )
2438 {
2439  bool newVisibility = expression.evaluate( expressionContext ).toBool();
2440 
2441  if ( newVisibility != isVisible )
2442  {
2443  if ( tabWidget )
2444  {
2445  tabWidget->setTabVisible( widget, newVisibility );
2446  }
2447  else
2448  {
2449  widget->setVisible( newVisibility );
2450  }
2451 
2452  isVisible = newVisibility;
2453  }
2454 }
2455 
2456 void QgsAttributeForm::updateJoinedFields( const QgsEditorWidgetWrapper &eww )
2457 {
2458  if ( !eww.layer()->fields().exists( eww.fieldIdx() ) )
2459  return;
2460 
2461  QgsFeature formFeature;
2462  QgsField field = eww.layer()->fields().field( eww.fieldIdx() );
2463  QList<const QgsVectorLayerJoinInfo *> infos = eww.layer()->joinBuffer()->joinsWhereFieldIsId( field );
2464 
2465  if ( infos.count() == 0 || !currentFormValuesFeature( formFeature ) )
2466  return;
2467 
2468  const QString hint = tr( "No feature joined" );
2469  const auto constInfos = infos;
2470  for ( const QgsVectorLayerJoinInfo *info : constInfos )
2471  {
2472  if ( !info->isDynamicFormEnabled() )
2473  continue;
2474 
2475  QgsFeature joinFeature = mLayer->joinBuffer()->joinedFeatureOf( info, formFeature );
2476 
2477  mJoinedFeatures[info] = joinFeature;
2478 
2479  if ( info->hasSubset() )
2480  {
2481  const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *info );
2482 
2483  const auto constSubsetNames = subsetNames;
2484  for ( const QString &field : constSubsetNames )
2485  {
2486  QString prefixedName = info->prefixedFieldName( field );
2487  QVariant val;
2488  QString hintText = hint;
2489 
2490  if ( joinFeature.isValid() )
2491  {
2492  val = joinFeature.attribute( field );
2493  hintText.clear();
2494  }
2495 
2496  changeAttribute( prefixedName, val, hintText );
2497  }
2498  }
2499  else
2500  {
2501  const QgsFields joinFields = joinFeature.fields();
2502  for ( const QgsField &field : joinFields )
2503  {
2504  QString prefixedName = info->prefixedFieldName( field );
2505  QVariant val;
2506  QString hintText = hint;
2507 
2508  if ( joinFeature.isValid() )
2509  {
2510  val = joinFeature.attribute( field.name() );
2511  hintText.clear();
2512  }
2513 
2514  changeAttribute( prefixedName, val, hintText );
2515  }
2516  }
2517  }
2518 }
2519 
2520 bool QgsAttributeForm::fieldIsEditable( int fieldIndex ) const
2521 {
2522  return QgsVectorLayerUtils::fieldIsEditable( mLayer, fieldIndex, mFeature );
2523 }
2524 
2525 void QgsAttributeForm::updateDefaultValueDependencies()
2526 {
2527  mDefaultValueDependencies.clear();
2528  //create defaultValueDependencies
2529  for ( QgsWidgetWrapper *ww : qgis::as_const( mWidgets ) )
2530  {
2531  QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
2532  if ( eww )
2533  {
2535  const QSet<QString> referencedColumns = exp.referencedColumns();
2536  for ( const QString &referencedColumn : referencedColumns )
2537  {
2538  if ( referencedColumn == QgsFeatureRequest::ALL_ATTRIBUTES )
2539  {
2540  const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
2541 
2542  for ( const int id : allAttributeIds )
2543  {
2544  mDefaultValueDependencies.insertMulti( id, eww );
2545  }
2546  }
2547  else
2548  {
2549  mDefaultValueDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
2550  }
2551  }
2552  }
2553  }
2554 }
2555 
2556 void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww )
2557 {
2558  if ( !eww->widget() || !mIconMap[eww->widget()] )
2559  return;
2560 
2561  // no icon by default
2562  mIconMap[eww->widget()]->hide();
2563 
2564  if ( !eww->widget()->isEnabled() && mLayer->isEditable() )
2565  {
2566  if ( mLayer->fields().fieldOrigin( eww->fieldIdx() ) == QgsFields::OriginJoin )
2567  {
2568  int srcFieldIndex;
2569  const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( eww->fieldIdx(), mLayer->fields(), srcFieldIndex );
2570 
2571  if ( !info )
2572  return;
2573 
2574  if ( !info->isEditable() )
2575  {
2576  const QString file = QStringLiteral( "/mIconJoinNotEditable.svg" );
2577  const QString tooltip = tr( "Join settings do not allow editing" );
2578  reloadIcon( file, tooltip, mIconMap[eww->widget()] );
2579  }
2580  else if ( mMode == QgsAttributeEditorContext::AddFeatureMode && !info->hasUpsertOnEdit() )
2581  {
2582  const QString file = QStringLiteral( "mIconJoinHasNotUpsertOnEdit.svg" );
2583  const QString tooltip = tr( "Join settings do not allow upsert on edit" );
2584  reloadIcon( file, tooltip, mIconMap[eww->widget()] );
2585  }
2586  else if ( !info->joinLayer()->isEditable() )
2587  {
2588  const QString file = QStringLiteral( "/mIconJoinedLayerNotEditable.svg" );
2589  const QString tooltip = tr( "Joined layer is not toggled editable" );
2590  reloadIcon( file, tooltip, mIconMap[eww->widget()] );
2591  }
2592  }
2593  }
2594 }
2595 
2596 void QgsAttributeForm::reloadIcon( const QString &file, const QString &tooltip, QSvgWidget *sw )
2597 {
2598  sw->load( QgsApplication::iconPath( file ) );
2599  sw->setToolTip( tooltip );
2600  sw->show();
2601 }
QgsAttributeEditorElement
This is an abstract base class for any elements of a drag and drop form.
Definition: qgsattributeeditorelement.h:40
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:993
QgsVectorLayerJoinInfo::isDynamicFormEnabled
bool isDynamicFormEnabled() const
Returns whether the form has to be dynamically updated with joined fields when a feature is being cre...
Definition: qgsvectorlayerjoininfo.h:81
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:370
QgsAttributeEditorContext::SingleEditMode
@ SingleEditMode
Single edit mode, for editing a single feature.
Definition: qgsattributeeditorcontext.h:49
QgsProject::relationManager
QgsRelationManager * relationManager
Definition: qgsproject.h:105
QgsEditFormConfig::initCodeSource
PythonInitCodeSource initCodeSource() const
Returns Python code source for edit form initialization (if it shall be loaded from a file,...
Definition: qgseditformconfig.cpp:313
QgsVectorLayer::beforeRemovingExpressionField
void beforeRemovingExpressionField(int idx)
Will be emitted, when an expression field is going to be deleted from this vector layer.
qgsexpressioncontextutils.h
QgsAttributeFormWidget::DefaultMode
@ DefaultMode
Default mode, only the editor widget is shown.
Definition: qgsattributeformwidget.h:47
QgsAttributeForm::modeChanged
void modeChanged(QgsAttributeEditorContext::Mode mode)
Emitted when the form changes mode.
QgsExpressionContext::appendScopes
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Definition: qgsexpressioncontext.cpp:495
QgsAttributeFormInterface
Definition: qgsattributeforminterface.h:29
QgsGui::editorWidgetRegistry
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition: qgsgui.cpp:74
QgsGui::pythonMacroAllowed
static bool pythonMacroAllowed(void(*lambda)()=nullptr, QgsMessageBar *messageBar=nullptr)
Returns true if python macros are currently allowed to be run If the global option is to ask user,...
Definition: qgsgui.cpp:256
QgsAttributeForm::flashFeatures
void flashFeatures(const QString &filter)
Emitted when the user chooses to flash a filtered set of features.
qgseditorwidgetwrapper.h
QgsFeature::id
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QgsAttributeFormEditorWidget::currentValue
QVariant currentValue() const
Returns the current value of the attached editor widget.
Definition: qgsattributeformeditorwidget.cpp:163
QgsAttributeForm::resetSearch
void resetSearch()
Resets the search/filter form values.
Definition: qgsattributeform.cpp:812
QgsEditorWidgetWrapper::setValues
void setValues(const QVariant &value, const QVariantList &additionalValues)
Is called when the value of the widget or additional field values needs to be changed.
Definition: qgseditorwidgetwrapper.cpp:86
QgsAttributeEditorContext::allowCustomUi
bool allowCustomUi() const
Returns true if the attribute editor should permit use of custom UI forms.
Definition: qgsattributeeditorcontext.h:219
qgsattributeform.h
QgsOptional::enabled
bool enabled() const
Check if this optional is enabled.
Definition: qgsoptional.h:89
QgsRelationManager
This class manages a set of relations between layers.
Definition: qgsrelationmanager.h:35
QgsAttributeEditorContainer::columnCount
int columnCount() const
Gets the number of columns in this group.
Definition: qgseditformconfig.cpp:727
qgsfeaturerequest.h
QgsTextEditWrapper
Wraps a text widget.
Definition: qgstexteditwrapper.h:42
QgsProperty
A store for object properties.
Definition: qgsproperty.h:232
QgsTextEditWrapper::isInvalidJSON
bool isInvalidJSON()
Returns whether the text edit widget contains Invalid JSON.
Definition: qgstexteditwrapper.cpp:267
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
qgsmessagebaritem.h
QgsWidgetWrapper
Manages an editor widget Widget and wrapper share the same parent.
Definition: qgswidgetwrapper.h:53
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
QgsEditorWidgetWrapper::isValidConstraint
bool isValidConstraint() const
Gets the current constraint status.
Definition: qgseditorwidgetwrapper.cpp:266
QgsAttributeEditorElement::AeTypeRelation
@ AeTypeRelation
A relation.
Definition: qgsattributeeditorelement.h:66
QgsEditorWidgetSetup
Holder for the widget type and its configuration for a field.
Definition: qgseditorwidgetsetup.h:29
QgsEditFormConfig::initFunction
QString initFunction() const
Gets Python function for edit form initialization.
Definition: qgseditformconfig.cpp:273
QgsExpression::referencedColumns
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
Definition: qgsexpression.cpp:217
QgsEditorWidgetWrapper::setValue
virtual void setValue(const QVariant &value)
Is called when the value of the widget needs to be changed.
Definition: qgseditorwidgetwrapper.cpp:79
QgsAttributeEditorField
This element will load a field's widget onto the form.
Definition: qgsattributeeditorelement.h:302
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsVectorLayer::RemoveFromSelection
@ RemoveFromSelection
Remove from current selection.
Definition: qgsvectorlayer.h:416
QgsEditFormConfig::labelOnTop
bool labelOnTop(int idx) const
If this returns true, the widget at the given index will receive its label on the previous line while...
Definition: qgseditformconfig.cpp:247
QgsExpressionContextUtils::formScope
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 form...
Definition: qgsexpressioncontextutils.cpp:201
qgsgui.h
QgsEditorWidgetWrapper::setHint
virtual void setHint(const QString &hintText)
Add a hint text on the widget.
Definition: qgseditorwidgetwrapper.cpp:289
QgsAttributeForm::addInterface
void addInterface(QgsAttributeFormInterface *iface)
Takes ownership.
Definition: qgsattributeform.cpp:121
QgsEditorWidgetWrapper::additionalFieldValues
virtual QVariantList additionalFieldValues() const
Will be used to access the widget's values for potential additional fields handled by the widget.
Definition: qgseditorwidgetwrapper.h:100
QgsAttributeFormLegacyInterface
This class helps to support legacy open form scripts to be compatible with the new QgsAttributeForm s...
Definition: qgsattributeformlegacyinterface.h:33
Qgis::Warning
@ Warning
Definition: qgis.h:91
QgsAttributeEditorContext::AddFeatureMode
@ AddFeatureMode
Definition: qgsattributeeditorcontext.h:50
QgsRelationWidgetWrapper
Definition: qgsrelationwidgetwrapper.h:31
QgsAttributeForm::changeAttribute
void changeAttribute(const QString &field, const QVariant &value, const QString &hintText=QString())
Call this to change the content of a given attribute.
Definition: qgsattributeform.cpp:254
QgsAttributeForm::setFeature
void setFeature(const QgsFeature &feature)
Update all editors to correspond to a different feature.
Definition: qgsattributeform.cpp:281
qgsfeatureiterator.h
QgsFields::count
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsAttributeEditorContainer::isGroupBox
virtual bool isGroupBox() const
Returns if this container is going to be rendered as a group box.
Definition: qgsattributeeditorelement.h:211
QgsEditorWidgetSetup::config
QVariantMap config() const
Definition: qgseditorwidgetsetup.h:51
QgsQmlWidgetWrapper
Wraps a QQuickWidget to display QML code.
Definition: qgsqmlwidgetwrapper.h:30
QgsAttributeForm::feature
const QgsFeature & feature()
Definition: qgsattributeform.h:76
QgsFieldConstraints::constraintExpression
QString constraintExpression() const
Returns the constraint expression for the field, if set.
Definition: qgsfieldconstraints.cpp:67
QgsAttributeForm::editable
bool editable()
Returns if the form is currently in editable mode.
Definition: qgsattributeform.cpp:126
QgsAttributeFormWidget::AggregateSearchMode
@ AggregateSearchMode
Embedded in a search form, show additional aggregate function toolbutton.
Definition: qgsattributeformwidget.h:50
QgsFeature::setValid
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:190
QgsEditFormConfig::CodeSourceFile
@ CodeSourceFile
Load the Python code from an external file.
Definition: qgseditformconfig.h:96
QgsAttributeForm::save
bool save()
Save all the values from the editors to the layer.
Definition: qgsattributeform.cpp:738
QgsAttributeFormRelationEditorWidget
Widget to show for child relations on an attribute form.
Definition: qgsattributeformrelationeditorwidget.h:34
QgsEditorWidgetWrapper::value
virtual QVariant value() const =0
Will be used to access the widget's value.
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
QgsRelationManager::relation
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Definition: qgsrelationmanager.cpp:96
QgsVectorLayerJoinBuffer::joinForFieldIndex
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
Definition: qgsvectorlayerjoinbuffer.cpp:397
QgsVectorLayer::selectByExpression
Q_INVOKABLE void selectByExpression(const QString &expression, QgsVectorLayer::SelectBehavior behavior=QgsVectorLayer::SetSelection)
Selects matching features using an expression.
Definition: qgsvectorlayer.cpp:463
QgsRelation::id
Q_GADGET QString id
Definition: qgsrelation.h:45
qgsattributeformlegacyinterface.h
QgsEditFormConfig::CodeSourceNone
@ CodeSourceNone
Do not use Python code at all.
Definition: qgseditformconfig.h:95
QgsAttributeFormEditorWidget::setConstraintStatus
void setConstraintStatus(const QString &constraint, const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result)
Set the constraint status for this widget.
Definition: qgsattributeformeditorwidget.cpp:93
QgsVectorLayerJoinInfo::joinFieldNamesSubset
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
Definition: qgsvectorlayerjoininfo.h:223
Qgis::Success
@ Success
Definition: qgis.h:93
QgsFieldModel::fieldToolTipExtended
static QString fieldToolTipExtended(const QgsField &field, const QgsVectorLayer *layer)
Returns a HTML formatted tooltip string for a field, containing details like the field name,...
Definition: qgsfieldmodel.cpp:500
QgsExpressionContextUtils::globalProjectLayerScopes
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Definition: qgsexpressioncontextutils.cpp:307
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
qgstabwidget.h
QgsAttributeEditorContext::SearchMode
@ SearchMode
Form values are used for searching/filtering the layer.
Definition: qgsattributeeditorcontext.h:54
QgsAttributeEditorContext::Embed
@ Embed
A form was embedded as a widget on another form.
Definition: qgsattributeeditorcontext.h:72
QgsApplication::instance
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
Definition: qgsapplication.cpp:411
QgsAttributeForm::zoomToFeatures
void zoomToFeatures(const QString &filter)
Emitted when the user chooses to zoom to a filtered set of features.
QgsEditorWidgetWrapper::ConstraintResult
ConstraintResult
Result of constraint checks.
Definition: qgseditorwidgetwrapper.h:61
QgsAttributeFormRelationEditorWidget::createSearchWidgetWrappers
void createSearchWidgetWrappers(const QgsAttributeEditorContext &context=QgsAttributeEditorContext()) override
Creates the search widget wrappers for the widget used when the form is in search mode.
Definition: qgsattributeformrelationeditorwidget.cpp:29
QgsRelationWidgetWrapper::setLabel
void setLabel(const QString &label=QString())
Sets label for this element If it's empty it takes the relation id as label.
Definition: qgsrelationwidgetwrapper.cpp:285
QgsEditFormConfig::initFilePath
QString initFilePath() const
Gets Python external file path for edit form initialization.
Definition: qgseditformconfig.cpp:295
field
const QgsField & field
Definition: qgsfield.h:456
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsEditorWidgetWrapper::additionalFields
virtual QStringList additionalFields() const
Returns the list of additional fields which the editor handles.
Definition: qgseditorwidgetwrapper.h:92
QgsApplication::iconPath
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
Definition: qgsapplication.cpp:615
QgsAttributeForm::aggregateFilter
QString aggregateFilter() const
The aggregate filter is only useful if the form is in AggregateFilter mode.
Definition: qgsattributeform.cpp:2408
QgsAttributeEditorContainer::children
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
Definition: qgsattributeeditorelement.h:218
QgsTabWidget::setTabVisible
void setTabVisible(QWidget *tab, bool visible)
Control the visibility for the tab with the given widget.
Definition: qgstabwidget.cpp:51
QgsMessageBar::popWidget
bool popWidget(QgsMessageBarItem *item)
Remove the specified item from the bar, and display the next most recent one in the stack.
Definition: qgsmessagebar.cpp:160
QgsField::name
QString name
Definition: qgsfield.h:59
QgsVectorLayer::beginEditCommand
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
Definition: qgsvectorlayer.cpp:3667
QgsWidgetWrapper::widget
QWidget * widget()
Access the widget managed by this wrapper.
Definition: qgswidgetwrapper.cpp:46
QgsVectorLayer::defaultValue
QVariant defaultValue(int index, const QgsFeature &feature=QgsFeature(), QgsExpressionContext *context=nullptr) const
Returns the calculated default value for the specified field index.
Definition: qgsvectorlayer.cpp:3898
QgsVectorLayer::changeAttributeValue
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).
Definition: qgsvectorlayer.cpp:2966
QgsRelationWidgetWrapper::setNmRelationId
void setNmRelationId(const QVariant &nmRelationId=QVariant())
Sets nmRelationId for the relation id of the second relation involved in an N:M relation.
Definition: qgsrelationwidgetwrapper.cpp:251
QgsAttributeForm::disconnectButtonBox
void disconnectButtonBox()
Disconnects the button box (OK/Cancel) from the accept/resetValues slots If this method is called,...
Definition: qgsattributeform.cpp:115
QgsVectorLayer::isEditable
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
Definition: qgsvectorlayer.cpp:3594
QgsEditorWidgetWrapper
Manages an editor widget Widget and wrapper share the same parent.
Definition: qgseditorwidgetwrapper.h:48
QgsPropertyCollection::property
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
Definition: qgspropertycollection.cpp:214
QgsFields::toList
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfields.cpp:212
QgsEditorWidgetWrapper::fieldIdx
int fieldIdx() const
Access the field index.
Definition: qgseditorwidgetwrapper.cpp:34
QgsAttributeForm::layer
QgsVectorLayer * layer()
Returns the layer for which this form is shown.
Definition: qgsattributeform.h:129
QgsAttributeFormEditorWidget::createSearchWidgetWrappers
void createSearchWidgetWrappers(const QgsAttributeEditorContext &context=QgsAttributeEditorContext()) override
Creates the search widget wrappers for the widget used when the form is in search mode.
Definition: qgsattributeformeditorwidget.cpp:73
QgsVectorLayer::changeAttributeValues
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).
Definition: qgsvectorlayer.cpp:2997
qgsapplication.h
qgsnetworkcontentfetcherregistry.h
QgsFeatureRequest::ALL_ATTRIBUTES
static const QString ALL_ATTRIBUTES
A special attribute that if set matches all attributes.
Definition: qgsfeaturerequest.h:282
QgsAttributeForm::parentFormValueChanged
void parentFormValueChanged(const QString &attribute, const QVariant &newValue)
Is called in embedded forms when an attribute value in the parent form has changed to newValue.
Definition: qgsattributeform.cpp:1287
QgsMapLayer::triggerRepaint
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
Definition: qgsmaplayer.cpp:1826
QgsAttributeFormWidget::MultiEditMode
@ MultiEditMode
Multi edit mode, both the editor widget and a QgsMultiEditToolButton is shown.
Definition: qgsattributeformwidget.h:48
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3283
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:797
QgsEditorWidgetSetup::type
QString type() const
Definition: qgseditorwidgetsetup.h:46
QgsAttributeForm::hideButtonBox
void hideButtonBox()
Hides the button box (OK/Cancel) and enables auto-commit.
Definition: qgsattributeform.cpp:99
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:76
QgsAttributeEditorContext::Mode
Mode
modes
Definition: qgsattributeeditorcontext.h:48
QgsAttributeEditorField::idx
int idx() const
Returns the index of the field.
Definition: qgsattributeeditorelement.h:320
QgsAttributeForm::refreshFeature
void refreshFeature()
reload current feature
Definition: qgsattributeform.cpp:1273
QgsAttributeForm::mode
QgsAttributeEditorContext::Mode mode() const
Returns the current mode of the form.
Definition: qgsattributeform.h:143
QgsAttributeForm::FilterOr
@ FilterOr
Filter should be combined using "OR".
Definition: qgsattributeform.h:67
QgsVectorLayer::editingStarted
void editingStarted()
Emitted when editing on this layer has started.
QgsAttributeEditorElement::AeTypeContainer
@ AeTypeContainer
A container.
Definition: qgsattributeeditorelement.h:64
qgsqmlwidgetwrapper.h
QgsAttributeForm::setExtraContextScope
void setExtraContextScope(QgsExpressionContextScope *extraScope)
Sets an additional expression context scope to be used for calculations in this form.
Definition: qgsattributeform.cpp:2426
QgsVectorLayer::endEditCommand
void endEditCommand()
Finish edit command and add it to undo/redo stack.
Definition: qgsvectorlayer.cpp:3683
QgsVectorLayer::AddToSelection
@ AddToSelection
Add selection to current selection.
Definition: qgsvectorlayer.h:414
QgsAttributeFormEditorWidget::changesCommitted
void changesCommitted()
Called when field values have been committed;.
Definition: qgsattributeformeditorwidget.cpp:132
QgsAttributeEditorQmlElement
An attribute editor widget that will represent arbitrary QML code.
Definition: qgsattributeeditorelement.h:531
Qgis::Info
@ Info
Definition: qgis.h:90
qgsscrollarea.h
QgsAttributeEditorElement::showLabel
bool showLabel() const
Controls if this element should be labeled with a title (field, relation or groupname).
Definition: qgsattributeeditorelement.cpp:125
QgsEditFormConfig::CodeSourceDialog
@ CodeSourceDialog
Use the Python code provided in the dialog.
Definition: qgseditformconfig.h:97
QgsRelationManager::referencedRelations
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
Definition: qgsrelationmanager.cpp:160
QgsAttributeForm::setMultiEditFeatureIds
void setMultiEditFeatureIds(const QgsFeatureIds &fids)
Sets all feature IDs which are to be edited if the form is in multiedit mode.
Definition: qgsattributeform.cpp:2320
QgsWidgetWrapper::layer
QgsVectorLayer * layer() const
Returns the vector layer associated with the widget.
Definition: qgswidgetwrapper.cpp:91
qgsvectorlayerjoinbuffer.h
QgsFeature::isValid
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:185
QgsFeature::setFields
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:164
QgsVectorLayer::selectionChanged
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
QgsFields::exists
bool exists(int i) const
Returns if a field index is valid.
Definition: qgsfields.cpp:153
QgsAttributeForm::setMessageBar
void setMessageBar(QgsMessageBar *messageBar)
Sets the message bar to display feedback from the form in.
Definition: qgsattributeform.cpp:2400
QgsMessageBarItem
Represents an item shown within a QgsMessageBar widget.
Definition: qgsmessagebaritem.h:39
QgsFieldConstraints::ConstraintOrigin
ConstraintOrigin
Origin of constraints.
Definition: qgsfieldconstraints.h:55
QgsEditorWidgetWrapper::setConstraintResultVisible
void setConstraintResultVisible(bool constraintResultVisible)
Sets whether the constraint result is visible.
Definition: qgseditorwidgetwrapper.cpp:156
QgsAttributeEditorRelation
This element will load a relation editor onto the form.
Definition: qgsattributeeditorelement.h:335
QgsAttributeForm::closed
void closed()
Emitted when the user selects the close option from the form's button bar.
QgsFieldConstraints::ConstraintOriginLayer
@ ConstraintOriginLayer
Constraint was set by layer.
Definition: qgsfieldconstraints.h:58
QgsVectorLayerJoinInfo::hasUpsertOnEdit
bool hasUpsertOnEdit() const
Returns whether a feature created on the target layer has to impact the joined layer by creating a ne...
Definition: qgsvectorlayerjoininfo.h:108
QgsEditFormConfig::widgetConfig
QVariantMap widgetConfig(const QString &widgetName) const
Gets the configuration for the editor widget with the given name.
Definition: qgseditformconfig.cpp:53
QgsMessageBar::pushItem
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar, after hiding the currently visible one and putting it in a stack.
Definition: qgsmessagebar.cpp:274
QgsVectorLayer::selectedFeatureIds
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Definition: qgsvectorlayer.cpp:3441
QgsField::defaultValueDefinition
QgsDefaultValue defaultValueDefinition
Definition: qgsfield.h:61
QgsField::comment
QString comment
Definition: qgsfield.h:58
qgsattributeformeditorwidget.h
QgsAttributeEditorQmlElement::qmlCode
QString qmlCode() const
The QML code that will be represented within this widget.
Definition: qgsattributeeditorelement.cpp:222
QgsFields::size
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsAttributeEditorContext::MultiEditMode
@ MultiEditMode
Multi edit mode, for editing fields of multiple features at once.
Definition: qgsattributeeditorcontext.h:53
QgsFields::fieldOrigin
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field's origin (value from an enumeration)
Definition: qgsfields.cpp:189
QgsWidgetWrapper::setConfig
void setConfig(const QVariantMap &config)
Will set the config of this wrapper to the specified config.
Definition: qgswidgetwrapper.cpp:61
qgsvectordataprovider.h
QgsMessageBar
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
QgsEditorWidgetRegistry::findBest
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
Definition: qgseditorwidgetregistry.cpp:76
QgsAttributeMap
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QgsEditorWidgetWrapper::constraintStatusChanged
void constraintStatusChanged(const QString &constraint, const QString &desc, const QString &err, QgsEditorWidgetWrapper::ConstraintResult status)
Emit this signal when the constraint status changed.
QgsVectorLayerJoinInfo::isEditable
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
Definition: qgsvectorlayerjoininfo.h:95
qgsvectorlayerutils.h
QgsLogger::warning
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
QgsAttributeEditorRelation::relation
const QgsRelation & relation() const
Gets the id of the relation which shall be embedded.
Definition: qgsattributeeditorelement.h:404
QgsVectorLayerJoinBuffer::joinsWhereFieldIsId
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
Definition: qgsvectorlayerjoinbuffer.cpp:412
QgsAttributeFormEditorWidget::setConstraintResultVisible
void setConstraintResultVisible(bool editable)
Set the constraint result label visible or invisible according to the layer editable status.
Definition: qgsattributeformeditorwidget.cpp:114
QgsEditorWidgetWrapper::field
QgsField field() const
Access the field.
Definition: qgseditorwidgetwrapper.cpp:39
QgsAttributeEditorRelation::label
QString label() const
Determines the label of this element.
Definition: qgsattributeeditorelement.cpp:209
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:264
QgsDefaultValue::expression
Q_GADGET QString expression
Definition: qgsdefaultvalue.h:52
qgsmessagebar.h
QgsAttributeFormWidget::resetSearch
void resetSearch()
Resets the search/filter value of the widget.
Definition: qgsattributeformwidget.cpp:133
QgsVectorLayerJoinInfo
Defines left outer join from our vector layer to some other vector layer.
Definition: qgsvectorlayerjoininfo.h:34
QgsEditFormConfig::TabLayout
@ TabLayout
Use a layout with tabs and group boxes. Needs to be configured.
Definition: qgseditformconfig.h:48
QgsAttributeEditorRelation::forceSuppressFormPopup
bool forceSuppressFormPopup() const
Determines the force suppress form popup status.
Definition: qgsattributeeditorelement.cpp:189
QgsAttributeEditorContext::formMode
FormMode formMode() const
Returns the form mode.
Definition: qgsattributeeditorcontext.h:204
QgsAttributeForm::beforeSave
void beforeSave(bool &ok)
Will be emitted before the feature is saved.
QgsVectorLayer::SelectBehavior
SelectBehavior
Selection behavior.
Definition: qgsvectorlayer.h:412
qgsrelationmanager.h
QgsAttributeEditorRelation::nmRelationId
QVariant nmRelationId() const
Determines the relation id of the second relation involved in an N:M relation.
Definition: qgsattributeeditorelement.cpp:199
QgsFeatureIds
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
QgsAttributeFormEditorWidget::hasChanged
bool hasChanged() const
Returns true if the widget's value has been changed since it was initialized.
Definition: qgsattributeformeditorwidget.h:74
QgsFeature::attributes
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsEditorWidgetWrapper::constraintFailureReason
QString constraintFailureReason() const
Returns the reason why a constraint check has failed (or an empty string if constraint check was succ...
Definition: qgseditorwidgetwrapper.cpp:277
QgsAttributeFormWidget::SearchMode
@ SearchMode
Layer search/filter mode.
Definition: qgsattributeformwidget.h:49
QgsAttributeEditorRelation::visibleButtons
QgsAttributeEditorRelation::Buttons visibleButtons() const
Returns the buttons which are shown.
Definition: qgsattributeeditorelement.h:468
QgsFields::field
QgsField field(int fieldIdx) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:168
QgsScrollArea
A QScrollArea subclass with improved scrolling behavior.
Definition: qgsscrollarea.h:42
QgsExpressionContextScope
Single scope for storing variables and functions for use within a QgsExpressionContext.
Definition: qgsexpressioncontext.h:112
QgsAttributeForm::showButtonBox
void showButtonBox()
Shows the button box (OK/Cancel) and disables auto-commit.
Definition: qgsattributeform.cpp:108
QgsExpressionContext::appendScope
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Definition: qgsexpressioncontext.cpp:490
QgsVectorLayer::beforeAddingExpressionField
void beforeAddingExpressionField(const QString &fieldName)
Will be emitted, when an expression field is going to be added to this vector layer.
QgsHtmlWidgetWrapper::setHtmlCode
void setHtmlCode(const QString &htmlCode)
Sets the HTML code to htmlCode.
Definition: qgshtmlwidgetwrapper.cpp:74
qgsattributeformrelationeditorwidget.h
QgsAttributeForm::resetValues
void resetValues()
Sets all values to the values of the current feature.
Definition: qgsattributeform.cpp:800
QgsPythonRunner::eval
static bool eval(const QString &command, QString &result)
Eval a Python statement.
Definition: qgspythonrunner.cpp:42
QgsTabWidget
The QgsTabWidget class is the same as the QTabWidget but with additional methods to temporarily hide/...
Definition: qgstabwidget.h:30
QgsAttributeEditorElement::AeTypeQmlElement
@ AeTypeQmlElement
A QML element.
Definition: qgsattributeeditorelement.h:68
QgsVectorLayer::SetSelection
@ SetSelection
Set selection, removing any existing selection.
Definition: qgsvectorlayer.h:413
QgsAttributeEditorHtmlElement::htmlCode
QString htmlCode() const
The QML code that will be represented within this widget.
Definition: qgsattributeeditorelement.cpp:251
QgsEditorWidgetRegistry::create
QgsEditorWidgetWrapper * create(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QVariantMap &config, QWidget *editor, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
Create an attribute editor widget wrapper of a given type for a given field.
Definition: qgseditorwidgetregistry.cpp:97
QgsFields::allAttributesList
QgsAttributeList allAttributesList() const
Utility function to get list of attribute indexes.
Definition: qgsfields.cpp:371
QgsFeature::setAttribute
bool setAttribute(int field, const QVariant &attr)
Set an attribute's value by field index.
Definition: qgsfeature.cpp:213
qgsattributeforminterface.h
QgsRelationWidgetWrapper::setVisibleButtons
void setVisibleButtons(const QgsAttributeEditorRelation::Buttons &buttons)
Defines the buttons which are shown.
Definition: qgsrelationwidgetwrapper.cpp:220
QgsAttributeForm::~QgsAttributeForm
~QgsAttributeForm() override
Definition: qgsattributeform.cpp:93
QgsFieldConstraints::ConstraintOriginNotSet
@ ConstraintOriginNotSet
Constraint is not set.
Definition: qgsfieldconstraints.h:56
QgsEditorWidgetWrapper::valuesChanged
void valuesChanged(const QVariant &value, const QVariantList &additionalFieldValues=QVariantList())
Emit this signal, whenever the value changed.
qgseditorwidgetregistry.h
QgsAttributeForm::setMode
void setMode(QgsAttributeEditorContext::Mode mode)
Sets the current mode of the form.
Definition: qgsattributeform.cpp:131
QgsVectorLayer::editingStopped
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
QgsEditorWidgetWrapper::parentFormValueChanged
virtual void parentFormValueChanged(const QString &attribute, const QVariant &value)
Is called in embedded form widgets when an attribute value in the parent form has changed.
Definition: qgseditorwidgetwrapper.cpp:99
QgsAttributeForm::QgsAttributeForm
QgsAttributeForm(QgsVectorLayer *vl, const QgsFeature &feature=QgsFeature(), const QgsAttributeEditorContext &context=QgsAttributeEditorContext(), QWidget *parent=nullptr)
Definition: qgsattributeform.cpp:65
QgsVectorLayer::IntersectSelection
@ IntersectSelection
Modify current selection to include only select features which match.
Definition: qgsvectorlayer.h:415
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:374
QgsAttributeForm::FilterAnd
@ FilterAnd
Filter should be combined using "AND".
Definition: qgsattributeform.h:66
QgsEditFormConfig::CodeSourceEnvironment
@ CodeSourceEnvironment
Use the Python code available in the Python environment.
Definition: qgseditformconfig.h:98
QgsVectorLayer::attributeDisplayName
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else.
Definition: qgsvectorlayer.cpp:3139
c
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
Definition: porting_processing.dox:1
QgsVectorLayer::destroyEditCommand
void destroyEditCommand()
Destroy active command and reverts all changes in it.
Definition: qgsvectorlayer.cpp:3699
qgsVariantEqual
bool qgsVariantEqual(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether they are equal, two NULL values are always treated a...
Definition: qgis.cpp:265
QgsQmlWidgetWrapper::setQmlCode
void setQmlCode(const QString &qmlCode)
writes the qmlCode into a temporary file
Definition: qgsqmlwidgetwrapper.cpp:71
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsNetworkContentFetcherRegistry::localFile
QFile * localFile(const QString &filePathOrUrl)
Returns a QFile from a local file or to a temporary file previously fetched by the registry.
Definition: qgsnetworkcontentfetcherregistry.cpp:52
QgsVectorLayer::editFormConfig
QgsEditFormConfig editFormConfig
Definition: qgsvectorlayer.h:393
QgsAttributeEditorContainer
This is a container for attribute editors, used to group them visually in the attribute form if it is...
Definition: qgsattributeeditorelement.h:172
QgsVectorLayerJoinBuffer::joinedFeatureOf
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
Definition: qgsvectorlayerjoinbuffer.cpp:429
QgsAttributeForm::attributeChanged
Q_DECL_DEPRECATED void attributeChanged(const QString &attribute, const QVariant &value)
Notifies about changes of attributes, this signal is not emitted when the value is set back to the or...
QgsEditFormConfig::layout
EditorLayout layout() const
Gets the active layout style for the attribute editor for this layer.
Definition: qgseditformconfig.cpp:196
QgsRelation::isValid
bool isValid
Definition: qgsrelation.h:49
QgsAttributeForm::displayWarning
void displayWarning(const QString &message)
Displays a warning message in the form message bar.
Definition: qgsattributeform.cpp:626
QgsRelationWidgetWrapper::setShowLabel
void setShowLabel(bool showLabel)
Defines if a title label should be shown for this widget.
Definition: qgsrelationwidgetwrapper.cpp:134
QgsVectorLayerUtils::fieldIsEditable
static bool fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature)
Definition: qgsvectorlayerutils.cpp:861
qgssettings.h
QgsVectorLayerJoinInfo::joinLayer
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet)
Definition: qgsvectorlayerjoininfo.h:45
qgsfieldmodel.h
QgsPythonRunner::run
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement.
Definition: qgspythonrunner.cpp:28
QgsAttributeFormEditorWidget
A widget consisting of both an editor widget and additional widgets for controlling the behavior of t...
Definition: qgsattributeformeditorwidget.h:43
QgsPropertyCollection::hasProperty
bool hasProperty(int key) const override
Returns true if the collection contains a property with the specified key.
Definition: qgspropertycollection.cpp:203
QgsAttributeEditorElement::type
AttributeEditorType type() const
The type of this element.
Definition: qgsattributeeditorelement.h:100
QgsField::constraints
QgsFieldConstraints constraints
Definition: qgsfield.h:62
QgsEditFormConfig::dataDefinedFieldProperties
QgsPropertyCollection dataDefinedFieldProperties(const QString &fieldName) const
Returns data defined properties for fieldName.
Definition: qgseditformconfig.cpp:43
QgsVectorLayer::beforeModifiedCheck
void beforeModifiedCheck() const
Emitted when the layer is checked for modifications. Use for last-minute additions.
QgsRelation
Definition: qgsrelation.h:42
QgsAttributeForm::filterExpressionSet
void filterExpressionSet(const QString &expression, QgsAttributeForm::FilterType type)
Emitted when a filter expression is set using the form.
QgsOptional::data
T data() const
Access the payload data.
Definition: qgsoptional.h:119
QgsAttributes
A vector of attributes.
Definition: qgsattributes.h:58
QgsAttributeForm::widgetValueChanged
void widgetValueChanged(const QString &attribute, const QVariant &value, bool attributeChanged)
Notifies about changes of attributes.
QgsEditFormConfig::uiForm
QString uiForm() const
Returns the path or URL to the .ui form.
Definition: qgseditformconfig.cpp:210
QgsAttributeEditorElement::AeTypeHtmlElement
@ AeTypeHtmlElement
A HTML element.
Definition: qgsattributeeditorelement.h:69
QgsFeature::fields
QgsFields fields
Definition: qgsfeature.h:66
QgsField::displayName
QString displayName() const
Returns the name to use when displaying this field.
Definition: qgsfield.cpp:88
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsAttributeEditorContainer::visibilityExpression
QgsOptionalExpression visibilityExpression() const
The visibility expression is used in the attribute form to show or hide this container based on an ex...
Definition: qgsattributeeditorelement.cpp:30
QgsAttributeFormWidget::currentFilterExpression
virtual QString currentFilterExpression() const
Creates an expression matching the current search filter value and search properties represented in t...
Definition: qgsattributeformwidget.cpp:107
QgsAttributeForm::eventFilter
bool eventFilter(QObject *object, QEvent *event) override
Intercepts keypress on custom form (escape should not close it)
Definition: qgsattributeform.cpp:2244
QgsAttributeEditorElement::name
QString name() const
Returns the name of this element.
Definition: qgsattributeeditorelement.h:93
QgsVectorLayer::updatedFields
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsRelationWidgetWrapper::setForceSuppressFormPopup
void setForceSuppressFormPopup(bool forceSuppressFormPopup)
Sets force suppress form popup status to forceSuppressFormPopup for this widget and for the vectorLay...
Definition: qgsrelationwidgetwrapper.cpp:231
QgsVectorLayer::selectedFeatureCount
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Definition: qgsvectorlayer.cpp:3436
QgsAttributeEditorContext::AggregateSearchMode
@ AggregateSearchMode
Form is in aggregate search mode, show each widget in this mode.
Definition: qgsattributeeditorcontext.h:55
qgslogger.h
QgsFeature::setAttributes
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:129
QgsDefaultValue::applyOnUpdate
bool applyOnUpdate
Definition: qgsdefaultvalue.h:53
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
QgsEditorWidgetWrapper::updateConstraint
void updateConstraint(const QgsFeature &featureContext, QgsFieldConstraints::ConstraintOrigin constraintOrigin=QgsFieldConstraints::ConstraintOriginNotSet)
Update constraint.
Definition: qgseditorwidgetwrapper.cpp:168
QgsFields::at
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:105
QgsAttributeForm::featureSaved
void featureSaved(const QgsFeature &feature)
Emitted when a feature is changed or added.
QgsAttributeEditorContext::FixAttributeMode
@ FixAttributeMode
Fix feature mode, for modifying the feature attributes without saving. The updated feature is availab...
Definition: qgsattributeeditorcontext.h:52
QgsAttributeEditorContext
This class contains context information for attribute editor widgets.
Definition: qgsattributeeditorcontext.h:41
qgsrelationwidgetwrapper.h
QgsEditFormConfig::tabs
QList< QgsAttributeEditorElement * > tabs() const
Returns a list of tabs for EditorLayout::TabLayout obtained from the invisible root container.
Definition: qgseditformconfig.cpp:180
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:265
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:796
qgshtmlwidgetwrapper.h
QgsVectorLayer::addFeature
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
Definition: qgsvectorlayer.cpp:1011
QgsWidgetWrapper::setContext
void setContext(const QgsAttributeEditorContext &context)
Set the context in which this widget is shown.
Definition: qgswidgetwrapper.cpp:66
qgspythonrunner.h
QgsHtmlWidgetWrapper::reinitWidget
void reinitWidget()
Clears the content and makes new initialization.
Definition: qgshtmlwidgetwrapper.cpp:66
QgsAttributeForm::ReplaceFilter
@ ReplaceFilter
Filter should replace any existing filter.
Definition: qgsattributeform.h:65
QgsAttributeFormWidget
Base class for all widgets shown on a QgsAttributeForm.
Definition: qgsattributeformwidget.h:39
QgsEditFormConfig::initCode
QString initCode() const
Gets Python code for edit form initialization.
Definition: qgseditformconfig.cpp:284
QgsAttributeEditorContext::setAttributeFormMode
void setAttributeFormMode(const Mode &attributeFormMode)
Set attributeFormMode for the edited form.
Definition: qgsattributeeditorcontext.h:270
QgsApplication::networkContentFetcherRegistry
static QgsNetworkContentFetcherRegistry * networkContentFetcherRegistry()
Returns the application's network content registry used for fetching temporary files during QGIS sess...
Definition: qgsapplication.cpp:2193
qgsproject.h
QgsFields::indexFromName
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
QgsField::type
QVariant::Type type
Definition: qgsfield.h:57
QgsMessageBar::pushMessage
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::Info, int duration=5)
A convenience method for pushing a message with the specified text to the bar.
Definition: qgsmessagebar.cpp:379
QgsFields::OriginJoin
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
QgsAttributeEditorElement::AeTypeField
@ AeTypeField
A field.
Definition: qgsattributeeditorelement.h:65
QgsAttributeEditorContext::attributeFormModeString
QString attributeFormModeString() const
Returns given attributeFormMode as string.
Definition: qgsattributeeditorcontext.h:276
QgsVectorLayer::joinBuffer
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
Definition: qgsvectorlayer.h:652
QgsEditorWidgetWrapper::isBlockingCommit
bool isBlockingCommit() const
Returns true if the widget is preventing the feature from being committed.
Definition: qgseditorwidgetwrapper.cpp:271
qgstexteditwrapper.h
QgsHtmlWidgetWrapper
Wraps a QQuickWidget to display HTML code.
Definition: qgshtmlwidgetwrapper.h:30
QgsFeatureId
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QgsAttributeEditorContext::IdentifyMode
@ IdentifyMode
Identify the feature.
Definition: qgsattributeeditorcontext.h:56
QgsExpressionContext::setFeature
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Definition: qgsexpressioncontext.cpp:521
QgsAttributeEditorHtmlElement
An attribute editor widget that will represent arbitrary HTML code.
Definition: qgsattributeeditorelement.h:572
QgsAttributeEditorContainer::backgroundColor
QColor backgroundColor() const
backgroundColor
Definition: qgsattributeeditorelement.cpp:43
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50
QgsEditFormConfig::UiFileLayout
@ UiFileLayout
Load a .ui file for the layout. Needs to be configured.
Definition: qgseditformconfig.h:49