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