QGIS API Documentation  3.17.0-Master (a84647cf30)
qgslayoutattributetablewidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutattributetablewidget.cpp
3  ---------------------------------
4  begin : November 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 #include "qgslayoutatlas.h"
20 #include "qgslayout.h"
21 #include "qgslayoutframe.h"
23 #include "qgslayoutitemwidget.h"
25 #include "qgslayouttablecolumn.h"
26 #include "qgslayoutitemmap.h"
27 #include "qgsvectorlayer.h"
29 #include "qgsproject.h"
30 #include "qgsrelationmanager.h"
31 #include "qgsguiutils.h"
33 
35  : QgsLayoutItemBaseWidget( nullptr, frame ? qobject_cast< QgsLayoutItemAttributeTable* >( frame->multiFrame() ) : nullptr )
36  , mTable( frame ? qobject_cast< QgsLayoutItemAttributeTable* >( frame->multiFrame() ) : nullptr )
37  , mFrame( frame )
38 {
39  setupUi( this );
40  connect( mRefreshPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeTableWidget::mRefreshPushButton_clicked );
41  connect( mAttributesPushButton, &QPushButton::clicked, this, &QgsLayoutAttributeTableWidget::mAttributesPushButton_clicked );
42  connect( mMaximumRowsSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsLayoutAttributeTableWidget::mMaximumRowsSpinBox_valueChanged );
43  connect( mMarginSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutAttributeTableWidget::mMarginSpinBox_valueChanged );
44  connect( mGridStrokeWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutAttributeTableWidget::mGridStrokeWidthSpinBox_valueChanged );
45  connect( mGridColorButton, &QgsColorButton::colorChanged, this, &QgsLayoutAttributeTableWidget::mGridColorButton_colorChanged );
46  connect( mBackgroundColorButton, &QgsColorButton::colorChanged, this, &QgsLayoutAttributeTableWidget::mBackgroundColorButton_colorChanged );
47  connect( mDrawHorizontalGrid, &QCheckBox::toggled, this, &QgsLayoutAttributeTableWidget::mDrawHorizontalGrid_toggled );
48  connect( mDrawVerticalGrid, &QCheckBox::toggled, this, &QgsLayoutAttributeTableWidget::mDrawVerticalGrid_toggled );
49  connect( mShowGridGroupCheckBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutAttributeTableWidget::mShowGridGroupCheckBox_toggled );
50  connect( mShowOnlyVisibleFeaturesCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutAttributeTableWidget::mShowOnlyVisibleFeaturesCheckBox_stateChanged );
51  connect( mFeatureFilterCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutAttributeTableWidget::mFeatureFilterCheckBox_stateChanged );
52  connect( mFeatureFilterEdit, &QLineEdit::editingFinished, this, &QgsLayoutAttributeTableWidget::mFeatureFilterEdit_editingFinished );
53  connect( mFeatureFilterButton, &QToolButton::clicked, this, &QgsLayoutAttributeTableWidget::mFeatureFilterButton_clicked );
54  connect( mHeaderHAlignmentComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutAttributeTableWidget::mHeaderHAlignmentComboBox_currentIndexChanged );
55  connect( mHeaderModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutAttributeTableWidget::mHeaderModeComboBox_currentIndexChanged );
56  connect( mWrapStringLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutAttributeTableWidget::mWrapStringLineEdit_editingFinished );
57  connect( mAddFramePushButton, &QPushButton::clicked, this, &QgsLayoutAttributeTableWidget::mAddFramePushButton_clicked );
58  connect( mResizeModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutAttributeTableWidget::mResizeModeComboBox_currentIndexChanged );
59  connect( mSourceComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutAttributeTableWidget::mSourceComboBox_currentIndexChanged );
60  connect( mRelationsComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutAttributeTableWidget::mRelationsComboBox_currentIndexChanged );
61  connect( mEmptyModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutAttributeTableWidget::mEmptyModeComboBox_currentIndexChanged );
62  connect( mDrawEmptyCheckBox, &QCheckBox::toggled, this, &QgsLayoutAttributeTableWidget::mDrawEmptyCheckBox_toggled );
63  connect( mEmptyMessageLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutAttributeTableWidget::mEmptyMessageLineEdit_editingFinished );
64  connect( mIntersectAtlasCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutAttributeTableWidget::mIntersectAtlasCheckBox_stateChanged );
65  connect( mUniqueOnlyCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutAttributeTableWidget::mUniqueOnlyCheckBox_stateChanged );
66  connect( mEmptyFrameCheckBox, &QCheckBox::toggled, this, &QgsLayoutAttributeTableWidget::mEmptyFrameCheckBox_toggled );
67  connect( mHideEmptyBgCheckBox, &QCheckBox::toggled, this, &QgsLayoutAttributeTableWidget::mHideEmptyBgCheckBox_toggled );
68  connect( mWrapBehaviorComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutAttributeTableWidget::mWrapBehaviorComboBox_currentIndexChanged );
69  connect( mAdvancedCustomizationButton, &QPushButton::clicked, this, &QgsLayoutAttributeTableWidget::mAdvancedCustomizationButton_clicked );
70  connect( mUseConditionalStylingCheckBox, &QCheckBox::stateChanged, this, &QgsLayoutAttributeTableWidget::useConditionalStylingChanged );
71  setPanelTitle( tr( "Table Properties" ) );
72 
73  mContentFontToolButton->setMode( QgsFontButton::ModeTextRenderer );
74  mHeaderFontToolButton->setMode( QgsFontButton::ModeTextRenderer );
75 
76  blockAllSignals( true );
77 
78  mResizeModeComboBox->addItem( tr( "Use Existing Frames" ), QgsLayoutMultiFrame::UseExistingFrames );
79  mResizeModeComboBox->addItem( tr( "Extend to Next Page" ), QgsLayoutMultiFrame::ExtendToNextPage );
80  mResizeModeComboBox->addItem( tr( "Repeat Until Finished" ), QgsLayoutMultiFrame::RepeatUntilFinished );
81 
82  mEmptyModeComboBox->addItem( tr( "Draw Headers Only" ), QgsLayoutTable::HeadersOnly );
83  mEmptyModeComboBox->addItem( tr( "Hide Entire Table" ), QgsLayoutTable::HideTable );
84  mEmptyModeComboBox->addItem( tr( "Show Set Message" ), QgsLayoutTable::ShowMessage );
85 
86  mWrapBehaviorComboBox->addItem( tr( "Truncate Text" ), QgsLayoutTable::TruncateText );
87  mWrapBehaviorComboBox->addItem( tr( "Wrap Text" ), QgsLayoutTable::WrapText );
88 
89  mHeaderModeComboBox->addItem( tr( "On First Frame" ), QgsLayoutTable::FirstFrame );
90  mHeaderModeComboBox->addItem( tr( "On All Frames" ), QgsLayoutTable::AllFrames );
91  mHeaderModeComboBox->addItem( tr( "No Header" ), QgsLayoutTable::NoHeaders );
92 
93  mHeaderHAlignmentComboBox->addItem( tr( "Follow Column Alignment" ), QgsLayoutTable::FollowColumn );
94  mHeaderHAlignmentComboBox->addItem( tr( "Left" ), QgsLayoutTable::HeaderLeft );
95  mHeaderHAlignmentComboBox->addItem( tr( "Center" ), QgsLayoutTable::HeaderCenter );
96  mHeaderHAlignmentComboBox->addItem( tr( "Right" ), QgsLayoutTable::HeaderRight );
97 
98  mSourceComboBox->addItem( tr( "Layer Features" ), QgsLayoutItemAttributeTable::LayerAttributes );
99  toggleAtlasSpecificControls( static_cast< bool >( coverageLayer() ) );
100 
101  //update relations combo when relations modified in project
102  connect( QgsProject::instance()->relationManager(), &QgsRelationManager::changed, this, &QgsLayoutAttributeTableWidget::updateRelationsCombo );
103 
104  mLayerComboBox->setFilters( QgsMapLayerProxyModel::VectorLayer );
105  connect( mLayerComboBox, &QgsMapLayerComboBox::layerChanged, this, &QgsLayoutAttributeTableWidget::changeLayer );
106 
107  mComposerMapComboBox->setCurrentLayout( mTable->layout() );
108  mComposerMapComboBox->setItemType( QgsLayoutItemRegistry::LayoutMap );
109  connect( mComposerMapComboBox, &QgsLayoutItemComboBox::itemChanged, this, &QgsLayoutAttributeTableWidget::composerMapChanged );
110 
111  mGridColorButton->setColorDialogTitle( tr( "Select Grid Color" ) );
112  mGridColorButton->setAllowOpacity( true );
113  mGridColorButton->setContext( QStringLiteral( "composer" ) );
114  mGridColorButton->setDefaultColor( Qt::black );
115  mBackgroundColorButton->setColorDialogTitle( tr( "Select Background Color" ) );
116  mBackgroundColorButton->setAllowOpacity( true );
117  mBackgroundColorButton->setContext( QStringLiteral( "composer" ) );
118  mBackgroundColorButton->setShowNoColor( true );
119  mBackgroundColorButton->setNoColorString( tr( "No Background" ) );
120 
121  updateGuiElements();
122 
123  if ( mTable )
124  {
125  connect( mTable, &QgsLayoutMultiFrame::changed, this, &QgsLayoutAttributeTableWidget::updateGuiElements );
126 
127  // repopulate relations combo box if atlas layer changes
128  connect( &mTable->layout()->reportContext(), &QgsLayoutReportContext::layerChanged,
129  this, &QgsLayoutAttributeTableWidget::atlasToggled );
130 
131  if ( QgsLayoutAtlas *atlas = layoutAtlas() )
132  {
133  connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutAttributeTableWidget::atlasToggled );
134  atlasToggled();
135  }
136 
137  mLayerSourceDDBtn->registerExpressionContextGenerator( mTable );
138  }
139 
141 
142  //embed widget for general options
143  if ( mFrame )
144  {
145  //add widget for general composer item properties
146  mItemPropertiesWidget = new QgsLayoutItemPropertiesWidget( this, mFrame );
147  mainLayout->addWidget( mItemPropertiesWidget );
148  }
149 
150  connect( mHeaderFontToolButton, &QgsFontButton::changed, this, &QgsLayoutAttributeTableWidget::headerFontChanged );
151  connect( mContentFontToolButton, &QgsFontButton::changed, this, &QgsLayoutAttributeTableWidget::contentFontChanged );
152 }
153 
155 {
156  mIntersectAtlasCheckBox->setText( tr( "Show only features intersecting %1 feature" ).arg( string ) );
157  const int atlasFeatureIndex = mSourceComboBox->findData( QgsLayoutItemAttributeTable::AtlasFeature );
158  if ( atlasFeatureIndex != -1 )
159  {
160  mSourceComboBox->setItemText( atlasFeatureIndex, tr( "Current %1 Feature" ).arg( string ) );
161  }
162 }
163 
165 {
166  if ( mItemPropertiesWidget )
167  mItemPropertiesWidget->setMasterLayout( masterLayout );
168 }
169 
171 {
172  QgsLayoutFrame *frame = qobject_cast< QgsLayoutFrame * >( item );
173  if ( !frame )
174  return false;
175 
176  QgsLayoutMultiFrame *multiFrame = frame->multiFrame();
177  if ( !multiFrame )
178  return false;
179 
180  if ( multiFrame->type() != QgsLayoutItemRegistry::LayoutAttributeTable )
181  return false;
182 
183  if ( mTable )
184  {
185  disconnect( mTable, &QgsLayoutObject::changed, this, &QgsLayoutAttributeTableWidget::updateGuiElements );
186  }
187 
188  mTable = qobject_cast< QgsLayoutItemAttributeTable * >( multiFrame );
189  mFrame = frame;
190  mItemPropertiesWidget->setItem( frame );
191 
192  if ( mTable )
193  {
194  connect( mTable, &QgsLayoutObject::changed, this, &QgsLayoutAttributeTableWidget::updateGuiElements );
195  }
196 
197  updateGuiElements();
198 
199  return true;
200 }
201 
202 
203 void QgsLayoutAttributeTableWidget::mRefreshPushButton_clicked()
204 {
205  if ( !mTable )
206  {
207  return;
208  }
209 
210  mTable->refreshAttributes();
211 }
212 
213 void QgsLayoutAttributeTableWidget::mAttributesPushButton_clicked()
214 {
215  if ( !mTable )
216  {
217  return;
218  }
219 
220  //make deep copy of current columns, so we can restore them in case of cancellation
221  QVector<QgsLayoutTableColumn> currentColumns = mTable->columns();
222  QVector<QgsLayoutTableColumn> currentSortColumns = mTable->sortColumns();
223 
224  mTable->beginCommand( tr( "Change Table Attributes" ) );
225 
226  //temporarily block updates for the window, to stop table trying to repaint under windows (#11462)
227  window()->setUpdatesEnabled( false );
228 
229  QgsLayoutAttributeSelectionDialog d( mTable, mTable->sourceLayer(), this );
230  if ( d.exec() == QDialog::Accepted )
231  {
232  mTable->refreshAttributes();
233  //safe to unblock updates
234  window()->setUpdatesEnabled( true );
235  mTable->update();
236  mTable->endCommand();
237 
238  //clear currentColumns to free memory
239  currentColumns.clear();
240  currentSortColumns.clear();
241  }
242  else
243  {
244  //undo changes
245  mTable->setColumns( currentColumns );
246  mTable->setSortColumns( currentSortColumns );
247  window()->setUpdatesEnabled( true );
248  mTable->cancelCommand();
249  }
250 }
251 
252 void QgsLayoutAttributeTableWidget::composerMapChanged( QgsLayoutItem *item )
253 {
254  if ( !mTable )
255  {
256  return;
257  }
258 
259  mTable->beginCommand( tr( "Change Table Map" ) );
260  mTable->setMap( qobject_cast< QgsLayoutItemMap * >( item ) );
261  mTable->update();
262  mTable->endCommand();
263 }
264 
265 void QgsLayoutAttributeTableWidget::mMaximumRowsSpinBox_valueChanged( int i )
266 {
267  if ( !mTable )
268  {
269  return;
270  }
271 
272  mTable->beginCommand( tr( "Change Table Rows" ), QgsLayoutMultiFrame::UndoTableMaximumFeatures );
273  mTable->setMaximumNumberOfFeatures( i );
274  mTable->update();
275  mTable->endCommand();
276 }
277 
278 void QgsLayoutAttributeTableWidget::mMarginSpinBox_valueChanged( double d )
279 {
280  if ( !mTable )
281  {
282  return;
283  }
284 
285  mTable->beginCommand( tr( "Change Table Margin" ), QgsLayoutMultiFrame::UndoTableMargin );
286  mTable->setCellMargin( d );
287  mTable->endCommand();
288 }
289 
290 void QgsLayoutAttributeTableWidget::headerFontChanged()
291 {
292  if ( !mTable )
293  return;
294 
295  mTable->beginCommand( tr( "Change Table Text Format" ) );
296  mTable->setHeaderTextFormat( mHeaderFontToolButton->textFormat() );
297  mTable->endCommand();
298 }
299 
300 void QgsLayoutAttributeTableWidget::contentFontChanged()
301 {
302  if ( !mTable )
303  {
304  return;
305  }
306 
307  mTable->beginCommand( tr( "Change Table Text Format" ) );
308  mTable->setContentTextFormat( mContentFontToolButton->textFormat() );
309  mTable->endCommand();
310 }
311 
312 void QgsLayoutAttributeTableWidget::mGridStrokeWidthSpinBox_valueChanged( double d )
313 {
314  if ( !mTable )
315  {
316  return;
317  }
318 
319  mTable->beginCommand( tr( "Change Table Line Width" ), QgsLayoutMultiFrame::UndoTableGridStrokeWidth );
320  mTable->setGridStrokeWidth( d );
321  mTable->endCommand();
322 }
323 
324 void QgsLayoutAttributeTableWidget::mGridColorButton_colorChanged( const QColor &newColor )
325 {
326  if ( !mTable )
327  {
328  return;
329  }
330 
331  mTable->beginCommand( tr( "Change Table Grid Color" ), QgsLayoutMultiFrame::UndoTableGridColor );
332  mTable->setGridColor( newColor );
333  mTable->endCommand();
334 }
335 
336 void QgsLayoutAttributeTableWidget::mDrawHorizontalGrid_toggled( bool state )
337 {
338  if ( !mTable )
339  {
340  return;
341  }
342 
343  mTable->beginCommand( tr( "Toggle Table Grid" ) );
344  mTable->setHorizontalGrid( state );
345  mTable->endCommand();
346 }
347 
348 void QgsLayoutAttributeTableWidget::mDrawVerticalGrid_toggled( bool state )
349 {
350  if ( !mTable )
351  {
352  return;
353  }
354 
355  mTable->beginCommand( tr( "Toggled Table Grid" ) );
356  mTable->setVerticalGrid( state );
357  mTable->endCommand();
358 }
359 
360 void QgsLayoutAttributeTableWidget::mShowGridGroupCheckBox_toggled( bool state )
361 {
362  if ( !mTable )
363  {
364  return;
365  }
366 
367  mTable->beginCommand( tr( "Toggle Table Grid" ) );
368  mTable->setShowGrid( state );
369  mTable->endCommand();
370 }
371 
372 void QgsLayoutAttributeTableWidget::mBackgroundColorButton_colorChanged( const QColor &newColor )
373 {
374  if ( !mTable )
375  {
376  return;
377  }
378 
379  mTable->beginCommand( tr( "Change Table Color" ), QgsLayoutMultiFrame::UndoTableBackgroundColor );
380  mTable->setBackgroundColor( newColor );
381  mTable->endCommand();
382 }
383 
384 void QgsLayoutAttributeTableWidget::updateGuiElements()
385 {
386  if ( !mTable || !mFrame )
387  {
388  return;
389  }
390 
391  blockAllSignals( true );
392 
393  mSourceComboBox->setCurrentIndex( mSourceComboBox->findData( mTable->source() ) );
394  mRelationsComboBox->setCurrentIndex( mRelationsComboBox->findData( mTable->relationId() ) );
395 
396  //layer combo box
397  if ( mTable->vectorLayer() )
398  {
399  mLayerComboBox->setLayer( mTable->vectorLayer() );
400  if ( mTable->vectorLayer()->geometryType() == QgsWkbTypes::NullGeometry )
401  {
402  //layer has no geometry, so uncheck & disable controls which require geometry
403  mShowOnlyVisibleFeaturesCheckBox->setChecked( false );
404  mShowOnlyVisibleFeaturesCheckBox->setEnabled( false );
405  }
406  else
407  {
408  mShowOnlyVisibleFeaturesCheckBox->setEnabled( true );
409  }
410  }
411 
412  mComposerMapComboBox->setItem( mTable->map() );
413  mMaximumRowsSpinBox->setValue( mTable->maximumNumberOfFeatures() );
414  mMarginSpinBox->setValue( mTable->cellMargin() );
415  mGridStrokeWidthSpinBox->setValue( mTable->gridStrokeWidth() );
416  mGridColorButton->setColor( mTable->gridColor() );
417  mDrawHorizontalGrid->setChecked( mTable->horizontalGrid() );
418  mDrawVerticalGrid->setChecked( mTable->verticalGrid() );
419  if ( mTable->showGrid() )
420  {
421  mShowGridGroupCheckBox->setChecked( true );
422  }
423  else
424  {
425  mShowGridGroupCheckBox->setChecked( false );
426  }
427  mBackgroundColorButton->setColor( mTable->backgroundColor() );
428 
429  mHeaderFontToolButton->setTextFormat( mTable->headerTextFormat() );
430  mContentFontToolButton->setTextFormat( mTable->contentTextFormat() );
431 
432  if ( mTable->displayOnlyVisibleFeatures() && mShowOnlyVisibleFeaturesCheckBox->isEnabled() )
433  {
434  mShowOnlyVisibleFeaturesCheckBox->setCheckState( Qt::Checked );
435  mComposerMapComboBox->setEnabled( true );
436  mComposerMapLabel->setEnabled( true );
437  }
438  else
439  {
440  mShowOnlyVisibleFeaturesCheckBox->setCheckState( Qt::Unchecked );
441  mComposerMapComboBox->setEnabled( false );
442  mComposerMapLabel->setEnabled( false );
443  }
444 
445  mUniqueOnlyCheckBox->setChecked( mTable->uniqueRowsOnly() );
446  mIntersectAtlasCheckBox->setChecked( mTable->filterToAtlasFeature() );
447  mFeatureFilterEdit->setText( mTable->featureFilter() );
448  mFeatureFilterCheckBox->setCheckState( mTable->filterFeatures() ? Qt::Checked : Qt::Unchecked );
449  mFeatureFilterEdit->setEnabled( mTable->filterFeatures() );
450  mFeatureFilterButton->setEnabled( mTable->filterFeatures() );
451  mUseConditionalStylingCheckBox->setChecked( mTable->useConditionalStyling() );
452 
453  mHeaderHAlignmentComboBox->setCurrentIndex( mHeaderHAlignmentComboBox->findData( mTable->headerHAlignment() ) );
454  mHeaderModeComboBox->setCurrentIndex( mHeaderModeComboBox->findData( mTable->headerMode() ) );
455 
456  mEmptyModeComboBox->setCurrentIndex( mEmptyModeComboBox->findData( mTable->emptyTableBehavior() ) );
457  mEmptyMessageLineEdit->setText( mTable->emptyTableMessage() );
458  mEmptyMessageLineEdit->setEnabled( mTable->emptyTableBehavior() == QgsLayoutTable::ShowMessage );
459  mEmptyMessageLabel->setEnabled( mTable->emptyTableBehavior() == QgsLayoutTable::ShowMessage );
460  mDrawEmptyCheckBox->setChecked( mTable->showEmptyRows() );
461  mWrapStringLineEdit->setText( mTable->wrapString() );
462  mWrapBehaviorComboBox->setCurrentIndex( mWrapBehaviorComboBox->findData( mTable->wrapBehavior() ) );
463 
464  mResizeModeComboBox->setCurrentIndex( mResizeModeComboBox->findData( mTable->resizeMode() ) );
465  mAddFramePushButton->setEnabled( mTable->resizeMode() == QgsLayoutMultiFrame::UseExistingFrames );
466 
467  mEmptyFrameCheckBox->setChecked( mFrame->hidePageIfEmpty() );
468  mHideEmptyBgCheckBox->setChecked( mFrame->hideBackgroundIfEmpty() );
469 
470  updateDataDefinedButton( mLayerSourceDDBtn );
471 
472  toggleSourceControls();
473 
474  blockAllSignals( false );
475 }
476 
477 void QgsLayoutAttributeTableWidget::atlasToggled()
478 {
479  // display/hide atlas options in source combobox depending on atlas status
480  // if there's no atlas but there IS a coverageLayer, it's a report export and we should enable the controls
481  bool atlasEnabled = ( layoutAtlas() && layoutAtlas()->enabled() ) || ( !layoutAtlas() && coverageLayer() );
482 
483 
484  toggleAtlasSpecificControls( atlasEnabled );
485 
486  if ( !mTable )
487  return;
488 
489  whileBlocking( mSourceComboBox )->setCurrentIndex( mSourceComboBox->findData( mTable->source() ) );
490 
491  if ( !atlasEnabled && mTable->filterToAtlasFeature() )
492  {
493  mTable->setFilterToAtlasFeature( false );
494  }
495 }
496 
497 void QgsLayoutAttributeTableWidget::updateRelationsCombo()
498 {
499  mRelationsComboBox->blockSignals( true );
500  mRelationsComboBox->clear();
501 
502  QgsVectorLayer *atlasLayer = coverageLayer();
503  if ( atlasLayer )
504  {
505  const QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( atlasLayer );
506  for ( const QgsRelation &relation : relations )
507  {
508  mRelationsComboBox->addItem( relation.name(), relation.id() );
509  }
510  if ( mTable )
511  {
512  mRelationsComboBox->setCurrentIndex( mRelationsComboBox->findData( mTable->relationId() ) );
513  }
514  }
515 
516  mRelationsComboBox->blockSignals( false );
517 }
518 
519 void QgsLayoutAttributeTableWidget::toggleAtlasSpecificControls( const bool atlasEnabled )
520 {
521  if ( !atlasEnabled )
522  {
523  if ( mTable->source() == QgsLayoutItemAttributeTable::AtlasFeature )
524  {
526  }
527  mSourceComboBox->removeItem( mSourceComboBox->findData( QgsLayoutItemAttributeTable::AtlasFeature ) );
528  mSourceComboBox->removeItem( mSourceComboBox->findData( QgsLayoutItemAttributeTable::RelationChildren ) );
529  mRelationsComboBox->blockSignals( true );
530  mRelationsComboBox->setEnabled( false );
531  mRelationsComboBox->clear();
532  mRelationsComboBox->blockSignals( false );
533  mIntersectAtlasCheckBox->setEnabled( false );
534  }
535  else
536  {
537  if ( mSourceComboBox->findData( QgsLayoutItemAttributeTable::AtlasFeature ) == -1 )
538  {
539  //add missing atlasfeature option to combobox
540  mSourceComboBox->addItem( tr( "Current Atlas Feature" ), QgsLayoutItemAttributeTable::AtlasFeature );
541  }
542  if ( mSourceComboBox->findData( QgsLayoutItemAttributeTable::RelationChildren ) == -1 )
543  {
544  //add missing relation children option to combobox
545  mSourceComboBox->addItem( tr( "Relation Children" ), QgsLayoutItemAttributeTable::RelationChildren );
546  }
547 
548  //add relations for coverage layer
549  updateRelationsCombo();
550  mRelationsComboBox->setEnabled( true );
551  mIntersectAtlasCheckBox->setEnabled( true );
552  }
553 }
554 
555 void QgsLayoutAttributeTableWidget::blockAllSignals( bool b )
556 {
557  mSourceComboBox->blockSignals( b );
558  mLayerComboBox->blockSignals( b );
559  mComposerMapComboBox->blockSignals( b );
560  mMaximumRowsSpinBox->blockSignals( b );
561  mMarginSpinBox->blockSignals( b );
562  mGridColorButton->blockSignals( b );
563  mGridStrokeWidthSpinBox->blockSignals( b );
564  mBackgroundColorButton->blockSignals( b );
565  mDrawHorizontalGrid->blockSignals( b );
566  mDrawVerticalGrid->blockSignals( b );
567  mShowGridGroupCheckBox->blockSignals( b );
568  mShowOnlyVisibleFeaturesCheckBox->blockSignals( b );
569  mUniqueOnlyCheckBox->blockSignals( b );
570  mIntersectAtlasCheckBox->blockSignals( b );
571  mFeatureFilterEdit->blockSignals( b );
572  mFeatureFilterCheckBox->blockSignals( b );
573  mHeaderHAlignmentComboBox->blockSignals( b );
574  mHeaderModeComboBox->blockSignals( b );
575  mResizeModeComboBox->blockSignals( b );
576  mRelationsComboBox->blockSignals( b );
577  mEmptyModeComboBox->blockSignals( b );
578  mEmptyMessageLineEdit->blockSignals( b );
579  mEmptyFrameCheckBox->blockSignals( b );
580  mHideEmptyBgCheckBox->blockSignals( b );
581  mDrawEmptyCheckBox->blockSignals( b );
582  mWrapStringLineEdit->blockSignals( b );
583  mWrapBehaviorComboBox->blockSignals( b );
584  mContentFontToolButton->blockSignals( b );
585  mHeaderFontToolButton->blockSignals( b );
586 }
587 
588 void QgsLayoutAttributeTableWidget::setMaximumNumberOfFeatures( int n )
589 {
590  whileBlocking( mMaximumRowsSpinBox )->setValue( n );
591 }
592 
593 void QgsLayoutAttributeTableWidget::mShowOnlyVisibleFeaturesCheckBox_stateChanged( int state )
594 {
595  if ( !mTable )
596  {
597  return;
598  }
599 
600  mTable->beginCommand( tr( "Toggle Visible Features Only" ) );
601  bool showOnlyVisibleFeatures = ( state == Qt::Checked );
602  mTable->setDisplayOnlyVisibleFeatures( showOnlyVisibleFeatures );
603  mTable->update();
604  mTable->endCommand();
605 
606  //enable/disable map combobox based on state of checkbox
607  mComposerMapComboBox->setEnabled( state == Qt::Checked );
608  mComposerMapLabel->setEnabled( state == Qt::Checked );
609 }
610 
611 void QgsLayoutAttributeTableWidget::mUniqueOnlyCheckBox_stateChanged( int state )
612 {
613  if ( !mTable )
614  {
615  return;
616  }
617 
618  mTable->beginCommand( tr( "Toggle Table Filter Duplicates" ) );
619  mTable->setUniqueRowsOnly( state == Qt::Checked );
620  mTable->update();
621  mTable->endCommand();
622 }
623 
624 void QgsLayoutAttributeTableWidget::mEmptyFrameCheckBox_toggled( bool checked )
625 {
626  if ( !mFrame )
627  {
628  return;
629  }
630 
631  mFrame->beginCommand( tr( "Toggle Empty Frame Mode" ) );
632  mFrame->setHidePageIfEmpty( checked );
633  mFrame->endCommand();
634 }
635 
636 void QgsLayoutAttributeTableWidget::mHideEmptyBgCheckBox_toggled( bool checked )
637 {
638  if ( !mFrame )
639  {
640  return;
641  }
642 
643  mFrame->beginCommand( tr( "Toggle Background Display" ) );
644  mFrame->setHideBackgroundIfEmpty( checked );
645  mFrame->endCommand();
646 }
647 
648 void QgsLayoutAttributeTableWidget::mIntersectAtlasCheckBox_stateChanged( int state )
649 {
650  if ( !mTable )
651  {
652  return;
653  }
654 
655  mTable->beginCommand( tr( "Toggle Table Atlas Filter" ) );
656  bool filterToAtlas = ( state == Qt::Checked );
657  mTable->setFilterToAtlasFeature( filterToAtlas );
658  mTable->update();
659  mTable->endCommand();
660 }
661 
662 void QgsLayoutAttributeTableWidget::mFeatureFilterCheckBox_stateChanged( int state )
663 {
664  if ( !mTable )
665  {
666  return;
667  }
668 
669  if ( state == Qt::Checked )
670  {
671  mFeatureFilterEdit->setEnabled( true );
672  mFeatureFilterButton->setEnabled( true );
673  }
674  else
675  {
676  mFeatureFilterEdit->setEnabled( false );
677  mFeatureFilterButton->setEnabled( false );
678  }
679 
680  mTable->beginCommand( tr( "Toggle Table Feature Filter" ) );
681  mTable->setFilterFeatures( state == Qt::Checked );
682  mTable->update();
683  mTable->endCommand();
684 }
685 
686 void QgsLayoutAttributeTableWidget::mFeatureFilterEdit_editingFinished()
687 {
688  if ( !mTable )
689  {
690  return;
691  }
692 
693  mTable->beginCommand( tr( "Change Table Feature Filter" ) );
694  mTable->setFeatureFilter( mFeatureFilterEdit->text() );
695  mTable->update();
696  mTable->endCommand();
697 }
698 
699 void QgsLayoutAttributeTableWidget::mFeatureFilterButton_clicked()
700 {
701  if ( !mTable )
702  {
703  return;
704  }
705 
706  QgsExpressionContext context = mTable->createExpressionContext();
707  QgsExpressionBuilderDialog exprDlg( mTable->sourceLayer(), mFeatureFilterEdit->text(), this, QStringLiteral( "generic" ), context );
708  exprDlg.setWindowTitle( tr( "Expression Based Filter" ) );
709  if ( exprDlg.exec() == QDialog::Accepted )
710  {
711  QString expression = exprDlg.expressionText();
712  if ( !expression.isEmpty() )
713  {
714  mFeatureFilterEdit->setText( expression );
715  mTable->beginCommand( tr( "Change Table Feature Filter" ) );
716  mTable->setFeatureFilter( mFeatureFilterEdit->text() );
717  mTable->update();
718  mTable->endCommand();
719  }
720  }
721 }
722 
723 void QgsLayoutAttributeTableWidget::mHeaderHAlignmentComboBox_currentIndexChanged( int )
724 {
725  if ( !mTable )
726  {
727  return;
728  }
729 
730  mTable->beginCommand( tr( "Change Table Alignment" ) );
731  mTable->setHeaderHAlignment( static_cast< QgsLayoutTable::HeaderHAlignment >( mHeaderHAlignmentComboBox->currentData().toInt() ) );
732  mTable->endCommand();
733 }
734 
735 void QgsLayoutAttributeTableWidget::mHeaderModeComboBox_currentIndexChanged( int )
736 {
737  if ( !mTable )
738  {
739  return;
740  }
741 
742  mTable->beginCommand( tr( "Change Table Header Mode" ) );
743  mTable->setHeaderMode( static_cast< QgsLayoutTable::HeaderMode >( mHeaderModeComboBox->currentData().toInt() ) );
744  mTable->endCommand();
745 }
746 
747 void QgsLayoutAttributeTableWidget::mWrapStringLineEdit_editingFinished()
748 {
749  if ( !mTable )
750  {
751  return;
752  }
753 
754  mTable->beginCommand( tr( "Change Table Wrap String" ) );
755  mTable->setWrapString( mWrapStringLineEdit->text() );
756  mTable->endCommand();
757 }
758 
759 void QgsLayoutAttributeTableWidget::changeLayer( QgsMapLayer *layer )
760 {
761  if ( !mTable )
762  {
763  return;
764  }
765 
766  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
767  if ( !vl )
768  {
769  return;
770  }
771 
772  mTable->beginCommand( tr( "Change Table Layer" ) );
773  mTable->setVectorLayer( vl );
774  mTable->update();
775  mTable->endCommand();
776 
778  {
779  //layer has no geometry, so uncheck & disable controls which require geometry
780  mShowOnlyVisibleFeaturesCheckBox->setChecked( false );
781  mShowOnlyVisibleFeaturesCheckBox->setEnabled( false );
782  }
783  else
784  {
785  mShowOnlyVisibleFeaturesCheckBox->setEnabled( true );
786  }
787 }
788 
789 void QgsLayoutAttributeTableWidget::mAddFramePushButton_clicked()
790 {
791  if ( !mTable || !mFrame )
792  {
793  return;
794  }
795 
796  //create a new frame based on the current frame
797  QPointF pos = mFrame->pos();
798  //shift new frame so that it sits 10 units below current frame
799  pos.ry() += mFrame->rect().height() + 10;
800 
801  QgsLayoutFrame *newFrame = mTable->createNewFrame( mFrame, pos, mFrame->rect().size() );
802  mTable->recalculateFrameSizes();
803 
804  //set new frame as selection
805  if ( QgsLayout *layout = mTable->layout() )
806  {
807  layout->setSelectedItem( newFrame );
808  }
809 }
810 
811 void QgsLayoutAttributeTableWidget::mResizeModeComboBox_currentIndexChanged( int index )
812 {
813  if ( !mTable )
814  {
815  return;
816  }
817 
818  mTable->beginCommand( tr( "Change Resize Mode" ) );
819  mTable->setResizeMode( static_cast< QgsLayoutMultiFrame::ResizeMode >( mResizeModeComboBox->itemData( index ).toInt() ) );
820  mTable->endCommand();
821 
822  mAddFramePushButton->setEnabled( mTable->resizeMode() == QgsLayoutMultiFrame::UseExistingFrames );
823 }
824 
825 void QgsLayoutAttributeTableWidget::mSourceComboBox_currentIndexChanged( int index )
826 {
827  if ( !mTable )
828  {
829  return;
830  }
831 
832  mTable->beginCommand( tr( "Change Table Source" ) );
833  mTable->setSource( static_cast< QgsLayoutItemAttributeTable::ContentSource >( mSourceComboBox->itemData( index ).toInt() ) );
834  mTable->endCommand();
835 
836  toggleSourceControls();
837 }
838 
839 void QgsLayoutAttributeTableWidget::mRelationsComboBox_currentIndexChanged( int index )
840 {
841  if ( !mTable )
842  {
843  return;
844  }
845 
846  mTable->beginCommand( tr( "Change Table Source Relation" ) );
847  mTable->setRelationId( mRelationsComboBox->itemData( index ).toString() );
848  mTable->endCommand();
849 }
850 
851 void QgsLayoutAttributeTableWidget::mEmptyModeComboBox_currentIndexChanged( int index )
852 {
853  if ( !mTable )
854  {
855  return;
856  }
857 
858  mTable->beginCommand( tr( "Change Empty Table Behavior" ) );
859  mTable->setEmptyTableBehavior( static_cast< QgsLayoutTable::EmptyTableMode >( mEmptyModeComboBox->itemData( index ).toInt() ) );
860  mTable->endCommand();
861  mEmptyMessageLineEdit->setEnabled( mTable->emptyTableBehavior() == QgsLayoutTable::ShowMessage );
862  mEmptyMessageLabel->setEnabled( mTable->emptyTableBehavior() == QgsLayoutTable::ShowMessage );
863 }
864 
865 void QgsLayoutAttributeTableWidget::mWrapBehaviorComboBox_currentIndexChanged( int index )
866 {
867  if ( !mTable )
868  {
869  return;
870  }
871 
872  mTable->beginCommand( tr( "Change Table Wrap Mode" ) );
873  mTable->setWrapBehavior( static_cast< QgsLayoutTable::WrapBehavior >( mWrapBehaviorComboBox->itemData( index ).toInt() ) );
874  mTable->endCommand();
875 }
876 
877 void QgsLayoutAttributeTableWidget::mAdvancedCustomizationButton_clicked()
878 {
879  if ( !mTable )
880  {
881  return;
882  }
883 
884  QgsLayoutTableBackgroundColorsDialog d( mTable, this );
885  d.exec();
886 }
887 
888 void QgsLayoutAttributeTableWidget::useConditionalStylingChanged( bool checked )
889 {
890  if ( !mTable )
891  {
892  return;
893  }
894 
895  mTable->beginCommand( tr( "Toggle Table Conditional Styling" ) );
896  mTable->setUseConditionalStyling( checked );
897  mTable->update();
898  mTable->endCommand();
899 }
900 
901 void QgsLayoutAttributeTableWidget::mDrawEmptyCheckBox_toggled( bool checked )
902 {
903  if ( !mTable )
904  {
905  return;
906  }
907 
908  mTable->beginCommand( tr( "Change Show Empty Rows" ) );
909  mTable->setShowEmptyRows( checked );
910  mTable->endCommand();
911 }
912 
913 void QgsLayoutAttributeTableWidget::mEmptyMessageLineEdit_editingFinished()
914 {
915  if ( !mTable )
916  {
917  return;
918  }
919 
920  mTable->beginCommand( tr( "Change Empty Table Message" ) );
921  mTable->setEmptyTableMessage( mEmptyMessageLineEdit->text() );
922  mTable->endCommand();
923 }
924 
925 void QgsLayoutAttributeTableWidget::toggleSourceControls()
926 {
927  switch ( mTable->source() )
928  {
930  mLayerComboBox->setEnabled( true );
931  mLayerComboBox->setVisible( true );
932  mLayerSourceDDBtn->setVisible( true );
933  mLayerLabel->setVisible( true );
934  mRelationsComboBox->setEnabled( false );
935  mRelationsComboBox->setVisible( false );
936  mRelationLabel->setVisible( false );
937  mMaximumRowsSpinBox->setEnabled( true );
938  mMaxNumFeaturesLabel->setEnabled( true );
939  mShowOnlyVisibleFeaturesCheckBox->setEnabled( true );
940  mComposerMapComboBox->setEnabled( mTable->displayOnlyVisibleFeatures() );
941  mComposerMapLabel->setEnabled( mTable->displayOnlyVisibleFeatures() );
942  break;
944  mLayerComboBox->setEnabled( false );
945  mLayerComboBox->setVisible( false );
946  mLayerSourceDDBtn->setVisible( false );
947  mLayerLabel->setVisible( false );
948  mRelationsComboBox->setEnabled( false );
949  mRelationsComboBox->setVisible( false );
950  mRelationLabel->setVisible( false );
951  mMaximumRowsSpinBox->setEnabled( false );
952  mMaxNumFeaturesLabel->setEnabled( false );
953  mShowOnlyVisibleFeaturesCheckBox->setEnabled( false );
954  mComposerMapComboBox->setEnabled( false );
955  mComposerMapLabel->setEnabled( false );
956  break;
958  mLayerComboBox->setEnabled( false );
959  mLayerComboBox->setVisible( false );
960  mLayerLabel->setVisible( false );
961  mLayerSourceDDBtn->setVisible( false );
962  mRelationsComboBox->setEnabled( true );
963  mRelationsComboBox->setVisible( true );
964  mRelationLabel->setVisible( true );
965  mMaximumRowsSpinBox->setEnabled( true );
966  mMaxNumFeaturesLabel->setEnabled( true );
967  mShowOnlyVisibleFeaturesCheckBox->setEnabled( true );
968  mComposerMapComboBox->setEnabled( true );
969  mComposerMapLabel->setEnabled( true );
970  break;
971  }
972 }
Base class for all map layer types.
Definition: qgsmaplayer.h:84
Base class for graphical items within a QgsLayout.
Show header rows only.
Header uses the same alignment as the column.
Don&#39;t automatically create new frames, just use existing frames.
A widget for controlling the common properties of layout items (e.g.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Text which doesn&#39;t fit inside the cell is truncated.
Table shows attributes from related child features.
void toggled(bool)
Emitted when atlas is enabled or disabled.
Shows preset message instead of table contents.
void updateDataDefinedButton(QgsPropertyOverrideButton *button)
Updates a previously registered data defined button to reflect the item&#39;s current properties...
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
Text which doesn&#39;t fit inside the cell is wrapped. Note that this only applies to text in columns wit...
bool setNewItem(QgsLayoutItem *item) override
Attempts to update the widget to show the properties for the specified item.
Table shows attributes from features in a vector layer.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
Creates new full page frames on the following page(s) until the entire multiframe content is visible...
A dialog for customization of the cell background colors for a QgsLayoutTable.
Abstract base class for layout items with the ability to distribute the content to several frames (Qg...
void registerDataDefinedButton(QgsPropertyOverrideButton *button, QgsLayoutObject::DataDefinedProperty property)
Registers a data defined button, setting up its initial value, connections and description.
Attribute table source layer.
No headers shown for table.
Align headers right.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector ...
QgsLayoutMultiFrame * multiFrame() const
Returns the parent multiframe for the frame.
void changed()
Emitted when the widget&#39;s text format settings are changed.
A layout table subclass that displays attributes from a vector layer.
Header shown on first frame only.
Table shows attributes from the current atlas feature.
void layerChanged(QgsVectorLayer *layer)
Emitted when the context&#39;s layer is changed.
Configure font settings for use with QgsTextRenderer.
Definition: qgsfontbutton.h:60
QgsLayoutAttributeTableWidget(QgsLayoutFrame *frame)
constructor
QgsRelationManager relationManager
Definition: qgsproject.h:109
void setMasterLayout(QgsMasterLayoutInterface *masterLayout) override
Sets the master layout associated with the item.
QgsLayoutAtlas * layoutAtlas() const
Returns the atlas for the layout (if available)
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
A dialog to select what attributes to display (in the table item), set the column properties and spec...
void itemChanged(QgsLayoutItem *item)
Emitted whenever the currently selected item changes.
Align headers left.
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
QgsVectorLayer * coverageLayer() const
Returns the current layout context coverage layer (if set).
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:263
void changed()
Emitted when relations are added or removed to the manager.
A base class for property widgets for layout items.
virtual int type() const =0
Returns unique multiframe type id.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:498
Headers shown on all frames.
bool enabled() const
Returns whether the atlas generation is enabled.
void setItem(QgsLayoutItem *item)
Sets the layout item.
void setReportTypeString(const QString &string) override
Sets the string to use to describe the current report type (e.g.
Interface for master layout type objects, such as print layouts and reports.
Align headers to center.
void changed()
Emitted when the object&#39;s properties change.
Represents a vector layer which manages a vector based data sets.
Base class for frame items, which form a layout multiframe item.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
A generic dialog for building expression strings.
Hides entire table if empty.
void setMasterLayout(QgsMasterLayoutInterface *masterLayout)
Sets the master layout associated with the item.