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