QGIS API Documentation  2.10.1-Pisa
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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  changedLayer = true;
200  }
201  else
202  mLayer->destroyEditCommand();
203  }
204  else
205  {
206  mLayer->beginEditCommand( mEditCommandMessage );
207 
208  int n = 0;
209  for ( int i = 0; i < dst.count(); ++i )
210  {
211  if (( dst[i] == src[i] && dst[i].isNull() == src[i].isNull() ) // If field is not changed...
212  || !dst[i].isValid() // or the widget returns invalid (== do not change)
213  || !mLayer->fieldEditable( i ) ) // or the field cannot be edited ...
214  {
215  continue;
216  }
217 
218  QgsDebugMsg( QString( "Updating field %1" ).arg( i ) );
219  QgsDebugMsg( QString( "dst:'%1' (type:%2, isNull:%3, isValid:%4)" )
220  .arg( dst[i].toString() ).arg( dst[i].typeName() ).arg( dst[i].isNull() ).arg( dst[i].isValid() ) );
221  QgsDebugMsg( QString( "src:'%1' (type:%2, isNull:%3, isValid:%4)" )
222  .arg( src[i].toString() ).arg( src[i].typeName() ).arg( src[i].isNull() ).arg( src[i].isValid() ) );
223 
224  success &= mLayer->changeAttributeValue( mFeature.id(), i, dst[i], src[i] );
225  n++;
226  }
227 
228  if ( success && n > 0 )
229  {
230  mLayer->endEditCommand();
231  mFeature.setAttributes( dst );
232  changedLayer = true;
233  }
234  else
235  {
236  mLayer->destroyEditCommand();
237  }
238  }
239  }
240  }
241 
242  emit featureSaved( updatedFeature );
243 
244  // [MD] Refresh canvas only when absolutely necessary - it interferes with other stuff (#11361).
245  // This code should be revisited - and the signals should be fired (+ layer repainted)
246  // only when actually doing any changes. I am unsure if it is actually a good idea
247  // to call save() whenever some code asks for vector layer's modified status
248  // (which is the case when attribute table is open)
249  if ( changedLayer )
250  mLayer->triggerRepaint();
251 
252  mIsSaving = false;
253 
254  return success;
255 }
256 
258 {
259  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
260  {
261  ww->setFeature( mFeature );
262  }
263 }
264 
265 void QgsAttributeForm::onAttributeChanged( const QVariant& value )
266 {
267  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( sender() );
268 
269  Q_ASSERT( eww );
270 
271  emit attributeChanged( eww->field().name(), value );
272 }
273 
274 void QgsAttributeForm::onAttributeAdded( int idx )
275 {
276  Q_UNUSED( idx ) // only used for Q_ASSERT
277  if ( mFeature.isValid() )
278  {
279  QgsAttributes attrs = mFeature.attributes();
280  attrs.insert( idx, QVariant( layer()->pendingFields()[idx].type() ) );
281  mFeature.setFields( layer()->pendingFields() );
282  mFeature.setAttributes( attrs );
283  }
284  init();
285  setFeature( mFeature );
286 }
287 
288 void QgsAttributeForm::onAttributeDeleted( int idx )
289 {
290  if ( mFeature.isValid() )
291  {
292  QgsAttributes attrs = mFeature.attributes();
293  attrs.remove( idx );
294  mFeature.setFields( layer()->pendingFields() );
295  mFeature.setAttributes( attrs );
296  }
297  init();
298  setFeature( mFeature );
299 }
300 
302 {
303  if ( mLayer->isEditable() || !mFeature.isValid() )
304  return;
305 
306  // reload feature if layer changed although not editable
307  // (datasource probably changed bypassing QgsVectorLayer)
308  if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( mFeature ) )
309  return;
310 
311  init();
312  setFeature( mFeature );
313 }
314 
315 void QgsAttributeForm::synchronizeEnabledState()
316 {
317  bool isEditable = ( mFeature.isValid() || mIsAddDialog ) && mLayer->isEditable();
318 
319  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
320  {
321  bool fieldEditable = true;
322  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
323  if ( eww )
324  {
325  fieldEditable = mLayer->fieldEditable( eww->fieldIdx() );
326  }
327  ww->setEnabled( isEditable && fieldEditable );
328  }
329 
330  QPushButton* okButton = mButtonBox->button( QDialogButtonBox::Ok );
331  if ( okButton )
332  okButton->setEnabled( isEditable );
333 }
334 
335 void QgsAttributeForm::init()
336 {
337  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
338 
339  // Cleanup of any previously shown widget, we start from scratch
340  QWidget* formWidget = 0;
341 
342  bool buttonBoxVisible = true;
343  // Cleanup button box but preserve visibility
344  if ( mButtonBox )
345  {
346  buttonBoxVisible = mButtonBox->isVisible();
347  delete mButtonBox;
348  mButtonBox = 0;
349  }
350 
351  qDeleteAll( mWidgets );
352  mWidgets.clear();
353 
354  while ( QWidget* w = this->findChild<QWidget*>() )
355  {
356  delete w;
357  }
358  delete layout();
359 
360  // Get a layout
361  setLayout( new QGridLayout( this ) );
362 
363  // Try to load Ui-File for layout
364  if ( mLayer->editorLayout() == QgsVectorLayer::UiFileLayout && !mLayer->editForm().isEmpty() )
365  {
366  QFile file( mLayer->editForm() );
367 
368  if ( file.open( QFile::ReadOnly ) )
369  {
370  QUiLoader loader;
371 
372  QFileInfo fi( mLayer->editForm() );
373  loader.setWorkingDirectory( fi.dir() );
374  formWidget = loader.load( &file, this );
375  formWidget->setWindowFlags( Qt::Widget );
376  layout()->addWidget( formWidget );
377  formWidget->show();
378  file.close();
379  mButtonBox = findChild<QDialogButtonBox*>();
380  createWrappers();
381 
382  formWidget->installEventFilter( this );
383  }
384  }
385 
386  // Tab layout
387  if ( !formWidget && mLayer->editorLayout() == QgsVectorLayer::TabLayout )
388  {
389  QTabWidget* tabWidget = new QTabWidget();
390  layout()->addWidget( tabWidget );
391 
392  Q_FOREACH ( QgsAttributeEditorElement *widgDef, mLayer->attributeEditorElements() )
393  {
394  QWidget* tabPage = new QWidget( tabWidget );
395 
396  tabWidget->addTab( tabPage, widgDef->name() );
397  QGridLayout* tabPageLayout = new QGridLayout();
398  tabPage->setLayout( tabPageLayout );
399 
401  {
402  QgsAttributeEditorContainer* containerDef = dynamic_cast<QgsAttributeEditorContainer*>( widgDef );
403  if ( !containerDef )
404  continue;
405 
406  containerDef->setIsGroupBox( false ); // Toplevel widgets are tabs not groupboxes
407  QString dummy1;
408  bool dummy2;
409  tabPageLayout->addWidget( createWidgetFromDef( widgDef, tabPage, mLayer, mContext, dummy1, dummy2 ) );
410  }
411  else
412  {
413  QgsDebugMsg( "No support for fields in attribute editor on top level" );
414  }
415  }
416  formWidget = tabWidget;
417  }
418 
419  // Autogenerate Layout
420  // If there is still no layout loaded (defined as autogenerate or other methods failed)
421  if ( !formWidget )
422  {
423  formWidget = new QWidget( this );
424  QGridLayout* gridLayout = new QGridLayout( formWidget );
425  formWidget->setLayout( gridLayout );
426 
427  // put the form into a scroll area to nicely handle cases with lots of attributes
428 
429  QScrollArea* scrollArea = new QScrollArea( this );
430  scrollArea->setWidget( formWidget );
431  scrollArea->setWidgetResizable( true );
432  scrollArea->setFrameShape( QFrame::NoFrame );
433  scrollArea->setFrameShadow( QFrame::Plain );
434  scrollArea->setFocusProxy( this );
435  layout()->addWidget( scrollArea );
436 
437  int row = 0;
438  Q_FOREACH ( const QgsField& field, mLayer->pendingFields().toList() )
439  {
440  int idx = mLayer->fieldNameIndex( field.name() );
441  if ( idx < 0 )
442  continue;
443 
444  //show attribute alias if available
445  QString fieldName = mLayer->attributeDisplayName( idx );
446 
447  const QString widgetType = mLayer->editorWidgetV2( idx );
448 
449  if ( widgetType == "Hidden" )
450  continue;
451 
452  const QgsEditorWidgetConfig widgetConfig = mLayer->editorWidgetV2Config( idx );
453  bool labelOnTop = mLayer->labelOnTop( idx );
454 
455  // This will also create the widget
456  QWidget *l = new QLabel( fieldName );
457  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, idx, widgetConfig, 0, this, mContext );
458  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 ) );
459 
460  if ( w )
461  w->setObjectName( field.name() );
462 
463  if ( eww )
464  addWidgetWrapper( eww );
465 
466  if ( labelOnTop )
467  {
468  gridLayout->addWidget( l, row++, 0, 1, 2 );
469  gridLayout->addWidget( w, row++, 0, 1, 2 );
470  }
471  else
472  {
473  gridLayout->addWidget( l, row, 0 );
474  gridLayout->addWidget( w, row++, 1 );
475  }
476  }
477 
478  Q_FOREACH ( const QgsRelation& rel, QgsProject::instance()->relationManager()->referencedRelations( mLayer ) )
479  {
480  QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, rel, 0, this );
481  rww->setContext( mContext );
482  gridLayout->addWidget( rww->widget(), row++, 0, 1, 2 );
483  mWidgets.append( rww );
484  }
485  }
486 
487  if ( !mButtonBox )
488  {
489  mButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
490  mButtonBox->setObjectName( "buttonBox" );
491  layout()->addWidget( mButtonBox );
492  }
493 
494  mButtonBox->setVisible( buttonBoxVisible );
495 
496  connectWrappers();
497 
498  connect( mButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
499  connect( mButtonBox, SIGNAL( rejected() ), this, SLOT( resetValues() ) );
500 
501  connect( mLayer, SIGNAL( editingStarted() ), this, SLOT( synchronizeEnabledState() ) );
502  connect( mLayer, SIGNAL( editingStopped() ), this, SLOT( synchronizeEnabledState() ) );
503 
504  Q_FOREACH ( QgsAttributeFormInterface* iface, mInterfaces )
505  {
506  iface->initForm();
507  }
509 }
510 
511 void QgsAttributeForm::cleanPython()
512 {
513  if ( !mPyFormVarName.isNull() )
514  {
515  QString expr = QString( "if locals().has_key('%1'): del %1\n" ).arg( mPyFormVarName );
516  QgsPythonRunner::run( expr );
517  }
518 }
519 
520 void QgsAttributeForm::initPython()
521 {
522  cleanPython();
523 
524  // Init Python
525  if ( !mLayer->editFormInit().isEmpty() )
526  {
527  QString module = mLayer->editFormInit();
528 
529  int pos = module.lastIndexOf( "." );
530  if ( pos >= 0 )
531  {
532  QgsPythonRunner::run( QString( "import %1" ).arg( module.left( pos ) ) );
533  }
534 
535  /* Reload the module if the DEBUGMODE switch has been set in the module.
536  If set to False you have to reload QGIS to reset it to True due to Python
537  module caching */
538  QString reload = QString( "if hasattr(%1,'DEBUGMODE') and %1.DEBUGMODE:"
539  " reload(%1)" ).arg( module.left( pos ) );
540 
541  QgsPythonRunner::run( reload );
542 
543  QgsPythonRunner::run( "import inspect" );
544  QString numArgs;
545  QgsPythonRunner::eval( QString( "len(inspect.getargspec(%1)[0])" ).arg( module ), numArgs );
546 
547  static int sFormId = 0;
548  mPyFormVarName = QString( "_qgis_featureform_%1_%2" ).arg( mFormNr ).arg( sFormId++ );
549 
550  QString form = QString( "%1 = sip.wrapinstance( %2, qgis.gui.QgsAttributeForm )" )
551  .arg( mPyFormVarName )
552  .arg(( unsigned long ) this );
553 
554  QgsPythonRunner::run( form );
555 
556  QgsDebugMsg( QString( "running featureForm init: %1" ).arg( mPyFormVarName ) );
557 
558  // Legacy
559  if ( numArgs == "3" )
560  {
561  addInterface( new QgsAttributeFormLegacyInterface( module, mPyFormVarName, this ) );
562  }
563  else
564  {
565 #if 0
566  QString expr = QString( "%1(%2)" )
567  .arg( mLayer->editFormInit() )
568  .arg( mPyFormVarName );
569  QgsAttributeFormInterface* iface = QgsPythonRunner::evalToSipObject<QgsAttributeFormInterface*>( expr, "QgsAttributeFormInterface" );
570  if ( iface )
571  addInterface( iface );
572 #endif
573  }
574  }
575 }
576 
577 QWidget* QgsAttributeForm::createWidgetFromDef( const QgsAttributeEditorElement *widgetDef, QWidget *parent, QgsVectorLayer *vl, QgsAttributeEditorContext &context, QString &labelText, bool &labelOnTop )
578 {
579  QWidget *newWidget = 0;
580 
581  switch ( widgetDef->type() )
582  {
584  {
585  const QgsAttributeEditorField* fieldDef = dynamic_cast<const QgsAttributeEditorField*>( widgetDef );
586  if ( !fieldDef )
587  break;
588 
589  int fldIdx = vl->fieldNameIndex( fieldDef->name() );
590  if ( fldIdx < vl->pendingFields().count() && fldIdx >= 0 )
591  {
592  const QString widgetType = mLayer->editorWidgetV2( fldIdx );
593  const QgsEditorWidgetConfig widgetConfig = mLayer->editorWidgetV2Config( fldIdx );
594 
595  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, fldIdx, widgetConfig, 0, this, mContext );
596  newWidget = eww->widget();
597  addWidgetWrapper( eww );
598 
599  newWidget->setObjectName( mLayer->pendingFields()[ fldIdx ].name() );
600  }
601 
602  labelOnTop = mLayer->labelOnTop( fieldDef->idx() );
603  labelText = mLayer->attributeDisplayName( fieldDef->idx() );
604 
605  break;
606  }
607 
609  {
610  const QgsAttributeEditorRelation* relDef = dynamic_cast<const QgsAttributeEditorRelation*>( widgetDef );
611 
612  QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, relDef->relation(), 0, this );
613  rww->setContext( context );
614  newWidget = rww->widget();
615  mWidgets.append( rww );
616  labelText = QString::null;
617  labelOnTop = true;
618  break;
619  }
620 
622  {
623  const QgsAttributeEditorContainer* container = dynamic_cast<const QgsAttributeEditorContainer*>( widgetDef );
624  if ( !container )
625  break;
626 
627  QWidget* myContainer;
628  if ( container->isGroupBox() )
629  {
630  QGroupBox* groupBox = new QGroupBox( parent );
631  groupBox->setTitle( container->name() );
632  myContainer = groupBox;
633  newWidget = myContainer;
634  }
635  else
636  {
637  QScrollArea *scrollArea = new QScrollArea( parent );
638 
639  myContainer = new QWidget( scrollArea );
640 
641  scrollArea->setWidget( myContainer );
642  scrollArea->setWidgetResizable( true );
643  scrollArea->setFrameShape( QFrame::NoFrame );
644 
645  newWidget = scrollArea;
646  }
647 
648  QGridLayout* gbLayout = new QGridLayout();
649  myContainer->setLayout( gbLayout );
650 
651  int index = 0;
652 
654 
655  Q_FOREACH ( QgsAttributeEditorElement* childDef, children )
656  {
657  QString labelText;
658  bool labelOnTop;
659  QWidget* editor = createWidgetFromDef( childDef, myContainer, vl, context, labelText, labelOnTop );
660 
661  if ( labelText.isNull() )
662  {
663  gbLayout->addWidget( editor, index, 0, 1, 2 );
664  }
665  else
666  {
667  QLabel* mypLabel = new QLabel( labelText );
668  if ( labelOnTop )
669  {
670  gbLayout->addWidget( mypLabel, index, 0, 1, 2 );
671  ++index;
672  gbLayout->addWidget( editor, index, 0, 1, 2 );
673  }
674  else
675  {
676  gbLayout->addWidget( mypLabel, index, 0 );
677  gbLayout->addWidget( editor, index, 1 );
678  }
679  }
680 
681  ++index;
682  }
683  QWidget* spacer = new QWidget();
684  spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
685  gbLayout->addWidget( spacer, index, 0 );
686 
687  labelText = QString::null;
688  labelOnTop = true;
689  break;
690  }
691 
692  default:
693  QgsDebugMsg( "Unknown attribute editor widget type encountered..." );
694  break;
695  }
696 
697  return newWidget;
698 }
699 
700 void QgsAttributeForm::addWidgetWrapper( QgsEditorWidgetWrapper* eww )
701 {
702  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
703  {
704  QgsEditorWidgetWrapper* meww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
705  if ( meww )
706  {
707  if ( meww->field() == eww->field() )
708  {
709  connect( meww, SIGNAL( valueChanged( QVariant ) ), eww, SLOT( setValue( QVariant ) ) );
710  connect( eww, SIGNAL( valueChanged( QVariant ) ), meww, SLOT( setValue( QVariant ) ) );
711  break;
712  }
713  }
714  }
715 
716  mWidgets.append( eww );
717 }
718 
719 void QgsAttributeForm::createWrappers()
720 {
721  QList<QWidget*> myWidgets = findChildren<QWidget*>();
722  const QList<QgsField> fields = mLayer->pendingFields().toList();
723 
724  Q_FOREACH ( QWidget* myWidget, myWidgets )
725  {
726  // Check the widget's properties for a relation definition
727  QVariant vRel = myWidget->property( "qgisRelation" );
728  if ( vRel.isValid() )
729  {
731  QgsRelation relation = relMgr->relation( vRel.toString() );
732  if ( relation.isValid() )
733  {
734  QgsRelationWidgetWrapper* rww = new QgsRelationWidgetWrapper( mLayer, relation, myWidget, this );
736  rww->setContext( mContext );
737  rww->widget(); // Will initialize the widget
738  mWidgets.append( rww );
739  }
740  }
741  else
742  {
743  Q_FOREACH ( const QgsField& field, fields )
744  {
745  if ( field.name() == myWidget->objectName() )
746  {
747  const QString widgetType = mLayer->editorWidgetV2( field.name() );
748  const QgsEditorWidgetConfig widgetConfig = mLayer->editorWidgetV2Config( field.name() );
749  int idx = mLayer->fieldNameIndex( field.name() );
750 
751  QgsEditorWidgetWrapper* eww = QgsEditorWidgetRegistry::instance()->create( widgetType, mLayer, idx, widgetConfig, myWidget, this, mContext );
752  addWidgetWrapper( eww );
753  }
754  }
755  }
756  }
757 }
758 
759 void QgsAttributeForm::connectWrappers()
760 {
761  bool isFirstEww = true;
762 
763  Q_FOREACH ( QgsWidgetWrapper* ww, mWidgets )
764  {
765  QgsEditorWidgetWrapper* eww = qobject_cast<QgsEditorWidgetWrapper*>( ww );
766 
767  if ( eww )
768  {
769  if ( isFirstEww )
770  {
771  setFocusProxy( eww->widget() );
772  isFirstEww = false;
773  }
774 
775  connect( eww, SIGNAL( valueChanged( const QVariant& ) ), this, SLOT( onAttributeChanged( const QVariant& ) ) );
776  }
777  }
778 }
779 
780 
782 {
783  Q_UNUSED( object )
784 
785  if ( e->type() == QEvent::KeyPress )
786  {
787  QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>( e );
788  if ( keyEvent && keyEvent->key() == Qt::Key_Escape )
789  {
790  // Re-emit to this form so it will be forwarded to parent
791  event( e );
792  return true;
793  }
794  }
795 
796  return false;
797 }
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.
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.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
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)