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