QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 dot kuhn at gmx 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 
26 #include <QDir>
27 #include <QFileInfo>
28 #include <QFormLayout>
29 #include <QGridLayout>
30 #include <QGroupBox>
31 #include <QKeyEvent>
32 #include <QLabel>
33 #include <QPushButton>
34 #include <QScrollArea>
35 #include <QTabWidget>
36 #include <QUiLoader>
37 
38 int QgsAttributeForm::sFormCounter = 0;
39 
41  : QWidget( parent )
42  , mLayer( vl )
43  , mContext( context )
44  , mButtonBox( 0 )
45  , mFormNr( sFormCounter++ )
46  , mIsSaving( false )
47  , mIsAddDialog( false )
48  , mEditCommandMessage( tr( "Attributes changed" ) )
49 {
50  init();
51  initPython();
52  setFeature( feature );
53 
54  connect( vl, SIGNAL( attributeAdded( int ) ), this, SLOT( onAttributeAdded( int ) ) );
55  connect( vl, SIGNAL( attributeDeleted( int ) ), this, SLOT( onAttributeDeleted( int ) ) );
56 }
57 
59 {
60  cleanPython();
61  qDeleteAll( mInterfaces );
62 }
63 
65 {
66  mButtonBox->hide();
67 
68  // Make sure that changes are taken into account if somebody tries to figure out if there have been some
69  if ( !mIsAddDialog )
70  connect( mLayer, SIGNAL( beforeModifiedCheck() ), this, SLOT( save() ) );
71 }
72 
74 {
75  mButtonBox->show();
76 
77  disconnect( mLayer, SIGNAL( beforeModifiedCheck() ), this, SLOT( save() ) );
78 }
79 
81 {
82  disconnect( mButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
83  disconnect( mButtonBox, SIGNAL( rejected() ), this, SLOT( resetValues() ) );
84 }
85 
87 {
88  mInterfaces.append( iface );
89 }
90 
92 {
93  return mFeature.isValid() && mLayer->isEditable();
94 }
95 
96 void QgsAttributeForm::setIsAddDialog( bool isAddDialog )
97 {
98  mIsAddDialog = isAddDialog;
99 
100  synchronizeEnabledState();
101 }
102 
103 void QgsAttributeForm::changeAttribute( const QString& field, const QVariant& value )
104 {
105  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
106  {
107  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
108  if ( eww && eww->field().name() == field )
109  {
110  eww->setValue( value );
111  }
112  }
113 }
114 
116 {
117  mFeature = feature;
118 
119  resetValues();
120 
121  synchronizeEnabledState();
122 
123  Q_FOREACH ( QgsAttributeFormInterface* iface, mInterfaces )
124  {
125  iface->featureChanged();
126  }
127 }
128 
130 {
131  if ( mIsSaving )
132  return true;
133 
134  mIsSaving = true;
135 
136  bool changedLayer = false;
137 
138  bool success = true;
139 
140  emit beforeSave( success );
141 
142  // Somebody wants to prevent this form from saving
143  if ( !success )
144  return false;
145 
146  QgsFeature updatedFeature = QgsFeature( mFeature );
147 
148  if ( mFeature.isValid() || mIsAddDialog )
149  {
150  bool doUpdate = false;
151 
152  // An add dialog should perform an action by default
153  // and not only if attributes have "changed"
154  if ( mIsAddDialog )
155  doUpdate = true;
156 
157  QgsAttributes src = mFeature.attributes();
158  QgsAttributes dst = mFeature.attributes();
159 
160  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
161  {
162  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
163  if ( eww )
164  {
165  QVariant dstVar = dst[eww->fieldIdx()];
166  QVariant srcVar = eww->value();
167  // need to check dstVar.isNull() != srcVar.isNull()
168  // otherwise if dstVar=NULL and scrVar=0, then dstVar = srcVar
169  if (( dstVar != srcVar || dstVar.isNull() != srcVar.isNull() ) && srcVar.isValid() && mLayer->fieldEditable( eww->fieldIdx() ) )
170  {
171  dst[eww->fieldIdx()] = srcVar;
172 
173  doUpdate = true;
174  }
175  }
176  }
177 
178  updatedFeature.setAttributes( dst );
179 
180  Q_FOREACH ( QgsAttributeFormInterface* iface, mInterfaces )
181  {
182  if ( !iface->acceptChanges( updatedFeature ) )
183  {
184  doUpdate = false;
185  }
186  }
187 
188  if ( doUpdate )
189  {
190  if ( mIsAddDialog )
191  {
192  mFeature.setValid( true );
193  mLayer->beginEditCommand( mEditCommandMessage );
194  bool res = mLayer->addFeature( updatedFeature );
195  if ( res )
196  {
197  mFeature.setAttributes( updatedFeature.attributes() );
198  mLayer->endEditCommand();
199  mIsAddDialog = false;
200  changedLayer = true;
201  }
202  else
203  mLayer->destroyEditCommand();
204  }
205  else
206  {
207  mLayer->beginEditCommand( mEditCommandMessage );
208 
209  int n = 0;
210  for ( int i = 0; i < dst.count(); ++i )
211  {
212  if (( dst[i] == src[i] && dst[i].isNull() == src[i].isNull() ) // If field is not changed...
213  || !dst[i].isValid() // or the widget returns invalid (== do not change)
214  || !mLayer->fieldEditable( i ) ) // or the field cannot be edited ...
215  {
216  continue;
217  }
218 
219  QgsDebugMsg( QString( "Updating field %1" ).arg( i ) );
220  QgsDebugMsg( QString( "dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
221  .arg( dst[i].toString() ).arg( dst[i].typeName() ).arg( dst[i].isNull() ).arg( dst[i].isValid() ) );
222  QgsDebugMsg( QString( "src:'%1' (type:%2, isNull:%3, isValid:%4)" )
223  .arg( src[i].toString() ).arg( src[i].typeName() ).arg( src[i].isNull() ).arg( src[i].isValid() ) );
224 
225  success &= mLayer->changeAttributeValue( mFeature.id(), i, dst[i], src[i] );
226  n++;
227  }
228 
229  if ( success && n > 0 )
230  {
231  mLayer->endEditCommand();
232  mFeature.setAttributes( dst );
233  changedLayer = true;
234  }
235  else
236  {
237  mLayer->destroyEditCommand();
238  }
239  }
240  }
241  }
242 
243  emit featureSaved( updatedFeature );
244 
245  // [MD] Refresh canvas only when absolutely necessary - it interferes with other stuff (#11361).
246  // This code should be revisited - and the signals should be fired (+ layer repainted)
247  // only when actually doing any changes. I am unsure if it is actually a good idea
248  // to call save() whenever some code asks for vector layer's modified status
249  // (which is the case when attribute table is open)
250  if ( changedLayer )
251  mLayer->triggerRepaint();
252 
253  mIsSaving = false;
254 
255  return success;
256 }
257 
259 {
260  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
261  {
262  ww->setFeature( mFeature );
263  }
264 }
265 
266 void QgsAttributeForm::onAttributeChanged( const QVariant& value )
267 {
268  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( sender() );
269 
270  Q_ASSERT( eww );
271 
272  emit attributeChanged( eww->field().name(), value );
273 }
274 
275 void QgsAttributeForm::onAttributeAdded( int idx )
276 {
277  Q_UNUSED( idx ) // only used for Q_ASSERT
278  if ( mFeature.isValid() )
279  {
280  QgsAttributes attrs = mFeature.attributes();
281  attrs.insert( idx, QVariant( layer()->pendingFields()[idx].type() ) );
282  mFeature.setFields( layer()->pendingFields() );
283  mFeature.setAttributes( attrs );
284  }
285  init();
286  setFeature( mFeature );
287 }
288 
289 void QgsAttributeForm::onAttributeDeleted( int idx )
290 {
291  if ( mFeature.isValid() )
292  {
293  QgsAttributes attrs = mFeature.attributes();
294  attrs.remove( idx );
295  mFeature.setFields( layer()->pendingFields() );
296  mFeature.setAttributes( attrs );
297  }
298  init();
299  setFeature( mFeature );
300 }
301 
303 {
304  if ( mLayer->isEditable() || !mFeature.isValid() )
305  return;
306 
307  // reload feature if layer changed although not editable
308  // (datasource probably changed bypassing QgsVectorLayer)
309  if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( mFeature ) )
310  return;
311 
312  init();
313  setFeature( mFeature );
314 }
315 
316 void QgsAttributeForm::synchronizeEnabledState()
317 {
318  bool isEditable = ( mFeature.isValid() || mIsAddDialog ) && mLayer->isEditable();
319 
320  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
321  {
322  bool fieldEditable = true;
323  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
324  if ( eww )
325  {
326  fieldEditable = mLayer->fieldEditable( eww->fieldIdx() );
327  }
328  ww->setEnabled( isEditable && fieldEditable );
329  }
330 
331  QPushButton* okButton = mButtonBox->button( QDialogButtonBox::Ok );
332  if ( okButton )
333  okButton->setEnabled( isEditable );
334 }
335 
336 void QgsAttributeForm::init()
337 {
338  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
339 
340  // Cleanup of any previously shown widget, we start from scratch
341  QWidget* formWidget = 0;
342 
343  bool buttonBoxVisible = true;
344  // Cleanup button box but preserve visibility
345  if ( mButtonBox )
346  {
347  buttonBoxVisible = mButtonBox->isVisible();
348  delete mButtonBox;
349  mButtonBox = 0;
350  }
351 
352  qDeleteAll( mWidgets );
353  mWidgets.clear();
354 
355  while ( QWidget* w = this->findChild<QWidget*>() )
356  {
357  delete w;
358  }
359  delete layout();
360 
361  // Get a layout
362  setLayout( new QGridLayout( this ) );
363 
364  // Try to load Ui-File for layout
365  if ( mLayer->editorLayout() == QgsVectorLayer::UiFileLayout && !mLayer->editForm().isEmpty() )
366  {
367  QFile file( mLayer->editForm() );
368 
369  if ( file.open( QFile::ReadOnly ) )
370  {
371  QUiLoader loader;
372 
373  QFileInfo fi( mLayer->editForm() );
374  loader.setWorkingDirectory( fi.dir() );
375  formWidget = loader.load( &file, this );
376  formWidget->setWindowFlags( Qt::Widget );
377  layout()->addWidget( formWidget );
378  formWidget->show();
379  file.close();
380  mButtonBox = findChild<QDialogButtonBox*>();
381  createWrappers();
382 
383  formWidget->installEventFilter( this );
384  }
385  }
386 
387  // Tab layout
388  if ( !formWidget && mLayer->editorLayout() == QgsVectorLayer::TabLayout )
389  {
390  QTabWidget* tabWidget = new QTabWidget();
391  layout()->addWidget( tabWidget );
392 
393  Q_FOREACH ( QgsAttributeEditorElement *widgDef, mLayer->attributeEditorElements() )
394  {
395  QWidget* tabPage = new QWidget( tabWidget );
396 
397  tabWidget->addTab( tabPage, widgDef->name() );
398  QGridLayout* tabPageLayout = new QGridLayout();
399  tabPage->setLayout( tabPageLayout );
400 
402  {
403  QgsAttributeEditorContainer* containerDef = dynamic_cast<QgsAttributeEditorContainer*>( widgDef );
404  if ( !containerDef )
405  continue;
406 
407  containerDef->setIsGroupBox( false ); // Toplevel widgets are tabs not groupboxes
408  QString dummy1;
409  bool dummy2;
410  tabPageLayout->addWidget( createWidgetFromDef( widgDef, tabPage, mLayer, mContext, dummy1, dummy2 ) );
411  }
412  else
413  {
414  QgsDebugMsg( "No support for fields in attribute editor on top level" );
415  }
416  }
417  formWidget = tabWidget;
418  }
419 
420  // Autogenerate Layout
421  // If there is still no layout loaded (defined as autogenerate or other methods failed)
422  if ( !formWidget )
423  {
424  formWidget = new QWidget( this );
425  QGridLayout* gridLayout = new QGridLayout( formWidget );
426  formWidget->setLayout( gridLayout );
427 
428  // put the form into a scroll area to nicely handle cases with lots of attributes
429 
430  QScrollArea* scrollArea = new QScrollArea( this );
431  scrollArea->setWidget( formWidget );
432  scrollArea->setWidgetResizable( true );
433  scrollArea->setFrameShape( QFrame::NoFrame );
434  scrollArea->setFrameShadow( QFrame::Plain );
435  scrollArea->setFocusProxy( this );
436  layout()->addWidget( scrollArea );
437 
438  int row = 0;
439  Q_FOREACH ( const QgsField& field, mLayer->pendingFields().toList() )
440  {
441  int idx = mLayer->fieldNameIndex( field.name() );
442  if ( idx < 0 )
443  continue;
444 
445  //show attribute alias if available
446  QString fieldName = mLayer->attributeDisplayName( idx );
447 
448  const QString widgetType = mLayer->editorWidgetV2( idx );
449 
450  if ( widgetType == "Hidden" )
451  continue;
452 
453  const QgsEditorWidgetConfig widgetConfig = mLayer->editorWidgetV2Config( idx );
454  bool labelOnTop = mLayer->labelOnTop( idx );
455 
456  // This will also create the widget
457  QWidget *l = new QLabel( fieldName );
458  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, idx, widgetConfig, 0, this, mContext );
459  QWidget *w = eww ? eww->widget() : new QLabel( QString( "<p style=\"color: red; font-style: italic;\">Failed to create widget with type '%1'</p>" ).arg( widgetType ) );
460 
461  if ( w )
462  w->setObjectName( field.name() );
463 
464  if ( eww )
465  addWidgetWrapper( eww );
466 
467  if ( labelOnTop )
468  {
469  gridLayout->addWidget( l, row++, 0, 1, 2 );
470  gridLayout->addWidget( w, row++, 0, 1, 2 );
471  }
472  else
473  {
474  gridLayout->addWidget( l, row, 0 );
475  gridLayout->addWidget( w, row++, 1 );
476  }
477  }
478 
479  Q_FOREACH ( const QgsRelation& rel, QgsProject::instance()->relationManager()->referencedRelations( mLayer ) )
480  {
481  QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, rel, 0, this );
482  rww->setContext( mContext );
483  gridLayout->addWidget( rww->widget(), row++, 0, 1, 2 );
484  mWidgets.append( rww );
485  }
486  }
487 
488  if ( !mButtonBox )
489  {
490  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
491  mButtonBox->setObjectName( "buttonBox" );
492  layout()->addWidget( mButtonBox );
493  }
494 
495  mButtonBox->setVisible( buttonBoxVisible );
496 
497  connectWrappers();
498 
499  connect( mButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
500  connect( mButtonBox, SIGNAL( rejected() ), this, SLOT( resetValues() ) );
501 
502  connect( mLayer, SIGNAL( editingStarted() ), this, SLOT( synchronizeEnabledState() ) );
503  connect( mLayer, SIGNAL( editingStopped() ), this, SLOT( synchronizeEnabledState() ) );
504 
505  Q_FOREACH ( QgsAttributeFormInterface* iface, mInterfaces )
506  {
507  iface->initForm();
508  }
510 }
511 
512 void QgsAttributeForm::cleanPython()
513 {
514  if ( !mPyFormVarName.isNull() )
515  {
516  QString expr = QString( "if locals().has_key('%1'): del %1\n" ).arg( mPyFormVarName );
517  QgsPythonRunner::run( expr );
518  }
519 }
520 
521 void QgsAttributeForm::initPython()
522 {
523  cleanPython();
524 
525  // Init Python
526  if ( !mLayer->editFormInit().isEmpty() )
527  {
528  QString module = mLayer->editFormInit();
529 
530  int pos = module.lastIndexOf( "." );
531  if ( pos >= 0 )
532  {
533  QgsPythonRunner::run( QString( "import %1" ).arg( module.left( pos ) ) );
534  }
535 
536  /* Reload the module if the DEBUGMODE switch has been set in the module.
537  If set to False you have to reload QGIS to reset it to True due to Python
538  module caching */
539  QString reload = QString( "if hasattr(%1,'DEBUGMODE') and %1.DEBUGMODE:"
540  " reload(%1)" ).arg( module.left( pos ) );
541 
542  QgsPythonRunner::run( reload );
543 
544  QgsPythonRunner::run( "import inspect" );
545  QString numArgs;
546  QgsPythonRunner::eval( QString( "len(inspect.getargspec(%1)[0])" ).arg( module ), numArgs );
547 
548  static int sFormId = 0;
549  mPyFormVarName = QString( "_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
550 
551  QString form = QString( "%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
552  .arg( mPyFormVarName )
553  .arg(( unsigned long ) this );
554 
555  QgsPythonRunner::run( form );
556 
557  QgsDebugMsg( QString( "running featureForm init: %1" ).arg( mPyFormVarName ) );
558 
559  // Legacy
560  if ( numArgs == "3" )
561  {
562  addInterface( new QgsAttributeFormLegacyInterface( module, mPyFormVarName, this ) );
563  }
564  else
565  {
566 #if 0
567  QString expr = QString( "%1(%2)" )
568  .arg( mLayer->editFormInit() )
569  .arg( mPyFormVarName );
570  QgsAttributeFormInterface* iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface*>( expr, "QgsAttributeFormInterface" );
571  if ( iface )
572  addInterface( iface );
573 #endif
574  }
575  }
576 }
577 
578 QWidget* QgsAttributeForm::createWidgetFromDef( const QgsAttributeEditorElement *widgetDef, QWidget *parent, QgsVectorLayer *vl, QgsAttributeEditorContext &context, QString &labelText, bool &labelOnTop )
579 {
580  QWidget *newWidget = 0;
581 
582  switch ( widgetDef->type() )
583  {
585  {
586  const QgsAttributeEditorField* fieldDef = dynamic_cast<const QgsAttributeEditorField*>( widgetDef );
587  if ( !fieldDef )
588  break;
589 
590  int fldIdx = vl->fieldNameIndex( fieldDef->name() );
591  if ( fldIdx < vl->pendingFields().count() && fldIdx >= 0 )
592  {
593  const QString widgetType = mLayer->editorWidgetV2( fldIdx );
594  const QgsEditorWidgetConfig widgetConfig = mLayer->editorWidgetV2Config( fldIdx );
595 
596  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, fldIdx, widgetConfig, 0, this, mContext );
597  newWidget = eww->widget();
598  addWidgetWrapper( eww );
599 
600  newWidget->setObjectName( mLayer->pendingFields()[ fldIdx ].name() );
601  }
602 
603  labelOnTop = mLayer->labelOnTop( fieldDef->idx() );
604  labelText = mLayer->attributeDisplayName( fieldDef->idx() );
605 
606  break;
607  }
608 
610  {
611  const QgsAttributeEditorRelation* relDef = dynamic_cast<const QgsAttributeEditorRelation*>( widgetDef );
612 
613  QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, relDef->relation(), 0, this );
614  rww->setContext( context );
615  newWidget = rww->widget();
616  mWidgets.append( rww );
617  labelText = QString::null;
618  labelOnTop = true;
619  break;
620  }
621 
623  {
624  const QgsAttributeEditorContainer* container = dynamic_cast<const QgsAttributeEditorContainer*>( widgetDef );
625  if ( !container )
626  break;
627 
628  QWidget* myContainer;
629  if ( container->isGroupBox() )
630  {
631  QGroupBox* groupBox = new QGroupBox( parent );
632  groupBox->setTitle( container->name() );
633  myContainer = groupBox;
634  newWidget = myContainer;
635  }
636  else
637  {
638  QScrollArea *scrollArea = new QScrollArea( parent );
639 
640  myContainer = new QWidget( scrollArea );
641 
642  scrollArea->setWidget( myContainer );
643  scrollArea->setWidgetResizable( true );
644  scrollArea->setFrameShape( QFrame::NoFrame );
645 
646  newWidget = scrollArea;
647  }
648 
649  QGridLayout* gbLayout = new QGridLayout();
650  myContainer->setLayout( gbLayout );
651 
652  int index = 0;
653 
655 
656  Q_FOREACH ( QgsAttributeEditorElement* childDef, children )
657  {
658  QString labelText;
659  bool labelOnTop;
660  QWidget* editor = createWidgetFromDef( childDef, myContainer, vl, context, labelText, labelOnTop );
661 
662  if ( labelText.isNull() )
663  {
664  gbLayout->addWidget( editor, index, 0, 1, 2 );
665  }
666  else
667  {
668  QLabel* mypLabel = new QLabel( labelText );
669  if ( labelOnTop )
670  {
671  gbLayout->addWidget( mypLabel, index, 0, 1, 2 );
672  ++index;
673  gbLayout->addWidget( editor, index, 0, 1, 2 );
674  }
675  else
676  {
677  gbLayout->addWidget( mypLabel, index, 0 );
678  gbLayout->addWidget( editor, index, 1 );
679  }
680  }
681 
682  ++index;
683  }
684  QWidget* spacer = new QWidget();
685  spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
686  gbLayout->addWidget( spacer, index, 0 );
687 
688  labelText = QString::null;
689  labelOnTop = true;
690  break;
691  }
692 
693  default:
694  QgsDebugMsg( "Unknown attribute editor widget type encountered..." );
695  break;
696  }
697 
698  return newWidget;
699 }
700 
701 void QgsAttributeForm::addWidgetWrapper( QgsEditorWidgetWrapper* eww )
702 {
703  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
704  {
705  QgsEditorWidgetWrapper* meww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
706  if ( meww )
707  {
708  if ( meww->field() == eww->field() )
709  {
710  connect( meww, SIGNAL( valueChanged( QVariant ) ), eww, SLOT( setValue( QVariant ) ) );
711  connect( eww, SIGNAL( valueChanged( QVariant ) ), meww, SLOT( setValue( QVariant ) ) );
712  break;
713  }
714  }
715  }
716 
717  mWidgets.append( eww );
718 }
719 
720 void QgsAttributeForm::createWrappers()
721 {
722  QList<QWidget*> myWidgets = findChildren<QWidget*>();
723  const QList<QgsField> fields = mLayer->pendingFields().toList();
724 
725  Q_FOREACH ( QWidget* myWidget, myWidgets )
726  {
727  // Check the widget's properties for a relation definition
728  QVariant vRel = myWidget->property( "qgisRelation" );
729  if ( vRel.isValid() )
730  {
732  QgsRelation relation = relMgr->relation( vRel.toString() );
733  if ( relation.isValid() )
734  {
735  QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, relation, myWidget, this );
737  rww->setContext( mContext );
738  rww->widget(); // Will initialize the widget
739  mWidgets.append( rww );
740  }
741  }
742  else
743  {
744  Q_FOREACH ( const QgsField& field, fields )
745  {
746  if ( field.name() == myWidget->objectName() )
747  {
748  const QString widgetType = mLayer->editorWidgetV2( field.name() );
749  const QgsEditorWidgetConfig widgetConfig = mLayer->editorWidgetV2Config( field.name() );
750  int idx = mLayer->fieldNameIndex( field.name() );
751 
752  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, idx, widgetConfig, myWidget, this, mContext );
753  addWidgetWrapper( eww );
754  }
755  }
756  }
757  }
758 }
759 
760 void QgsAttributeForm::connectWrappers()
761 {
762  bool isFirstEww = true;
763 
764  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
765  {
766  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
767 
768  if ( eww )
769  {
770  if ( isFirstEww )
771  {
772  setFocusProxy( eww->widget() );
773  isFirstEww = false;
774  }
775 
776  connect( eww, SIGNAL( valueChanged( const QVariant& ) ), this, SLOT( onAttributeChanged( const QVariant& ) ) );
777  }
778  }
779 }
780 
781 
783 {
784  Q_UNUSED( object )
785 
786  if ( e->type() == QEvent::KeyPress )
787  {
788  QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>( e );
789  if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
790  {
791  // Re-emit to this form so it will be forwarded to parent
792  event( e );
793  return true;
794  }
795  }
796 
797  return false;
798 }
QLayout * layout() const
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:341
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:51
const QgsEditorWidgetConfig editorWidgetV2Config(int fieldIdx) const
Get the configuration for the editor widget used to represent the field at the given index...
const QString & name() const
Gets the name of the field.
Definition: qgsfield.cpp:69
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()
bool isValid() const
Returns the validity of this relation.
static unsigned index
bool fieldEditable(int idx)
Is edit widget editable.
void setWidget(QWidget *widget)
Type type() const
This is an abstract base class for any elements of a drag and drop form.
EditorLayout editorLayout()
Get the active layout for the attribute editor for this layer.
virtual bool isGroupBox() const
Returns if this ccontainer is going to be rendered as a group box.
Q_DECL_DEPRECATED void accept()
Alias for save()
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:168
void addWidget(QWidget *widget, int row, int column, QFlags< Qt::AlignmentFlag > alignment)
void hideButtonBox()
Hides the button box (Ok/Cancel) and enables auto-commit.
QgsAttributeForm(QgsVectorLayer *vl, const QgsFeature &feature=QgsFeature(), const QgsAttributeEditorContext &context=QgsAttributeEditorContext(), QWidget *parent=0)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
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.
void beginEditCommand(QString text)
Create edit command for undo/redo operations.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
bool editable()
Returns if the form is currently in editable mode.
bool save()
Save all the values from the editors to the layer.
const QObjectList & children() const
void insert(int i, const T &value)
void setIsAddDialog(bool isAddDialog)
Toggles the form mode between edit feature and add feature.
static bool eval(QString command, QString &result)
Eval a python statement.
bool isVisible() const
virtual QVariant value()=0
Will be used to access the widget's value.
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:95
This element will load a field'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.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:162
void setWorkingDirectory(const QDir &dir)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QgsField field()
Access the field.
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...
int idx() const
Return the index of the field.
QgsVectorLayer * layer()
Returns the layer for which this form is shown.
virtual void setFeature(const QgsFeature &feature)=0
Is called, when the value of the widget needs to be changed.
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
bool isNull() const
QString name() const
Return the name of this element.
QString editForm()
Get edit form.
void setContext(const QgsAttributeEditorContext &context)
Set the context in which this widget is shown.
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)
void installEventFilter(QObject *filterObj)
bool isNull() const
QString attributeDisplayName(int attributeIndex) const
Convenience function that returns the attribute alias if defined or the field name else...
void setFeature(const QgsFeature &feature)
Update all editors to correspond to a different feature.
static bool run(QString command, QString messageOnError=QString())
Execute a python statement.
QgsRelation relation(const QString &id) const
Get access to a relation by its id.
QgsAttributes attributes() const
Returns the feature's attributes.
Definition: qgsfeature.cpp:90
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.
const QString editorWidgetV2(int fieldIdx) const
Get the id for the editor widget used to represent the field at the given index.
const QgsFields & pendingFields() const
Returns the list of fields of this layer.
void setObjectName(const QString &name)
void setFocusProxy(QWidget *w)
bool isEmpty() 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)
QPoint pos() const
AttributeEditorType type() const
The type of this element.
void destroyEditCommand()
Destroy active command and reverts all changes in it.
Q_DECL_DEPRECATED bool changeAttributeValue(QgsFeatureId fid, int field, QVariant value, bool emitSignal)
Changes an attribute value (but does not commit it)
void restoreOverrideCursor()
void refreshFeature()
reload current feature
virtual void setValue(const QVariant &value)=0
Is called, when the value of the widget needs to be changed.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:38
void hide()
void beforeSave(bool &ok)
Will be emitted before the feature is saved.
void disconnectButtonBox()
Disconnects the button box (Ok/Cancel) from the accept/resetValues slots If this method is called...
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:147
void endEditCommand()
Finish edit command and add it to undo/redo stack.
bool labelOnTop(int idx)
Label widget on top.
int key() const
QList< QgsAttributeEditorElement * > children() const
Get a list of the children elements of this container.
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 bool acceptChanges(const QgsFeature &feature)
void setFrameShadow(Shadow)
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:173
void setWindowFlags(QFlags< Qt::WindowType > type)
void changeAttribute(const QString &field, const QVariant &value)
Call this to change the content of a given attribute.
void attributeChanged(QString attribute, const QVariant &value)
Notifies about changes of attributes.
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.
This class manages a set of relations between layers.
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:351
QWidget(QWidget *parent, QFlags< Qt::WindowType > f)
int count(const T &value) const
QList< QgsAttributeEditorElement * > & attributeEditorElements()
Returns a list of tabs holding groups and fields.
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.
QString left(int n) const
QMap< QString, QVariant > QgsEditorWidgetConfig
Holds a set of configuration parameters for a editor widget wrapper.
bool isValid() const
int fieldIdx()
Access the field index.
QPushButton * button(StandardButton which) const
const QgsFeature & feature()
void show()
QWidget * widget()
Access the widget managed by this wrapper.
QString editFormInit()
Get python function for edit form initialization.
bool nextFeature(QgsFeature &f)
A vector of attributes.
Definition: qgsfeature.h:109
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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.
bool isNull(const QVariant &v)
#define tr(sourceText)