QGIS API Documentation
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 
18 #include "qgsattributeeditor.h"
22 #include "qgsproject.h"
23 #include "qgspythonrunner.h"
25 #include "qgsvectordataprovider.h"
27 #include "qgsmessagebar.h"
28 #include "qgsmessagebaritem.h"
29 
30 #include <QDir>
31 #include <QTextStream>
32 #include <QFileInfo>
33 #include <QFile>
34 #include <QFormLayout>
35 #include <QGridLayout>
36 #include <QGroupBox>
37 #include <QKeyEvent>
38 #include <QLabel>
39 #include <QPushButton>
40 #include <QScrollArea>
41 #include <QTabWidget>
42 #include <QUiLoader>
43 #include <QMessageBox>
44 #include <QSettings>
45 
46 int QgsAttributeForm::sFormCounter = 0;
47 
49  : QWidget( parent )
50  , mLayer( vl )
51  , mMessageBar( nullptr )
52  , mMultiEditUnsavedMessageBarItem( nullptr )
53  , mMultiEditMessageBarItem( nullptr )
54  , mContext( context )
55  , mButtonBox( nullptr )
56  , mFormNr( sFormCounter++ )
57  , mIsSaving( false )
58  , mPreventFeatureRefresh( false )
59  , mIsSettingFeature( false )
60  , mIsSettingMultiEditFeatures( false )
61  , mUnsavedMultiEditChanges( false )
62  , mEditCommandMessage( tr( "Attributes changed" ) )
63  , mMode( SingleEditMode )
64 {
65  init();
66  initPython();
67  setFeature( feature );
68 
69  // Using attributeAdded() attributeDeleted() are not emitted on all fields changes (e.g. layer fields changed,
70  // joined fields changed) -> use updatedFields() instead
71 #if 0
72  connect( vl, SIGNAL( attributeAdded( int ) ), this, SLOT( onAttributeAdded( int ) ) );
73  connect( vl, SIGNAL( attributeDeleted( int ) ), this, SLOT( onAttributeDeleted( int ) ) );
74 #endif
75  connect( vl, SIGNAL( updatedFields() ), this, SLOT( onUpdatedFields() ) );
76  connect( vl, SIGNAL( beforeAddingExpressionField( QString ) ), this, SLOT( preventFeatureRefresh() ) );
77  connect( vl, SIGNAL( beforeRemovingExpressionField( int ) ), this, SLOT( preventFeatureRefresh() ) );
78  connect( vl, SIGNAL( selectionChanged() ), this, SLOT( layerSelectionChanged() ) );
79 }
80 
82 {
83  cleanPython();
84  qDeleteAll( mInterfaces );
85 }
86 
88 {
89  mButtonBox->hide();
90 
91  // Make sure that changes are taken into account if somebody tries to figure out if there have been some
92  if ( mMode == SingleEditMode )
93  connect( mLayer, SIGNAL( beforeModifiedCheck() ), this, SLOT( save() ) );
94 }
95 
97 {
98  mButtonBox->show();
99 
100  disconnect( mLayer, SIGNAL( beforeModifiedCheck() ), this, SLOT( save() ) );
101 }
102 
104 {
105  disconnect( mButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
106  disconnect( mButtonBox, SIGNAL( rejected() ), this, SLOT( resetValues() ) );
107 }
108 
110 {
111  mInterfaces.append( iface );
112 }
113 
115 {
116  return mFeature.isValid() && mLayer->isEditable();
117 }
118 
120 {
121  if ( mode == mMode )
122  return;
123 
124  if ( mMode == MultiEditMode )
125  {
126  //switching out of multi edit mode triggers a save
127  if ( mUnsavedMultiEditChanges )
128  {
129  // prompt for save
130  int res = QMessageBox::information( this, tr( "Multiedit attributes" ),
131  tr( "Apply changes to edited features?" ), QMessageBox::Yes | QMessageBox::No );
132  if ( res == QMessageBox::Yes )
133  {
134  save();
135  }
136  }
137  clearMultiEditMessages();
138  }
139  mUnsavedMultiEditChanges = false;
140 
141  mMode = mode;
142 
143  if ( mButtonBox->isVisible() && mMode == SingleEditMode )
144  {
145  connect( mLayer, SIGNAL( beforeModifiedCheck() ), this, SLOT( save() ) );
146  }
147  else
148  {
149  disconnect( mLayer, SIGNAL( beforeModifiedCheck() ), this, SLOT( save() ) );
150  }
151 
152  //update all form editor widget modes to match
153  Q_FOREACH ( QgsAttributeFormEditorWidget* w, findChildren< QgsAttributeFormEditorWidget* >() )
154  {
155  switch ( mode )
156  {
159  break;
160 
163  break;
164 
167  break;
168 
169 #if 0
170  case QgsAttributeForm::SearchMode:
171  w->setMode( QgsAttributeFormEditorWidget::SearchMode );
172  break;
173 #endif
174  }
175  }
176 
177  switch ( mode )
178  {
180  setFeature( mFeature );
181  break;
182 
184  synchronizeEnabledState();
185  break;
186 
188  resetMultiEdit( false );
189  synchronizeEnabledState();
190  break;
191  }
192 
193 }
194 
195 void QgsAttributeForm::setIsAddDialog( bool isAddDialog )
196 {
197  setMode( isAddDialog ? AddFeatureMode : SingleEditMode );
198 }
199 
200 void QgsAttributeForm::changeAttribute( const QString& field, const QVariant& value )
201 {
202  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
203  {
204  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
205  if ( eww && eww->field().name() == field )
206  {
207  eww->setValue( value );
208  }
209  }
210 }
211 
213 {
214  mIsSettingFeature = true;
215  mFeature = feature;
216 
217  switch ( mMode )
218  {
219  case SingleEditMode:
220  case AddFeatureMode:
221  {
222  resetValues();
223 
224  synchronizeEnabledState();
225 
226  Q_FOREACH ( QgsAttributeFormInterface* iface, mInterfaces )
227  {
228  iface->featureChanged();
229  }
230  break;
231  }
232  case MultiEditMode:
233  {
234  //ignore setFeature
235  break;
236  }
237  }
238  mIsSettingFeature = false;
239 }
240 
241 bool QgsAttributeForm::saveEdits()
242 {
243  bool success = true;
244  bool changedLayer = false;
245 
246  QgsFeature updatedFeature = QgsFeature( mFeature );
247 
248  if ( mFeature.isValid() || mMode == AddFeatureMode )
249  {
250  bool doUpdate = false;
251 
252  // An add dialog should perform an action by default
253  // and not only if attributes have "changed"
254  if ( mMode == AddFeatureMode )
255  doUpdate = true;
256 
257  QgsAttributes src = mFeature.attributes();
258  QgsAttributes dst = mFeature.attributes();
259 
260  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
261  {
262  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
263  if ( eww )
264  {
265  QVariant dstVar = dst.at( eww->fieldIdx() );
266  QVariant srcVar = eww->value();
267  // need to check dstVar.isNull() != srcVar.isNull()
268  // otherwise if dstVar=NULL and scrVar=0, then dstVar = srcVar
269  if (( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() && !mLayer->editFormConfig()->readOnly( eww->fieldIdx() ) )
270  {
271  dst[eww->fieldIdx()] = srcVar;
272 
273  doUpdate = true;
274  }
275  }
276  }
277 
278  updatedFeature.setAttributes( dst );
279 
280  Q_FOREACH ( QgsAttributeFormInterface* iface, mInterfaces )
281  {
282  if ( !iface->acceptChanges( updatedFeature ) )
283  {
284  doUpdate = false;
285  }
286  }
287 
288  if ( doUpdate )
289  {
290  if ( mMode == AddFeatureMode )
291  {
292  mFeature.setValid( true );
293  mLayer->beginEditCommand( mEditCommandMessage );
294  bool res = mLayer->addFeature( updatedFeature );
295  if ( res )
296  {
297  mFeature.setAttributes( updatedFeature.attributes() );
298  mLayer->endEditCommand();
300  changedLayer = true;
301  }
302  else
303  mLayer->destroyEditCommand();
304  }
305  else
306  {
307  mLayer->beginEditCommand( mEditCommandMessage );
308 
309  int n = 0;
310  for ( int i = 0; i < dst.count(); ++i )
311  {
312  if (( dst.at( i ) == src.at( i ) && dst.at( i ).isNull() == src.at( i ).isNull() ) // If field is not changed...
313  || !dst.at( i ).isValid() // or the widget returns invalid (== do not change)
314  || mLayer->editFormConfig()->readOnly( i ) ) // or the field cannot be edited ...
315  {
316  continue;
317  }
318 
319  QgsDebugMsg( QString( "Updating field %1" ).arg( i ) );
320  QgsDebugMsg( QString( "dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
321  .arg( dst.at( i ).toString(), dst.at( i ).typeName() ).arg( dst.at( i ).isNull() ).arg( dst.at( i ).isValid() ) );
322  QgsDebugMsg( QString( "src:'%1' (type:%2, isNull:%3, isValid:%4)" )
323  .arg( src.at( i ).toString(), src.at( i ).typeName() ).arg( src.at( i ).isNull() ).arg( src.at( i ).isValid() ) );
324 
325  success &= mLayer->changeAttributeValue( mFeature.id(), i, dst.at( i ), src.at( i ) );
326  n++;
327  }
328 
329  if ( success && n > 0 )
330  {
331  mLayer->endEditCommand();
332  mFeature.setAttributes( dst );
333  changedLayer = true;
334  }
335  else
336  {
337  mLayer->destroyEditCommand();
338  }
339  }
340  }
341  }
342 
343  emit featureSaved( updatedFeature );
344 
345  // [MD] Refresh canvas only when absolutely necessary - it interferes with other stuff (#11361).
346  // This code should be revisited - and the signals should be fired (+ layer repainted)
347  // only when actually doing any changes. I am unsure if it is actually a good idea
348  // to call save() whenever some code asks for vector layer's modified status
349  // (which is the case when attribute table is open)
350  if ( changedLayer )
351  mLayer->triggerRepaint();
352 
353  return success;
354 }
355 
356 void QgsAttributeForm::resetMultiEdit( bool promptToSave )
357 {
358  if ( promptToSave )
359  save();
360 
361  mUnsavedMultiEditChanges = false;
363 }
364 
365 void QgsAttributeForm::multiEditMessageClicked( const QString& link )
366 {
367  clearMultiEditMessages();
368  resetMultiEdit( link == "#apply" );
369 }
370 
371 bool QgsAttributeForm::saveMultiEdits()
372 {
373  //find changed attributes
374  QgsAttributeMap newAttributeValues;
376  for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
377  {
379  if ( !w->hasChanged() )
380  continue;
381 
382  if ( !w->currentValue().isValid() // if the widget returns invalid (== do not change)
383  || mLayer->editFormConfig()->readOnly( wIt.key() ) ) // or the field cannot be edited ...
384  {
385  continue;
386  }
387 
388  // let editor know we've accepted the changes
389  w->changesCommitted();
390 
391  newAttributeValues.insert( wIt.key(), w->currentValue() );
392  }
393 
394  if ( newAttributeValues.isEmpty() )
395  {
396  //nothing to change
397  return true;
398  }
399 
400 #if 0
401  // prompt for save
402  int res = QMessageBox::information( this, tr( "Multiedit attributes" ),
403  tr( "Edits will be applied to all selected features" ), QMessageBox::Ok | QMessageBox::Cancel );
404  if ( res != QMessageBox::Ok )
405  {
406  resetMultiEdit();
407  return false;
408  }
409 #endif
410 
411  mLayer->beginEditCommand( tr( "Updated multiple feature attributes" ) );
412 
413  bool success = true;
414 
415  Q_FOREACH ( QgsFeatureId fid, mMultiEditFeatureIds )
416  {
417  QgsAttributeMap::const_iterator aIt = newAttributeValues.constBegin();
418  for ( ; aIt != newAttributeValues.constEnd(); ++aIt )
419  {
420  success &= mLayer->changeAttributeValue( fid, aIt.key(), aIt.value() );
421  }
422  }
423 
424  clearMultiEditMessages();
425  if ( success )
426  {
427  mLayer->endEditCommand();
428  mLayer->triggerRepaint();
429  mMultiEditMessageBarItem = new QgsMessageBarItem( tr( "Attribute changes for multiple features applied" ), QgsMessageBar::SUCCESS, messageTimeout() );
430  }
431  else
432  {
433  mLayer->destroyEditCommand();
434  mMultiEditMessageBarItem = new QgsMessageBarItem( tr( "Changes could not be applied" ), QgsMessageBar::WARNING, messageTimeout() );
435  }
436 
437  if ( !mButtonBox->isVisible() )
438  mMessageBar->pushItem( mMultiEditMessageBarItem );
439  return success;
440 }
441 
443 {
444  if ( mIsSaving )
445  return true;
446 
447  mIsSaving = true;
448 
449  bool success = true;
450 
451  emit beforeSave( success );
452 
453  // Somebody wants to prevent this form from saving
454  if ( !success )
455  return false;
456 
457  switch ( mMode )
458  {
459  case SingleEditMode:
460  case AddFeatureMode:
461  success = saveEdits();
462  break;
463 
464  case MultiEditMode:
465  success = saveMultiEdits();
466  break;
467  }
468 
469  mIsSaving = false;
470  mUnsavedMultiEditChanges = false;
471 
472  return success;
473 }
474 
476 {
477  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
478  {
479  ww->setFeature( mFeature );
480  }
481 }
482 
483 void QgsAttributeForm::clearMultiEditMessages()
484 {
485  if ( mMultiEditUnsavedMessageBarItem )
486  {
487  if ( !mButtonBox->isVisible() )
488  mMessageBar->popWidget( mMultiEditUnsavedMessageBarItem );
489  mMultiEditUnsavedMessageBarItem = nullptr;
490  }
491  if ( mMultiEditMessageBarItem )
492  {
493  if ( !mButtonBox->isVisible() )
494  mMessageBar->popWidget( mMultiEditMessageBarItem );
495  mMultiEditMessageBarItem = nullptr;
496  }
497 }
498 
499 void QgsAttributeForm::onAttributeChanged( const QVariant& value )
500 {
501  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( sender() );
502 
503  Q_ASSERT( eww );
504 
505  switch ( mMode )
506  {
507  case SingleEditMode:
508  case AddFeatureMode:
509  {
510  // don't emit signal if it was triggered by a feature change
511  if ( !mIsSettingFeature )
512  {
513  emit attributeChanged( eww->field().name(), value );
514  }
515  break;
516  }
517  case MultiEditMode:
518  {
519  if ( !mIsSettingMultiEditFeatures )
520  {
521  mUnsavedMultiEditChanges = true;
522 
523  QLabel *msgLabel = new QLabel( tr( "Unsaved multiedit changes: <a href=\"#apply\">apply changes</a> or <a href=\"#reset\">reset changes</a>." ), mMessageBar );
524  msgLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
525  msgLabel->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
526  connect( msgLabel, SIGNAL( linkActivated( QString ) ), this, SLOT( multiEditMessageClicked( QString ) ) );
527  clearMultiEditMessages();
528 
529  mMultiEditUnsavedMessageBarItem = new QgsMessageBarItem( msgLabel, QgsMessageBar::WARNING );
530  if ( !mButtonBox->isVisible() )
531  mMessageBar->pushItem( mMultiEditUnsavedMessageBarItem );
532  }
533  break;
534  }
535  }
536 }
537 
538 void QgsAttributeForm::onAttributeAdded( int idx )
539 {
540  mPreventFeatureRefresh = false;
541  if ( mFeature.isValid() )
542  {
543  QgsAttributes attrs = mFeature.attributes();
544  attrs.insert( idx, QVariant( layer()->fields().at( idx ).type() ) );
545  mFeature.setFields( layer()->fields() );
546  mFeature.setAttributes( attrs );
547  }
548  init();
549  setFeature( mFeature );
550 }
551 
552 void QgsAttributeForm::onAttributeDeleted( int idx )
553 {
554  mPreventFeatureRefresh = false;
555  if ( mFeature.isValid() )
556  {
557  QgsAttributes attrs = mFeature.attributes();
558  attrs.remove( idx );
559  mFeature.setFields( layer()->fields() );
560  mFeature.setAttributes( attrs );
561  }
562  init();
563  setFeature( mFeature );
564 }
565 
566 void QgsAttributeForm::onUpdatedFields()
567 {
568  mPreventFeatureRefresh = false;
569  if ( mFeature.isValid() )
570  {
571  QgsAttributes attrs( layer()->fields().size() );
572  for ( int i = 0; i < layer()->fields().size(); i++ )
573  {
574  int idx = mFeature.fields()->indexFromName( layer()->fields().at( i ).name() );
575  if ( idx != -1 )
576  {
577  attrs[i] = mFeature.attributes().at( idx );
578  if ( mFeature.attributes().at( idx ).type() != layer()->fields().at( i ).type() )
579  {
580  attrs[i].convert( layer()->fields().at( i ).type() );
581  }
582  }
583  else
584  {
585  attrs[i] = QVariant( layer()->fields().at( i ).type() );
586  }
587  }
588  mFeature.setFields( layer()->fields() );
589  mFeature.setAttributes( attrs );
590  }
591  init();
592  setFeature( mFeature );
593 }
594 
595 void QgsAttributeForm::preventFeatureRefresh()
596 {
597  mPreventFeatureRefresh = true;
598 }
599 
601 {
602  if ( mPreventFeatureRefresh || mLayer->isEditable() || !mFeature.isValid() )
603  return;
604 
605  // reload feature if layer changed although not editable
606  // (datasource probably changed bypassing QgsVectorLayer)
607  if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( mFeature ) )
608  return;
609 
610  init();
611  setFeature( mFeature );
612 }
613 
614 void QgsAttributeForm::synchronizeEnabledState()
615 {
616  bool isEditable = ( mFeature.isValid()
617  || mMode == AddFeatureMode
618  || mMode == MultiEditMode ) && mLayer->isEditable();
619 
620  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
621  {
622  bool fieldEditable = true;
623  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
624  if ( eww )
625  {
626  fieldEditable = !mLayer->editFormConfig()->readOnly( eww->fieldIdx() ) &&
628  FID_IS_NEW( mFeature.id() ) );
629  }
630  ww->setEnabled( isEditable && fieldEditable );
631  }
632 
633  QPushButton* okButton = mButtonBox->button( QDialogButtonBox::Ok );
634  if ( okButton )
635  okButton->setEnabled( isEditable );
636 }
637 
638 void QgsAttributeForm::init()
639 {
640  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
641 
642  // Cleanup of any previously shown widget, we start from scratch
643  QWidget* formWidget = nullptr;
644 
645  bool buttonBoxVisible = true;
646  // Cleanup button box but preserve visibility
647  if ( mButtonBox )
648  {
649  buttonBoxVisible = mButtonBox->isVisible();
650  delete mButtonBox;
651  mButtonBox = nullptr;
652  }
653 
654  qDeleteAll( mWidgets );
655  mWidgets.clear();
656 
657  while ( QWidget* w = this->findChild<QWidget*>() )
658  {
659  delete w;
660  }
661  delete layout();
662 
663  // Get a layout
664  QGridLayout* layout = new QGridLayout();
665  setLayout( layout );
666 
667  mFormEditorWidgets.clear();
668 
669  // a bar to warn the user with non-blocking messages
670  setContentsMargins( 0, 0, 0, 0 );
671  mMessageBar = new QgsMessageBar( this );
672  mMessageBar->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
673  layout->addWidget( mMessageBar, 0, 0, 1, 1 );
674 
675  // Try to load Ui-File for layout
676  if ( mLayer->editFormConfig()->layout() == QgsEditFormConfig::UiFileLayout && !mLayer->editFormConfig()->uiForm().isEmpty() )
677  {
678  QFile file( mLayer->editFormConfig()->uiForm() );
679 
680  if ( file.open( QFile::ReadOnly ) )
681  {
682  QUiLoader loader;
683 
684  QFileInfo fi( mLayer->editFormConfig()->uiForm() );
685  loader.setWorkingDirectory( fi.dir() );
686  formWidget = loader.load( &file, this );
687  formWidget->setWindowFlags( Qt::Widget );
688  layout->addWidget( formWidget );
689  formWidget->show();
690  file.close();
691  mButtonBox = findChild<QDialogButtonBox*>();
692  createWrappers();
693 
694  formWidget->installEventFilter( this );
695  }
696  }
697 
698  // Tab layout
699  if ( !formWidget && mLayer->editFormConfig()->layout() == QgsEditFormConfig::TabLayout )
700  {
701  QTabWidget* tabWidget = new QTabWidget();
702  layout->addWidget( tabWidget );
703 
704  Q_FOREACH ( QgsAttributeEditorElement* widgDef, mLayer->editFormConfig()->tabs() )
705  {
706  QWidget* tabPage = new QWidget( tabWidget );
707 
708  tabWidget->addTab( tabPage, widgDef->name() );
709  QGridLayout* tabPageLayout = new QGridLayout();
710  tabPage->setLayout( tabPageLayout );
711 
713  {
714  QgsAttributeEditorContainer* containerDef = dynamic_cast<QgsAttributeEditorContainer*>( widgDef );
715  if ( !containerDef )
716  continue;
717 
718  containerDef->setIsGroupBox( false ); // Toplevel widgets are tabs not groupboxes
719  WidgetInfo widgetInfo = createWidgetFromDef( widgDef, tabPage, mLayer, mContext );
720  tabPageLayout->addWidget( widgetInfo.widget );
721  }
722  else
723  {
724  QgsDebugMsg( "No support for fields in attribute editor on top level" );
725  }
726  }
727  formWidget = tabWidget;
728  }
729 
730  // Autogenerate Layout
731  // If there is still no layout loaded (defined as autogenerate or other methods failed)
732  if ( !formWidget )
733  {
734  formWidget = new QWidget( this );
735  QGridLayout* gridLayout = new QGridLayout( formWidget );
736  formWidget->setLayout( gridLayout );
737 
738  // put the form into a scroll area to nicely handle cases with lots of attributes
739 
740  QScrollArea* scrollArea = new QScrollArea( this );
741  scrollArea->setWidget( formWidget );
742  scrollArea->setWidgetResizable( true );
743  scrollArea->setFrameShape( QFrame::NoFrame );
744  scrollArea->setFrameShadow( QFrame::Plain );
745  scrollArea->setFocusProxy( this );
746  layout->addWidget( scrollArea );
747 
748  int row = 0;
749  Q_FOREACH ( const QgsField& field, mLayer->fields().toList() )
750  {
751  int idx = mLayer->fieldNameIndex( field.name() );
752  if ( idx < 0 )
753  continue;
754 
755  //show attribute alias if available
756  QString fieldName = mLayer->attributeDisplayName( idx );
757 
758  const QString widgetType = mLayer->editFormConfig()->widgetType( idx );
759 
760  if ( widgetType == "Hidden" )
761  continue;
762 
763  const QgsEditorWidgetConfig widgetConfig = mLayer->editFormConfig()->widgetConfig( idx );
764  bool labelOnTop = mLayer->editFormConfig()->labelOnTop( idx );
765 
766  // This will also create the widget
767  QWidget *l = new QLabel( fieldName );
768  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, idx, widgetConfig, nullptr, this, mContext );
769 
770  QWidget* w = nullptr;
771  if ( eww )
772  {
773  w = new QgsAttributeFormEditorWidget( eww, this );
774  mFormEditorWidgets.insert( idx, static_cast< QgsAttributeFormEditorWidget* >( w ) );
775  }
776  else
777  {
778  w = new QLabel( QString( "<p style=\"color: red; font-style: italic;\">Failed to create widget with type '%1'</p>" ).arg( widgetType ) );
779  }
780 
781  if ( eww )
782  addWidgetWrapper( eww );
783 
784  if ( labelOnTop )
785  {
786  gridLayout->addWidget( l, row++, 0, 1, 2 );
787  gridLayout->addWidget( w, row++, 0, 1, 2 );
788  }
789  else
790  {
791  gridLayout->addWidget( l, row, 0 );
792  gridLayout->addWidget( w, row++, 1 );
793  }
794  }
795 
796  Q_FOREACH ( const QgsRelation& rel, QgsProject::instance()->relationManager()->referencedRelations( mLayer ) )
797  {
798  QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, rel, nullptr, this );
799  QgsEditorWidgetConfig cfg = mLayer->editFormConfig()->widgetConfig( rel.id() );
800  rww->setConfig( cfg );
801  rww->setContext( mContext );
802  gridLayout->addWidget( rww->widget(), row++, 0, 1, 2 );
803  mWidgets.append( rww );
804  }
805 
806  if ( QgsProject::instance()->relationManager()->referencedRelations( mLayer ).isEmpty() )
807  {
808  QSpacerItem *spacerItem = new QSpacerItem( 20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding );
809  gridLayout->addItem( spacerItem, row++, 0 );
810  }
811  }
812 
813  if ( !mButtonBox )
814  {
815  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
816  mButtonBox->setObjectName( "buttonBox" );
817  layout->addWidget( mButtonBox );
818  }
819 
820  mButtonBox->setVisible( buttonBoxVisible );
821 
822  connectWrappers();
823 
824  connect( mButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
825  connect( mButtonBox, SIGNAL( rejected() ), this, SLOT( resetValues() ) );
826 
827  connect( mLayer, SIGNAL( editingStarted() ), this, SLOT( synchronizeEnabledState() ) );
828  connect( mLayer, SIGNAL( editingStopped() ), this, SLOT( synchronizeEnabledState() ) );
829 
830  Q_FOREACH ( QgsAttributeFormInterface* iface, mInterfaces )
831  {
832  iface->initForm();
833  }
835 }
836 
837 void QgsAttributeForm::cleanPython()
838 {
839  if ( !mPyFormVarName.isNull() )
840  {
841  QString expr = QString( "if locals().has_key('%1'): del %1\n" ).arg( mPyFormVarName );
842  QgsPythonRunner::run( expr );
843  }
844 }
845 
846 void QgsAttributeForm::initPython()
847 {
848  cleanPython();
849 
850  // Init Python, if init function is not empty and the combo indicates
851  // the source for the function code
852  if ( !mLayer->editFormConfig()->initFunction().isEmpty()
854  {
855 
856  QString initFunction = mLayer->editFormConfig()->initFunction();
857  QString initFilePath = mLayer->editFormConfig()->initFilePath();
858  QString initCode;
859 
860  switch ( mLayer->editFormConfig()->initCodeSource() )
861  {
863  if ( ! initFilePath.isEmpty() )
864  {
865  QFile inputFile( initFilePath );
866 
867  if ( inputFile.open( QFile::ReadOnly ) )
868  {
869  // Read it into a string
870  QTextStream inf( &inputFile );
871  initCode = inf.readAll();
872  inputFile.close();
873  }
874  else // The file couldn't be opened
875  {
876  QgsLogger::warning( QString( "The external python file path %1 could not be opened!" ).arg( initFilePath ) );
877  }
878  }
879  else
880  {
881  QgsLogger::warning( QString( "The external python file path is empty!" ) );
882  }
883  break;
884 
886  initCode = mLayer->editFormConfig()->initCode();
887  if ( initCode.isEmpty() )
888  {
889  QgsLogger::warning( QString( "The python code provided in the dialog is empty!" ) );
890  }
891  break;
892 
895  default:
896  // Nothing to do: the function code should be already in the environment
897  break;
898  }
899 
900  // If we have a function code, run it
901  if ( ! initCode.isEmpty() )
902  {
903  QgsPythonRunner::run( initCode );
904  }
905 
906  QgsPythonRunner::run( "import inspect" );
907  QString numArgs;
908 
909  // Check for eval result
910  if ( QgsPythonRunner::eval( QString( "len(inspect.getargspec(%1)[0])" ).arg( initFunction ), numArgs ) )
911  {
912  static int sFormId = 0;
913  mPyFormVarName = QString( "_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
914 
915  QString form = QString( "%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
916  .arg( mPyFormVarName )
917  .arg(( unsigned long ) this );
918 
919  QgsPythonRunner::run( form );
920 
921  QgsDebugMsg( QString( "running featureForm init: %1" ).arg( mPyFormVarName ) );
922 
923  // Legacy
924  if ( numArgs == "3" )
925  {
926  addInterface( new QgsAttributeFormLegacyInterface( initFunction, mPyFormVarName, this ) );
927  }
928  else
929  {
930  // If we get here, it means that the function doesn't accept three arguments
931  QMessageBox msgBox;
932  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 ) );
933  msgBox.exec();
934 #if 0
935  QString expr = QString( "%1(%2)" )
936  .arg( mLayer->editFormInit() )
937  .arg( mPyFormVarName );
938  QgsAttributeFormInterface* iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface*>( expr, "QgsAttributeFormInterface" );
939  if ( iface )
940  addInterface( iface );
941 #endif
942  }
943  }
944  else
945  {
946  // If we get here, it means that inspect couldn't find the function
947  QMessageBox msgBox;
948  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 ) );
949  msgBox.exec();
950  }
951  }
952 }
953 
954 QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAttributeEditorElement* widgetDef, QWidget* parent, QgsVectorLayer* vl, QgsAttributeEditorContext& context )
955 {
956  WidgetInfo newWidgetInfo;
957 
958  switch ( widgetDef->type() )
959  {
961  {
962  const QgsAttributeEditorField* fieldDef = dynamic_cast<const QgsAttributeEditorField*>( widgetDef );
963  if ( !fieldDef )
964  break;
965 
966  int fldIdx = vl->fieldNameIndex( fieldDef->name() );
967  if ( fldIdx < vl->fields().count() && fldIdx >= 0 )
968  {
969  const QString widgetType = mLayer->editFormConfig()->widgetType( fldIdx );
970  const QgsEditorWidgetConfig widgetConfig = mLayer->editFormConfig()->widgetConfig( fldIdx );
971 
972  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, fldIdx, widgetConfig, nullptr, this, mContext );
973 
974  QWidget* w = new QgsAttributeFormEditorWidget( eww, this );
975  mFormEditorWidgets.insert( fldIdx, static_cast< QgsAttributeFormEditorWidget* >( w ) );
976 
977  newWidgetInfo.widget = w;
978  addWidgetWrapper( eww );
979 
980  newWidgetInfo.widget->setObjectName( mLayer->fields().at( fldIdx ).name() );
981  }
982 
983  newWidgetInfo.labelOnTop = mLayer->editFormConfig()->labelOnTop( fieldDef->idx() );
984  newWidgetInfo.labelText = mLayer->attributeDisplayName( fieldDef->idx() );
985 
986  break;
987  }
988 
990  {
991  const QgsAttributeEditorRelation* relDef = dynamic_cast<const QgsAttributeEditorRelation*>( widgetDef );
992 
993  QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, relDef->relation(), nullptr, this );
994  QgsEditorWidgetConfig cfg = mLayer->editFormConfig()->widgetConfig( relDef->relation().id() );
995  rww->setConfig( cfg );
996  rww->setContext( context );
997  newWidgetInfo.widget = rww->widget();
998  mWidgets.append( rww );
999  newWidgetInfo.labelText = QString::null;
1000  newWidgetInfo.labelOnTop = true;
1001  break;
1002  }
1003 
1005  {
1006  const QgsAttributeEditorContainer* container = dynamic_cast<const QgsAttributeEditorContainer*>( widgetDef );
1007  if ( !container )
1008  break;
1009 
1010  int columnCount = container->columnCount();
1011 
1012  if ( columnCount <= 0 )
1013  columnCount = 1;
1014 
1015  QWidget* myContainer;
1016  if ( container->isGroupBox() )
1017  {
1018  QGroupBox* groupBox = new QGroupBox( parent );
1019  groupBox->setTitle( container->name() );
1020  myContainer = groupBox;
1021  newWidgetInfo.widget = myContainer;
1022  }
1023  else
1024  {
1025  QScrollArea *scrollArea = new QScrollArea( parent );
1026 
1027  myContainer = new QWidget( scrollArea );
1028 
1029  scrollArea->setWidget( myContainer );
1030  scrollArea->setWidgetResizable( true );
1031  scrollArea->setFrameShape( QFrame::NoFrame );
1032 
1033  newWidgetInfo.widget = scrollArea;
1034  }
1035 
1036  QGridLayout* gbLayout = new QGridLayout();
1037  myContainer->setLayout( gbLayout );
1038 
1039  int row = 0;
1040  int column = 0;
1041 
1043 
1044  Q_FOREACH ( QgsAttributeEditorElement* childDef, children )
1045  {
1046  WidgetInfo widgetInfo = createWidgetFromDef( childDef, myContainer, vl, context );
1047 
1048  if ( widgetInfo.labelText.isNull() )
1049  {
1050  gbLayout->addWidget( widgetInfo.widget, row, column, 1, 2 );
1051  column += 2;
1052  }
1053  else
1054  {
1055  QLabel* mypLabel = new QLabel( widgetInfo.labelText );
1056  if ( columnCount > 1 && !widgetInfo.labelOnTop )
1057  {
1058  mypLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
1059  }
1060 
1061  if ( widgetInfo.labelOnTop )
1062  {
1063  QVBoxLayout* c = new QVBoxLayout();
1064  mypLabel->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed );
1065  c->layout()->addWidget( mypLabel );
1066  c->layout()->addWidget( widgetInfo.widget );
1067  gbLayout->addLayout( c, row, column, 1, 2 );
1068  column += 2;
1069  }
1070  else
1071  {
1072  gbLayout->addWidget( mypLabel, row, column++ );
1073  gbLayout->addWidget( widgetInfo.widget, row, column++ );
1074  }
1075  }
1076 
1077  if ( column >= columnCount * 2 )
1078  {
1079  column = 0;
1080  row += 1;
1081  }
1082  }
1083  QWidget* spacer = new QWidget();
1084  spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
1085  gbLayout->addWidget( spacer, ++row, 0 );
1086 
1087  newWidgetInfo.labelText = QString::null;
1088  newWidgetInfo.labelOnTop = true;
1089  break;
1090  }
1091 
1092  default:
1093  QgsDebugMsg( "Unknown attribute editor widget type encountered..." );
1094  break;
1095  }
1096 
1097  return newWidgetInfo;
1098 }
1099 
1100 void QgsAttributeForm::addWidgetWrapper( QgsEditorWidgetWrapper* eww )
1101 {
1102  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
1103  {
1104  QgsEditorWidgetWrapper* meww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
1105  if ( meww )
1106  {
1107  if ( meww->field() == eww->field() )
1108  {
1109  connect( meww, SIGNAL( valueChanged( QVariant ) ), eww, SLOT( setValue( QVariant ) ) );
1110  connect( eww, SIGNAL( valueChanged( QVariant ) ), meww, SLOT( setValue( QVariant ) ) );
1111  break;
1112  }
1113  }
1114  }
1115 
1116  mWidgets.append( eww );
1117 }
1118 
1119 void QgsAttributeForm::createWrappers()
1120 {
1121  QList<QWidget*> myWidgets = findChildren<QWidget*>();
1122  const QList<QgsField> fields = mLayer->fields().toList();
1123 
1124  Q_FOREACH ( QWidget* myWidget, myWidgets )
1125  {
1126  // Check the widget's properties for a relation definition
1127  QVariant vRel = myWidget->property( "qgisRelation" );
1128  if ( vRel.isValid() )
1129  {
1131  QgsRelation relation = relMgr->relation( vRel.toString() );
1132  if ( relation.isValid() )
1133  {
1134  QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, relation, myWidget, this );
1135  rww->setConfig( mLayer->editFormConfig()->widgetConfig( relation.id() ) );
1136  rww->setContext( mContext );
1137  rww->widget(); // Will initialize the widget
1138  mWidgets.append( rww );
1139  }
1140  }
1141  else
1142  {
1143  Q_FOREACH ( const QgsField& field, fields )
1144  {
1145  if ( field.name() == myWidget->objectName() )
1146  {
1147  const QString widgetType = mLayer->editFormConfig()->widgetType( field.name() );
1148  const QgsEditorWidgetConfig widgetConfig = mLayer->editFormConfig()->widgetConfig( field.name() );
1149  int idx = mLayer->fieldNameIndex( field.name() );
1150 
1151  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, idx, widgetConfig, myWidget, this, mContext );
1152  addWidgetWrapper( eww );
1153  }
1154  }
1155  }
1156  }
1157 }
1158 
1159 void QgsAttributeForm::connectWrappers()
1160 {
1161  bool isFirstEww = true;
1162 
1163  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
1164  {
1165  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
1166 
1167  if ( eww )
1168  {
1169  if ( isFirstEww )
1170  {
1171  setFocusProxy( eww->widget() );
1172  isFirstEww = false;
1173  }
1174 
1175  connect( eww, SIGNAL( valueChanged( const QVariant& ) ), this, SLOT( onAttributeChanged( const QVariant& ) ) );
1176  }
1177  }
1178 }
1179 
1180 
1182 {
1183  Q_UNUSED( object )
1184 
1185  if ( e->type() == QEvent::KeyPress )
1186  {
1187  QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>( e );
1188  if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
1189  {
1190  // Re-emit to this form so it will be forwarded to parent
1191  event( e );
1192  return true;
1193  }
1194  }
1195 
1196  return false;
1197 }
1198 
1199 void QgsAttributeForm::scanForEqualAttributes( QgsFeatureIterator& fit, QSet< int >& mixedValueFields, QHash< int, QVariant >& fieldSharedValues ) const
1200 {
1201  mixedValueFields.clear();
1202  fieldSharedValues.clear();
1203 
1204  QgsFeature f;
1205  bool first = true;
1206  while ( fit.nextFeature( f ) )
1207  {
1208  for ( int i = 0; i < mLayer->fields().count(); ++i )
1209  {
1210  if ( mixedValueFields.contains( i ) )
1211  continue;
1212 
1213  if ( first )
1214  {
1215  fieldSharedValues[i] = f.attribute( i );
1216  }
1217  else
1218  {
1219  if ( fieldSharedValues.value( i ) != f.attribute( i ) )
1220  {
1221  fieldSharedValues.remove( i );
1222  mixedValueFields.insert( i );
1223  }
1224  }
1225  }
1226  first = false;
1227 
1228  if ( mixedValueFields.count() == mLayer->fields().count() )
1229  {
1230  // all attributes are mixed, no need to keep scanning
1231  break;
1232  }
1233  }
1234 }
1235 
1236 
1237 void QgsAttributeForm::layerSelectionChanged()
1238 {
1239  switch ( mMode )
1240  {
1241  case SingleEditMode:
1242  case AddFeatureMode:
1243  break;
1244 
1245  case MultiEditMode:
1246  resetMultiEdit( true );
1247  break;
1248  }
1249 }
1250 
1252 {
1253  mIsSettingMultiEditFeatures = true;
1254  mMultiEditFeatureIds = fids;
1255 
1256  if ( fids.isEmpty() )
1257  {
1258  // no selected features
1260  for ( ; wIt != mFormEditorWidgets.constEnd(); ++ wIt )
1261  {
1262  wIt.value()->initialize( QVariant() );
1263  }
1264  mIsSettingMultiEditFeatures = false;
1265  return;
1266  }
1267 
1268  QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest().setFilterFids( fids ) );
1269 
1270  // Scan through all features to determine which attributes are initially the same
1271  QSet< int > mixedValueFields;
1272  QHash< int, QVariant > fieldSharedValues;
1273  scanForEqualAttributes( fit, mixedValueFields, fieldSharedValues );
1274 
1275  // also fetch just first feature
1276  fit = mLayer->getFeatures( QgsFeatureRequest().setFilterFid( *fids.constBegin() ) );
1277  QgsFeature firstFeature;
1278  fit.nextFeature( firstFeature );
1279 
1280  Q_FOREACH ( int field, mixedValueFields )
1281  {
1282  if ( QgsAttributeFormEditorWidget* w = mFormEditorWidgets.value( field, nullptr ) )
1283  {
1284  w->initialize( firstFeature.attribute( field ), true );
1285  }
1286  }
1287  QHash< int, QVariant >::const_iterator sharedValueIt = fieldSharedValues.constBegin();
1288  for ( ; sharedValueIt != fieldSharedValues.constEnd(); ++sharedValueIt )
1289  {
1290  if ( QgsAttributeFormEditorWidget* w = mFormEditorWidgets.value( sharedValueIt.key(), nullptr ) )
1291  {
1292  w->initialize( sharedValueIt.value(), false );
1293  }
1294  }
1295  mIsSettingMultiEditFeatures = false;
1296 }
1297 
1298 int QgsAttributeForm::messageTimeout()
1299 {
1300  QSettings settings;
1301  return settings.value( "/qgis/messageTimeout", 5 ).toInt();
1302 }
QLayout * layout() const
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:429
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
Load the python code from an external file.
Use the python code available in the python environment.
void resetValues()
Sets all values to the values of the current feature.
virtual void setEnabled(bool enabled)
Is used to enable or disable the edit functionality of the managed widget.
void clear()
Wrapper for iterator of features from vector data provider or vector layer.
bool isValid() const
Returns the validity of this relation.
Use the python code provided in the dialog.
virtual QLayout * layout()
void setWidget(QWidget *widget)
Type type() const
int fieldIdx() const
Access the field index.
This is an abstract base class for any elements of a drag and drop form.
void setMultiEditFeatureIds(const QgsFeatureIds &fids)
Sets all feature IDs which are to be edited if the form is in multiedit mode.
virtual bool isGroupBox() const
Returns if this container is going to be rendered as a group box.
PythonInitCodeSource initCodeSource() const
Return python code source for edit form initialization (if it shall be loaded from a file...
Q_DECL_DEPRECATED void accept()
Alias for save()
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:199
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
void addWidget(QWidget *widget, int row, int column, QFlags< Qt::AlignmentFlag > alignment)
void hideButtonBox()
Hides the button box (Ok/Cancel) and enables auto-commit.
QVariant currentValue() const
Returns the current value of the attached editor widget.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QgsFields fields() const
Returns the list of fields of this layer.
void setFrameShape(Shape)
This class contains context information for attribute editor widgets.
QObject * sender() const
Manages an editor widget Widget and wrapper share the same parent.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:124
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
QgsField field() const
Access the field.
bool editable()
Returns if the form is currently in editable mode.
const_iterator constBegin() const
bool save()
Save all the values from the editors to the layer.
const QObjectList & children() const
Use a layout with tabs and group boxes. Needs to be configured.
void insert(int i, const T &value)
Q_DECL_DEPRECATED void setIsAddDialog(bool isAddDialog)
Toggles the form mode between edit feature and add feature.
bool isVisible() const
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:42
void setAlignment(QFlags< Qt::AlignmentFlag >)
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:115
bool readOnly(int idx) const
This returns true if the field is manually set to read only or if the field does not support editing ...
This element will load a field&#39;s widget onto the form.
This element will load a relation editor onto the form.
bool addFeature(QgsFeature &f, bool alsoUpdateExtent=true)
Adds a feature.
const QgsRelation & relation() const
Get the id of the relation which shall be embedded.
const Key & key() const
const_iterator insert(const T &value)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
QString widgetType(int fieldIdx) const
Get the id for the editor widget used to represent the field at the given index.
QgsEditFormConfig * editFormConfig() const
Get the configuration of the form used to represent this vector layer.
void setWorkingDirectory(const QDir &dir)
void clear()
QString id() const
A (project-wide) unique id for this relation.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
static QgsEditorWidgetRegistry * instance()
This class is a singleton and has therefore to be accessed with this method instead of a constructor...
QString tr(const char *sourceText, const char *disambiguation, int n)
int idx() const
Return the index of the field.
StandardButton information(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
QgsVectorLayer * layer()
Returns the layer for which this form is shown.
Mode mode() const
Returns the current mode of the form.
virtual void setFeature(const QgsFeature &feature)=0
Is called, when the value of the widget needs to be changed.
bool isNull() const
QString name() const
Return the name of this element.
bool labelOnTop(int idx) const
If this returns true, the widget at the given index will receive its label on the previous line while...
EditorLayout layout() const
Get the active layout style for the attribute editor for this layer.
QString uiForm() const
Get path to the .ui form.
A widget consisting of both an editor widget and additional widgets for controlling the behaviour of ...
void setMode(Mode mode)
Sets the current mode of the form.
QSize size() const
void setContext(const QgsAttributeEditorContext &context)
Set the context in which this widget is shown.
const char * name() const
void showButtonBox()
Shows the button box (Ok/Cancel) and disables auto-commit.
void setConfig(const QgsEditorWidgetConfig &config)
Will set the config of this wrapper to the specified config.
void setEnabled(bool)
void append(const T &value)
QVariant property(const char *name) const
void setLayout(QLayout *layout)
const_iterator constEnd() const
void installEventFilter(QObject *filterObj)
Do not use python code at all.
int toInt(bool *ok) const
bool isNull() const
QString attributeDisplayName(int attributeIndex) const
Convenience function that returns the attribute alias if defined or the field name else...
const QgsFeatureIds & selectedFeaturesIds() const
Return reference to identifiers of selected features.
void setFeature(const QgsFeature &feature)
Update all editors to correspond to a different feature.
bool popWidget(QgsMessageBarItem *item)
Remove the passed widget from the bar (if previously added), then display the next one in the stack i...
QgsRelation relation(const QString &id) const
Get access to a relation by its id.
bool hasChanged() const
Returns true if the widget&#39;s value has been changed since it was initialized.
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
virtual int capabilities() const
Returns a bitmask containing the supported capabilities Note, some capabilities may change depending ...
QgsEditorWidgetWrapper * create(const QString &widgetId, QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, QWidget *editor, QWidget *parent, const QgsAttributeEditorContext &context=QgsAttributeEditorContext())
Create an attribute editor widget wrapper of a given type for a given field.
QString name() const
Gets the name of the field.
Definition: qgsfield.cpp:84
const QgsFields * fields() const
Returns the field map associated with the feature.
Definition: qgsfeature.cpp:188
void setObjectName(const QString &name)
const T & value() const
void setFocusProxy(QWidget *w)
bool isEmpty() const
void setText(const QString &text)
const_iterator constEnd() const
void remove(int i)
void triggerRepaint()
Will advice the map canvas (and any other interested party) that this layer requires to be repainted...
int addTab(QWidget *page, const QString &label)
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void setOverrideCursor(const QCursor &cursor)
AttributeEditorType type() const
The type of this element.
void destroyEditCommand()
Destroy active command and reverts all changes in it.
void restoreOverrideCursor()
QgsEditorWidgetConfig widgetConfig(int fieldIdx) const
Get the configuration for the editor widget used to represent the field at the given index...
void refreshFeature()
reload current feature
virtual void setValue(const QVariant &value)=0
Is called, when the value of the widget needs to be changed.
int count() const
Return number of items.
Definition: qgsfield.cpp:365
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:44
void hide()
int remove(const Key &key)
int count() const
void beforeSave(bool &ok)
Will be emitted before the feature is saved.
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
void disconnectButtonBox()
Disconnects the button box (Ok/Cancel) from the accept/resetValues slots If this method is called...
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:385
void setSizePolicy(QSizePolicy)
void addWidget(QWidget *w)
bool eventFilter(QObject *object, QEvent *event) override
Intercepts keypress on custom form (escape should not close it)
Q_DECL_DEPRECATED 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:173
Q_DECL_DEPRECATED bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &value, bool emitSignal)
Changes an attribute value (but does not commit it)
void endEditCommand()
Finish edit command and add it to undo/redo stack.
int indexFromName(const QString &name) const
Look up field&#39;s index from name. Returns -1 on error.
Definition: qgsfield.cpp:424
void clear()
const T value(const Key &key) const
int key() const
QList< QgsAttributeEditorElement * > children() const
Get a list of the children elements of this container.
static bool eval(const QString &command, QString &result)
Eval a python statement.
This class helps to support legacy open form scripts to be compatible with the new QgsAttributeForm s...
void featureSaved(const QgsFeature &feature)
Is emitted, when a feature is changed or added.
void setWidgetResizable(bool resizable)
virtual void close()
const_iterator constBegin() const
bool contains(const T &value) const
virtual bool acceptChanges(const QgsFeature &feature)
void setFrameShadow(Shadow)
const Key key(const T &value) const
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:204
void setMode(Mode mode)
Sets the current mode for the widget.
void addLayout(QLayout *layout, int row, int column, QFlags< Qt::AlignmentFlag > alignment)
void setWindowFlags(QFlags< Qt::WindowType > type)
const T & at(int i) const
QVariant value(const QString &key, const QVariant &defaultValue) const
const_iterator constBegin() const
QgsAttributeForm(QgsVectorLayer *vl, const QgsFeature &feature=QgsFeature(), const QgsAttributeEditorContext &context=QgsAttributeEditorContext(), QWidget *parent=nullptr)
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar after hiding the currently visible one and putting it in a stack...
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
void changeAttribute(const QString &field, const QVariant &value)
Call this to change the content of a given attribute.
void attributeChanged(const QString &attribute, const QVariant &value)
Notifies about changes of attributes.
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a python statement.
Q_DECL_DEPRECATED QString editFormInit() const
Get python function for edit form initialization.
void setTitle(const QString &title)
virtual void setIsGroupBox(bool isGroupBox)
Determines if this container is rendered as collapsible group box or tab in a tabwidget.
Load a .ui file for the layout. Needs to be configured.
This class manages a set of relations between layers.
QString initFunction() const
Get python function for edit form initialization.
void addItem(QLayoutItem *item, int row, int column, int rowSpan, int columnSpan, QFlags< Qt::AlignmentFlag > alignment)
int columnCount() const
Get the number of columns in this group.
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:388
QWidget(QWidget *parent, QFlags< Qt::WindowType > f)
virtual QVariant value() const =0
Will be used to access the widget&#39;s value.
int count(const T &value) const
QList< QgsAttributeEditorElement * > tabs() const
Returns a list of tabs for EditorLayout::TabLayout.
int size() const
Return number of items.
Definition: qgsfield.cpp:370
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QWidget * load(QIODevice *device, QWidget *parentWidget)
void addInterface(QgsAttributeFormInterface *iface)
Takes ownership.
bool isEmpty() const
QString initCode() const
Get python code for edit form initialization.
qint64 QgsFeatureId
Definition: qgsfeature.h:31
bool isValid() const
QPushButton * button(StandardButton which) const
#define FID_IS_NEW(fid)
Definition: qgsfeature.h:87
const QgsFeature & feature()
iterator insert(const Key &key, const T &value)
void show()
bool isEmpty() const
QWidget * widget()
Access the widget managed by this wrapper.
void changesCommitted()
Called when field values have been committed;.
QgsVectorDataProvider * dataProvider()
Returns the data provider.
bool nextFeature(QgsFeature &f)
void clear()
A vector of attributes.
Definition: qgsfeature.h:115
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
QString readAll()
Represents a vector layer which manages a vector based data sets.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
QgsRelationManager * relationManager() const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
virtual bool event(QEvent *event)
Manages an editor widget Widget and wrapper share the same parent.
Allows modification of attribute values.
void setContentsMargins(int left, int top, int right, int bottom)
QVariant::Type type() const
Gets variant type of the field as it will be retrieved from data source.
Definition: qgsfield.cpp:89
const T value(const Key &key) const
QString initFilePath() const
Get python external file path for edit form initialization.
#define tr(sourceText)