QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgslayoutitemwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutitemwidget.cpp
3  ------------------------
4  Date : July 2017
5  Copyright : (C) 2017 Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
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 "qgslayoutitemwidget.h"
18 #include "qgslayout.h"
19 #include "qgsproject.h"
20 #include "qgslayoutundostack.h"
21 #include "qgsprintlayout.h"
22 #include "qgslayoutatlas.h"
24 #include "qgslayoutframe.h"
25 #include "qgssymbolbutton.h"
26 #include "qgsfontbutton.h"
29 #include "qgslayoutmultiframe.h"
30 #include <QButtonGroup>
31 
32 //
33 // QgsLayoutConfigObject
34 //
35 
37  : QObject( parent )
38  , mLayoutObject( layoutObject )
39 {
40  if ( mLayoutObject->layout() )
41  {
42  connect( &mLayoutObject->layout()->reportContext(), &QgsLayoutReportContext::layerChanged,
43  this, [ = ] { updateDataDefinedButtons(); } );
44  }
45  if ( layoutAtlas() )
46  {
47  connect( layoutAtlas(), &QgsLayoutAtlas::toggled, this, &QgsLayoutConfigObject::updateDataDefinedButtons );
48  }
49 }
50 
51 void QgsLayoutConfigObject::updateDataDefinedProperty()
52 {
53  //match data defined button to item's data defined property
54  QgsPropertyOverrideButton *ddButton = qobject_cast<QgsPropertyOverrideButton *>( sender() );
55  if ( !ddButton )
56  {
57  return;
58  }
60 
61  if ( ddButton->propertyKey() >= 0 )
62  key = static_cast< QgsLayoutObject::DataDefinedProperty >( ddButton->propertyKey() );
63 
64  if ( key == QgsLayoutObject::NoProperty )
65  {
66  return;
67  }
68 
69  const bool propertyAssociatesWithMultiFrame = QgsLayoutObject::propertyAssociatesWithParentMultiframe( key );
70 
71  //set the data defined property and refresh the item
72  if ( propertyAssociatesWithMultiFrame )
73  {
74  if ( QgsLayoutFrame *frame = dynamic_cast< QgsLayoutFrame * >( mLayoutObject.data() ) )
75  {
76  if ( QgsLayoutMultiFrame *multiFrame = frame->multiFrame() )
77  {
78  multiFrame->dataDefinedProperties().setProperty( key, ddButton->toProperty() );
79  multiFrame->refresh();
80  }
81  }
82  else if ( QgsLayoutMultiFrame *multiFrame = dynamic_cast< QgsLayoutMultiFrame * >( mLayoutObject.data() ) )
83  {
84  multiFrame->dataDefinedProperties().setProperty( key, ddButton->toProperty() );
85  multiFrame->refresh();
86  }
87  }
88  else if ( mLayoutObject )
89  {
90  mLayoutObject->dataDefinedProperties().setProperty( key, ddButton->toProperty() );
91  mLayoutObject->refresh();
92  }
93 }
94 
95 void QgsLayoutConfigObject::updateDataDefinedButtons()
96 {
97  const QList< QgsPropertyOverrideButton * > buttons = findChildren< QgsPropertyOverrideButton * >();
98  for ( QgsPropertyOverrideButton *button : buttons )
99  {
100  button->setVectorLayer( coverageLayer() );
101  }
102 }
103 
105 {
106  button->blockSignals( true );
107  button->init( key, mLayoutObject->dataDefinedProperties(), QgsLayoutObject::propertyDefinitions(), coverageLayer() );
108  connect( button, &QgsPropertyOverrideButton::changed, this, &QgsLayoutConfigObject::updateDataDefinedProperty, Qt::UniqueConnection );
109  button->registerExpressionContextGenerator( mLayoutObject );
110  button->blockSignals( false );
111 }
112 
114 {
115  if ( !button )
116  return;
117 
118  if ( button->propertyKey() < 0 || !mLayoutObject )
119  return;
120 
122  const bool propertyAssociatesWithMultiFrame = QgsLayoutObject::propertyAssociatesWithParentMultiframe( key );
123 
124  //set the data defined property
125  if ( propertyAssociatesWithMultiFrame )
126  {
127  if ( QgsLayoutFrame *frame = dynamic_cast< QgsLayoutFrame * >( mLayoutObject.data() ) )
128  {
129  if ( QgsLayoutMultiFrame *multiFrame = frame->multiFrame() )
130  {
131  whileBlocking( button )->setToProperty( multiFrame->dataDefinedProperties().property( key ) );
132  }
133  }
134  else if ( QgsLayoutMultiFrame *multiFrame = dynamic_cast< QgsLayoutMultiFrame * >( mLayoutObject.data() ) )
135  {
136  whileBlocking( button )->setToProperty( multiFrame->dataDefinedProperties().property( key ) );
137  }
138  }
139  else if ( mLayoutObject )
140  {
141  whileBlocking( button )->setToProperty( mLayoutObject->dataDefinedProperties().property( key ) );
142  }
143 
144  // In case the button was initialized to a different config object, we need to reconnect to it here (see https://github.com/qgis/QGIS/issues/26582 )
145  connect( button, &QgsPropertyOverrideButton::changed, this, &QgsLayoutConfigObject::updateDataDefinedProperty, Qt::UniqueConnection );
146  button->registerExpressionContextGenerator( mLayoutObject );
147 }
148 
150 {
151  if ( !mLayoutObject )
152  {
153  return nullptr;
154  }
155 
156  QgsPrintLayout *printLayout = qobject_cast< QgsPrintLayout * >( mLayoutObject->layout() );
157 
158  if ( !printLayout )
159  {
160  return nullptr;
161  }
162 
163  return printLayout->atlas();
164 }
165 
167 {
168  mLayoutObject = object;
169 }
170 
172 {
173  if ( !mLayoutObject )
174  return nullptr;
175 
176  QgsLayout *layout = mLayoutObject->layout();
177  if ( !layout )
178  return nullptr;
179 
180  return layout->reportContext().layer();
181 }
182 
183 
184 //
185 // QgsLayoutItemBaseWidget
186 //
187 
189  : QgsPanelWidget( parent )
190  , mConfigObject( new QgsLayoutConfigObject( this, layoutObject ) )
191  , mObject( layoutObject )
192 {
193 
194 }
195 
197 {
198  return mObject;
199 }
200 
202 {
203  QgsLayoutObject *oldObject = mObject;
204  QgsLayoutConfigObject *oldConfigObject = mConfigObject;
205  // have to set new mObject/mConfigObject here, because setNewItem methods require access to them
206  mObject = item;
207  mConfigObject = new QgsLayoutConfigObject( this, mObject );
208  if ( setNewItem( item ) )
209  {
210  oldConfigObject->deleteLater();
211  return true;
212  }
213  else
214  {
215  // revert object change since it was unsuccessful
216  mObject = oldObject;
217  mConfigObject->deleteLater();
218  mConfigObject = oldConfigObject;
219  return false;
220  }
221 }
222 
224 {
225 }
226 
228 {
229  const auto symbolButtonWidgets = findChildren<QgsSymbolButton *>();
230  for ( QgsSymbolButton *symbolWidget : symbolButtonWidgets )
231  {
232  symbolWidget->setMessageBar( iface->messageBar() );
233  }
234  const auto fontButtonWidgets = findChildren<QgsFontButton *>();
235  for ( QgsFontButton *fontButton : fontButtonWidgets )
236  {
237  fontButton->setMessageBar( iface->messageBar() );
238  }
239 }
240 
242 {
243 
244 }
245 
247 {
248  mConfigObject->initializeDataDefinedButton( button, property );
249 }
250 
252 {
253  mConfigObject->updateDataDefinedButton( button );
254 }
255 
257 {
258  return mConfigObject->coverageLayer();
259 }
260 
262 {
263  return false;
264 }
265 
267 {
268  return mConfigObject->layoutAtlas();
269 }
270 
271 //
272 
273 
274 //QgsLayoutItemPropertiesWidget
275 
277 {
278  if ( !mItem )
279  return;
280 
281  mBlockVariableUpdates = true;
282  QgsExpressionContext context = mItem->createExpressionContext();
283  mVariableEditor->setContext( &context );
284 
285  // here, we prefer to make the multiframe's scope the editable one. That's because most expressions are evaluated
286  // on the multiframe subclass level, not on a frame-by-frame basis. Ideally both would be editable, but for now let's go
287  // with the most useful one.
288  const int multiFrameScopeIndex = context.indexOfScope( tr( "Multiframe Item" ) );
289  const int itemScopeIndex = context.indexOfScope( tr( "Layout Item" ) );
290  if ( multiFrameScopeIndex >= 0 )
291  mVariableEditor->setEditableScopeIndex( multiFrameScopeIndex );
292  else if ( itemScopeIndex >= 0 )
293  mVariableEditor->setEditableScopeIndex( itemScopeIndex );
294  mBlockVariableUpdates = false;
295 }
296 
298  : QWidget( parent )
299  , mConfigObject( new QgsLayoutConfigObject( this, item ) )
300 {
301  setupUi( this );
302 
303  mVariableEditor->setMinimumHeight( mVariableEditor->fontMetrics().height() * 15 );
304 
305  mItemRotationSpinBox->setClearValue( 0 );
306  mStrokeUnitsComboBox->linkToWidget( mStrokeWidthSpinBox );
307  mStrokeUnitsComboBox->setConverter( &item->layout()->renderContext().measurementConverter() );
308 
309  mPosUnitsComboBox->linkToWidget( mXPosSpin );
310  mPosUnitsComboBox->linkToWidget( mYPosSpin );
311  mSizeUnitsComboBox->linkToWidget( mWidthSpin );
312  mSizeUnitsComboBox->linkToWidget( mHeightSpin );
313 
314  mPosUnitsComboBox->setConverter( &item->layout()->renderContext().measurementConverter() );
315  mSizeUnitsComboBox->setConverter( &item->layout()->renderContext().measurementConverter() );
316 
317  mPosLockAspectRatio->setWidthSpinBox( mXPosSpin );
318  mPosLockAspectRatio->setHeightSpinBox( mYPosSpin );
319  mSizeLockAspectRatio->setWidthSpinBox( mWidthSpin );
320  mSizeLockAspectRatio->setHeightSpinBox( mHeightSpin );
321 
322  mItemFrameColorDDBtn->registerLinkedWidget( mFrameColorButton );
323  mItemBackgroundColorDDBtn->registerLinkedWidget( mBackgroundColorButton );
324 
325  connect( mFrameColorButton, &QgsColorButton::colorChanged, this, &QgsLayoutItemPropertiesWidget::mFrameColorButton_colorChanged );
326  connect( mBackgroundColorButton, &QgsColorButton::colorChanged, this, &QgsLayoutItemPropertiesWidget::mBackgroundColorButton_colorChanged );
327  connect( mStrokeWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mStrokeWidthSpinBox_valueChanged );
328  connect( mStrokeUnitsComboBox, &QgsLayoutUnitsComboBox::changed, this, &QgsLayoutItemPropertiesWidget::strokeUnitChanged );
329  connect( mFrameGroupBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutItemPropertiesWidget::mFrameGroupBox_toggled );
330  connect( mFrameJoinStyleCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutItemPropertiesWidget::mFrameJoinStyleCombo_currentIndexChanged );
331  connect( mBackgroundGroupBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutItemPropertiesWidget::mBackgroundGroupBox_toggled );
332  connect( mItemIdLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutItemPropertiesWidget::mItemIdLineEdit_editingFinished );
333  connect( mPageSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mPageSpinBox_valueChanged );
334  connect( mXPosSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mXPosSpin_valueChanged );
335  connect( mYPosSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mYPosSpin_valueChanged );
336  connect( mPosUnitsComboBox, &QgsLayoutUnitsComboBox::changed, this, &QgsLayoutItemPropertiesWidget::positionUnitsChanged );
337  connect( mWidthSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mWidthSpin_valueChanged );
338  connect( mHeightSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mHeightSpin_valueChanged );
339  connect( mSizeUnitsComboBox, &QgsLayoutUnitsComboBox::changed, this, &QgsLayoutItemPropertiesWidget::sizeUnitsChanged );
340  connect( mUpperLeftRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mUpperLeftCheckBox_stateChanged );
341  connect( mUpperMiddleRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mUpperMiddleCheckBox_stateChanged );
342  connect( mUpperRightRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mUpperRightCheckBox_stateChanged );
343  connect( mMiddleLeftRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mMiddleLeftCheckBox_stateChanged );
344  connect( mMiddleRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mMiddleCheckBox_stateChanged );
345  connect( mMiddleRightRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mMiddleRightCheckBox_stateChanged );
346  connect( mLowerLeftRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mLowerLeftCheckBox_stateChanged );
347  connect( mLowerMiddleRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mLowerMiddleCheckBox_stateChanged );
348  connect( mLowerRightRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mLowerRightCheckBox_stateChanged );
349  connect( mBlendModeCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutItemPropertiesWidget::mBlendModeCombo_currentIndexChanged );
350  connect( mItemRotationSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mItemRotationSpinBox_valueChanged );
351  connect( mExcludeFromPrintsCheckBox, &QCheckBox::toggled, this, &QgsLayoutItemPropertiesWidget::mExcludeFromPrintsCheckBox_toggled );
352 
353  //make button exclusive
354  QButtonGroup *buttonGroup = new QButtonGroup( this );
355  buttonGroup->addButton( mUpperLeftRadioButton );
356  buttonGroup->addButton( mUpperMiddleRadioButton );
357  buttonGroup->addButton( mUpperRightRadioButton );
358  buttonGroup->addButton( mMiddleLeftRadioButton );
359  buttonGroup->addButton( mMiddleRadioButton );
360  buttonGroup->addButton( mMiddleRightRadioButton );
361  buttonGroup->addButton( mLowerLeftRadioButton );
362  buttonGroup->addButton( mLowerMiddleRadioButton );
363  buttonGroup->addButton( mLowerRightRadioButton );
364  buttonGroup->setExclusive( true );
365 
367 
368  setItem( item );
369 
370  connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsLayoutItemPropertiesWidget::opacityChanged );
371 
372  updateVariables();
373  connect( mVariableEditor, &QgsVariableEditorWidget::scopeChanged, this, [ = ]
374  {
375  if ( !mBlockVariableUpdates )
376  QgsLayoutItemPropertiesWidget::variablesChanged();
377  } );
378  // listen out for variable edits
382 
383  if ( item->layout() )
384  {
388  }
389 }
390 
392 {
393  mBackgroundGroupBox->setVisible( showGroup );
394 }
395 
397 {
398  mFrameGroupBox->setVisible( showGroup );
399 }
400 
402 {
403  if ( mItem )
404  {
405  disconnect( mItem, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutItemPropertiesWidget::setValuesForGuiPositionElements );
406  disconnect( mItem, &QgsLayoutObject::changed, this, &QgsLayoutItemPropertiesWidget::setValuesForGuiNonPositionElements );
407  }
408  mItem = item;
409  if ( mItem )
410  {
411  connect( mItem, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutItemPropertiesWidget::setValuesForGuiPositionElements );
412  connect( mItem, &QgsLayoutObject::changed, this, &QgsLayoutItemPropertiesWidget::setValuesForGuiNonPositionElements );
413  }
414 
415  mConfigObject->setObject( mItem );
416 
417  setValuesForGuiElements();
418 }
419 
421 {
422  if ( QgsPrintLayout *printLayout = dynamic_cast< QgsPrintLayout * >( masterLayout ) )
423  {
426  }
427 }
428 
429 //slots
430 
431 void QgsLayoutItemPropertiesWidget::mFrameColorButton_colorChanged( const QColor &newFrameColor )
432 {
433  if ( !mItem )
434  {
435  return;
436  }
437  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Frame Color" ), QgsLayoutItem::UndoStrokeColor );
438  mItem->setFrameStrokeColor( newFrameColor );
439  mItem->layout()->undoStack()->endCommand();
440  mItem->update();
441 }
442 
443 void QgsLayoutItemPropertiesWidget::mBackgroundColorButton_colorChanged( const QColor &newBackgroundColor )
444 {
445  if ( !mItem )
446  {
447  return;
448  }
449  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Background Color" ), QgsLayoutItem::UndoBackgroundColor );
450  mItem->setBackgroundColor( newBackgroundColor );
451  mItem->layout()->undoStack()->endCommand();
452  mItem->invalidateCache();
453 }
454 
455 void QgsLayoutItemPropertiesWidget::changeItemPosition()
456 {
457  if ( !mItem )
458  return;
459 
460  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Move Item" ), QgsLayoutItem::UndoIncrementalMove );
461 
462  const QgsLayoutPoint point( mXPosSpin->value(), mYPosSpin->value(), mPosUnitsComboBox->unit() );
463  mItem->attemptMove( point, true, false, mPageSpinBox->value() - 1 );
464 
465  mItem->layout()->undoStack()->endCommand();
466 }
467 
468 void QgsLayoutItemPropertiesWidget::changeItemReference( QgsLayoutItem::ReferencePoint point )
469 {
470  if ( !mItem )
471  return;
472 
473  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Item Reference" ) );
474  mItem->setReferencePoint( point );
475  mItem->layout()->undoStack()->endCommand();
476 }
477 
478 void QgsLayoutItemPropertiesWidget::changeItemSize()
479 {
480  if ( !mItem )
481  return;
482 
483  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Resize Item" ), QgsLayoutItem::UndoIncrementalResize );
484 
485  const QgsLayoutSize size( mWidthSpin->value(), mHeightSpin->value(), mSizeUnitsComboBox->unit() );
486  mItem->attemptResize( size );
487 
488  mItem->layout()->undoStack()->endCommand();
489 }
490 
491 void QgsLayoutItemPropertiesWidget::variablesChanged()
492 {
493  if ( !mItem )
494  return;
495 
496  if ( QgsLayoutFrame *frame = qobject_cast< QgsLayoutFrame * >( mItem ) )
497  {
498  if ( QgsLayoutMultiFrame *mf = frame->multiFrame() )
499  {
500  QgsExpressionContextUtils::setLayoutMultiFrameVariables( mf, mVariableEditor->variablesInActiveScope() );
501  }
502  }
503  else
504  {
505  QgsExpressionContextUtils::setLayoutItemVariables( mItem, mVariableEditor->variablesInActiveScope() );
506  }
507 }
508 
510 {
511  if ( mUpperLeftRadioButton->isChecked() )
512  {
514  }
515  else if ( mUpperMiddleRadioButton->isChecked() )
516  {
518  }
519  else if ( mUpperRightRadioButton->isChecked() )
520  {
522  }
523  else if ( mMiddleLeftRadioButton->isChecked() )
524  {
526  }
527  else if ( mMiddleRadioButton->isChecked() )
528  {
529  return QgsLayoutItem::Middle;
530  }
531  else if ( mMiddleRightRadioButton->isChecked() )
532  {
534  }
535  else if ( mLowerLeftRadioButton->isChecked() )
536  {
538  }
539  else if ( mLowerMiddleRadioButton->isChecked() )
540  {
542  }
543  else if ( mLowerRightRadioButton->isChecked() )
544  {
546  }
548 }
549 
550 void QgsLayoutItemPropertiesWidget::mStrokeWidthSpinBox_valueChanged( double d )
551 {
552  if ( !mItem )
553  {
554  return;
555  }
556 
557  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Frame Stroke Width" ), QgsLayoutItem::UndoStrokeWidth );
558  mItem->setFrameStrokeWidth( QgsLayoutMeasurement( d, mStrokeUnitsComboBox->unit() ) );
559  mItem->layout()->undoStack()->endCommand();
560 }
561 
562 void QgsLayoutItemPropertiesWidget::strokeUnitChanged( QgsUnitTypes::LayoutUnit unit )
563 {
564  if ( !mItem )
565  {
566  return;
567  }
568 
569  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Frame Stroke Width" ), QgsLayoutItem::UndoStrokeWidth );
570  mItem->setFrameStrokeWidth( QgsLayoutMeasurement( mStrokeWidthSpinBox->value(), unit ) );
571  mItem->layout()->undoStack()->endCommand();
572 }
573 
574 void QgsLayoutItemPropertiesWidget::mFrameJoinStyleCombo_currentIndexChanged( int index )
575 {
576  Q_UNUSED( index )
577  if ( !mItem )
578  {
579  return;
580  }
581 
582  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Frame Join Style" ) );
583  mItem->setFrameJoinStyle( mFrameJoinStyleCombo->penJoinStyle() );
584  mItem->layout()->undoStack()->endCommand();
585 }
586 
587 void QgsLayoutItemPropertiesWidget::mFrameGroupBox_toggled( bool state )
588 {
589  if ( !mItem )
590  {
591  return;
592  }
593 
594  mItem->layout()->undoStack()->beginCommand( mItem, state ? tr( "Enable Frame" ) : tr( "Disable Frame" ) );
595  mItem->setFrameEnabled( state );
596  mItem->update();
597  mItem->layout()->undoStack()->endCommand();
598 }
599 
600 void QgsLayoutItemPropertiesWidget::mBackgroundGroupBox_toggled( bool state )
601 {
602  if ( !mItem )
603  {
604  return;
605  }
606 
607  mItem->layout()->undoStack()->beginCommand( mItem, state ? tr( "Enable Background" ) : tr( "Disable Background" ) );
608  mItem->setBackgroundEnabled( state );
609  mItem->layout()->undoStack()->endCommand();
610  mItem->invalidateCache();
611 }
612 
613 
614 void QgsLayoutItemPropertiesWidget::setValuesForGuiPositionElements()
615 {
616  if ( !mItem )
617  {
618  return;
619  }
620 
621  auto block = [ = ]( bool blocked )
622  {
623  mXPosSpin->blockSignals( blocked );
624  mYPosSpin->blockSignals( blocked );
625  mPosUnitsComboBox->blockSignals( blocked );
626  mWidthSpin->blockSignals( blocked );
627  mHeightSpin->blockSignals( blocked );
628  mSizeUnitsComboBox->blockSignals( blocked );
629  mUpperLeftRadioButton->blockSignals( blocked );
630  mUpperMiddleRadioButton->blockSignals( blocked );
631  mUpperRightRadioButton->blockSignals( blocked );
632  mMiddleLeftRadioButton->blockSignals( blocked );
633  mMiddleRadioButton->blockSignals( blocked );
634  mMiddleRightRadioButton->blockSignals( blocked );
635  mLowerLeftRadioButton->blockSignals( blocked );
636  mLowerMiddleRadioButton->blockSignals( blocked );
637  mLowerRightRadioButton->blockSignals( blocked );
638  mPageSpinBox->blockSignals( blocked );
639  };
640  block( true );
641 
642  const QgsLayoutPoint point = mItem->pagePositionWithUnits();
643 
644  if ( !mFreezeXPosSpin )
645  mXPosSpin->setValue( point.x() );
646  if ( !mFreezeYPosSpin )
647  mYPosSpin->setValue( point.y() );
648  mPosUnitsComboBox->setUnit( point.units() );
649 
650  switch ( mItem->referencePoint() )
651  {
653  {
654  mUpperLeftRadioButton->setChecked( true );
655  break;
656  }
657 
659  {
660  mUpperMiddleRadioButton->setChecked( true );
661  break;
662  }
663 
665  {
666  mUpperRightRadioButton->setChecked( true );
667  break;
668  }
669 
671  {
672  mMiddleLeftRadioButton->setChecked( true );
673  break;
674  }
675 
677  {
678  mMiddleRadioButton->setChecked( true );
679  break;
680  }
681 
683  {
684  mMiddleRightRadioButton->setChecked( true );
685  break;
686  }
687 
689  {
690  mLowerLeftRadioButton->setChecked( true );
691  break;
692  }
693 
695  {
696  mLowerMiddleRadioButton->setChecked( true );
697  break;
698  }
699 
701  {
702  mLowerRightRadioButton->setChecked( true );
703  break;
704  }
705  }
706 
707  const QgsLayoutSize size = mItem->sizeWithUnits();
708  if ( !mFreezeWidthSpin )
709  mWidthSpin->setValue( size.width() );
710  if ( !mFreezeHeightSpin )
711  mHeightSpin->setValue( size.height() );
712 
713  mSizeUnitsComboBox->setUnit( size.units() );
714 
715  mSizeLockAspectRatio->resetRatio();
716  mPosLockAspectRatio->resetRatio();
717 
718  if ( !mFreezePageSpin )
719  mPageSpinBox->setValue( mItem->page() + 1 );
720 
721  block( false );
722 }
723 
724 void QgsLayoutItemPropertiesWidget::setValuesForGuiNonPositionElements()
725 {
726  if ( !mItem )
727  {
728  return;
729  }
730 
731  auto block = [ = ]( bool blocked )
732  {
733  mStrokeWidthSpinBox->blockSignals( blocked );
734  mStrokeUnitsComboBox->blockSignals( blocked );
735  mFrameGroupBox->blockSignals( blocked );
736  mBackgroundGroupBox->blockSignals( blocked );
737  mItemIdLineEdit->blockSignals( blocked );
738  mBlendModeCombo->blockSignals( blocked );
739  mOpacityWidget->blockSignals( blocked );
740  mFrameColorButton->blockSignals( blocked );
741  mFrameJoinStyleCombo->blockSignals( blocked );
742  mBackgroundColorButton->blockSignals( blocked );
743  mItemRotationSpinBox->blockSignals( blocked );
744  mExcludeFromPrintsCheckBox->blockSignals( blocked );
745  };
746  block( true );
747 
748  mBackgroundColorButton->setColor( mItem->backgroundColor() );
749  mFrameColorButton->setColor( mItem->frameStrokeColor() );
750  mStrokeUnitsComboBox->setUnit( mItem->frameStrokeWidth().units() );
751  mStrokeWidthSpinBox->setValue( mItem->frameStrokeWidth().length() );
752  mFrameJoinStyleCombo->setPenJoinStyle( mItem->frameJoinStyle() );
753  mItemIdLineEdit->setText( mItem->id() );
754  mFrameGroupBox->setChecked( mItem->frameEnabled() );
755  mBackgroundGroupBox->setChecked( mItem->hasBackground() );
756  mBlendModeCombo->setBlendMode( mItem->blendMode() );
757  mOpacityWidget->setOpacity( mItem->itemOpacity() );
758  mItemRotationSpinBox->setValue( mItem->itemRotation() );
759  mExcludeFromPrintsCheckBox->setChecked( mItem->excludeFromExports() );
760 
761  block( false );
762 }
763 
765 {
766  mConfigObject->initializeDataDefinedButton( mXPositionDDBtn, QgsLayoutObject::PositionX );
767  mConfigObject->initializeDataDefinedButton( mYPositionDDBtn, QgsLayoutObject::PositionY );
768  mConfigObject->initializeDataDefinedButton( mWidthDDBtn, QgsLayoutObject::ItemWidth );
769  mConfigObject->initializeDataDefinedButton( mHeightDDBtn, QgsLayoutObject::ItemHeight );
770  mConfigObject->initializeDataDefinedButton( mItemRotationDDBtn, QgsLayoutObject::ItemRotation );
771  mConfigObject->initializeDataDefinedButton( mOpacityDDBtn, QgsLayoutObject::Opacity );
772  mConfigObject->initializeDataDefinedButton( mBlendModeDDBtn, QgsLayoutObject::BlendMode );
773  mConfigObject->initializeDataDefinedButton( mExcludePrintsDDBtn, QgsLayoutObject::ExcludeFromExports );
774  mConfigObject->initializeDataDefinedButton( mItemFrameColorDDBtn, QgsLayoutObject::FrameColor );
775  mConfigObject->initializeDataDefinedButton( mItemBackgroundColorDDBtn, QgsLayoutObject::BackgroundColor );
776 }
777 
779 {
780  const QList< QgsPropertyOverrideButton * > buttons = findChildren< QgsPropertyOverrideButton * >();
781  for ( QgsPropertyOverrideButton *button : buttons )
782  {
783  mConfigObject->updateDataDefinedButton( button );
784  }
785 }
786 
787 void QgsLayoutItemPropertiesWidget::setValuesForGuiElements()
788 {
789  if ( !mItem )
790  {
791  return;
792  }
793 
794  mBackgroundColorButton->setColorDialogTitle( tr( "Select Background Color" ) );
795  mBackgroundColorButton->setAllowOpacity( true );
796  mBackgroundColorButton->setContext( QStringLiteral( "composer" ) );
797  mFrameColorButton->setColorDialogTitle( tr( "Select Frame Color" ) );
798  mFrameColorButton->setAllowOpacity( true );
799  mFrameColorButton->setContext( QStringLiteral( "composer" ) );
800 
801  setValuesForGuiPositionElements();
802  setValuesForGuiNonPositionElements();
804 
805  updateVariables();
806 }
807 
808 void QgsLayoutItemPropertiesWidget::mBlendModeCombo_currentIndexChanged( int index )
809 {
810  Q_UNUSED( index )
811  if ( mItem )
812  {
813  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Blend Mode" ) );
814  mItem->setBlendMode( mBlendModeCombo->blendMode() );
815  mItem->layout()->undoStack()->endCommand();
816  }
817 }
818 
819 void QgsLayoutItemPropertiesWidget::opacityChanged( double value )
820 {
821  if ( mItem )
822  {
823  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Opacity" ), QgsLayoutItem::UndoOpacity );
824  mItem->setItemOpacity( value );
825  mItem->layout()->undoStack()->endCommand();
826  }
827 }
828 
829 void QgsLayoutItemPropertiesWidget::mItemIdLineEdit_editingFinished()
830 {
831  if ( mItem )
832  {
833  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Item ID" ), QgsLayoutItem::UndoSetId );
834  mItem->setId( mItemIdLineEdit->text() );
835  mItemIdLineEdit->setText( mItem->id() );
836  mItem->layout()->undoStack()->endCommand();
837  }
838 }
839 
840 void QgsLayoutItemPropertiesWidget::mPageSpinBox_valueChanged( int )
841 {
842  mFreezePageSpin = true;
843  changeItemPosition();
844  mFreezePageSpin = false;
845 }
846 
847 void QgsLayoutItemPropertiesWidget::mXPosSpin_valueChanged( double )
848 {
849  mFreezeXPosSpin = true;
850  changeItemPosition();
851  mFreezeXPosSpin = false;
852 }
853 
854 void QgsLayoutItemPropertiesWidget::mYPosSpin_valueChanged( double )
855 {
856  mFreezeYPosSpin = true;
857  changeItemPosition();
858  mFreezeYPosSpin = false;
859 }
860 
861 void QgsLayoutItemPropertiesWidget::positionUnitsChanged( QgsUnitTypes::LayoutUnit )
862 {
863  changeItemPosition();
864 }
865 
866 void QgsLayoutItemPropertiesWidget::mWidthSpin_valueChanged( double )
867 {
868  mFreezeWidthSpin = true;
869  changeItemSize();
870  mFreezeWidthSpin = false;
871 }
872 
873 void QgsLayoutItemPropertiesWidget::mHeightSpin_valueChanged( double )
874 {
875  mFreezeHeightSpin = true;
876  changeItemSize();
877  mFreezeHeightSpin = false;
878 }
879 
880 void QgsLayoutItemPropertiesWidget::sizeUnitsChanged( QgsUnitTypes::LayoutUnit )
881 {
882  changeItemSize();
883 }
884 
885 void QgsLayoutItemPropertiesWidget::mUpperLeftCheckBox_stateChanged( bool state )
886 {
887  if ( !state )
888  return;
889 
890  if ( mItem )
891  {
892  changeItemReference( QgsLayoutItem::UpperLeft );
893  }
894  setValuesForGuiPositionElements();
895 }
896 
897 void QgsLayoutItemPropertiesWidget::mUpperMiddleCheckBox_stateChanged( bool state )
898 {
899  if ( !state )
900  return;
901  if ( mItem )
902  {
903  changeItemReference( QgsLayoutItem::UpperMiddle );
904  }
905  setValuesForGuiPositionElements();
906 }
907 
908 void QgsLayoutItemPropertiesWidget::mUpperRightCheckBox_stateChanged( bool state )
909 {
910  if ( !state )
911  return;
912  if ( mItem )
913  {
914  changeItemReference( QgsLayoutItem::UpperRight );
915  }
916  setValuesForGuiPositionElements();
917 }
918 
919 void QgsLayoutItemPropertiesWidget::mMiddleLeftCheckBox_stateChanged( bool state )
920 {
921  if ( !state )
922  return;
923  if ( mItem )
924  {
925  changeItemReference( QgsLayoutItem::MiddleLeft );
926  }
927  setValuesForGuiPositionElements();
928 }
929 
930 void QgsLayoutItemPropertiesWidget::mMiddleCheckBox_stateChanged( bool state )
931 {
932  if ( !state )
933  return;
934  if ( mItem )
935  {
936  changeItemReference( QgsLayoutItem::Middle );
937  }
938  setValuesForGuiPositionElements();
939 }
940 
941 void QgsLayoutItemPropertiesWidget::mMiddleRightCheckBox_stateChanged( bool state )
942 {
943  if ( !state )
944  return;
945  if ( mItem )
946  {
947  changeItemReference( QgsLayoutItem::MiddleRight );
948  }
949  setValuesForGuiPositionElements();
950 }
951 
952 void QgsLayoutItemPropertiesWidget::mLowerLeftCheckBox_stateChanged( bool state )
953 {
954  if ( !state )
955  return;
956  if ( mItem )
957  {
958  changeItemReference( QgsLayoutItem::LowerLeft );
959  }
960  setValuesForGuiPositionElements();
961 }
962 
963 void QgsLayoutItemPropertiesWidget::mLowerMiddleCheckBox_stateChanged( bool state )
964 {
965  if ( !state )
966  return;
967  if ( mItem )
968  {
969  changeItemReference( QgsLayoutItem::LowerMiddle );
970  }
971  setValuesForGuiPositionElements();
972 }
973 
974 void QgsLayoutItemPropertiesWidget::mLowerRightCheckBox_stateChanged( bool state )
975 {
976  if ( !state )
977  return;
978  if ( mItem )
979  {
980  changeItemReference( QgsLayoutItem::LowerRight );
981  }
982  setValuesForGuiPositionElements();
983 }
984 
985 void QgsLayoutItemPropertiesWidget::mItemRotationSpinBox_valueChanged( double val )
986 {
987  if ( mItem )
988  {
989  mItem->layout()->undoStack()->beginCommand( mItem, tr( "Rotate" ), QgsLayoutItem::UndoRotation );
990  mItem->setItemRotation( val, true );
991  mItem->update();
992  mItem->layout()->undoStack()->endCommand();
993  }
994 }
995 
996 void QgsLayoutItemPropertiesWidget::mExcludeFromPrintsCheckBox_toggled( bool checked )
997 {
998  if ( mItem )
999  {
1000  mItem->layout()->undoStack()->beginCommand( mItem, checked ? tr( "Exclude from Exports" ) : tr( "Include in Exports" ) );
1001  mItem->setExcludeFromExports( checked );
1002  mItem->layout()->undoStack()->endCommand();
1003  }
1004 }
void customVariablesChanged()
Emitted whenever a custom global variable changes.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
static void setLayoutItemVariables(QgsLayoutItem *item, const QVariantMap &variables)
Sets all layout item context variables for an item.
static void setLayoutMultiFrameVariables(QgsLayoutMultiFrame *frame, const QVariantMap &variables)
Sets all layout multiframe context variables for an frame.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
int indexOfScope(QgsExpressionContextScope *scope) const
Returns the index of the specified scope if it exists within the context.
A button for customizing QgsTextFormat settings.
Definition: qgsfontbutton.h:48
Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector ...
void toggled(bool)
Emitted when atlas is enabled or disabled.
void coverageLayerChanged(QgsVectorLayer *layer)
Emitted when the coverage layer for the atlas changes.
An object for property widgets for layout items.
QgsLayoutAtlas * layoutAtlas() const
Returns the atlas for the layout, if available.
void setObject(QgsLayoutObject *object) SIP_SKIP
Links a new layout object to this QgsLayoutConfigObject.
void initializeDataDefinedButton(QgsPropertyOverrideButton *button, QgsLayoutObject::DataDefinedProperty key)
Registers a data defined button, setting up its initial value, connections and description.
QgsVectorLayer * coverageLayer() const
Returns the current layout context coverage layer (if set).
void updateDataDefinedButton(QgsPropertyOverrideButton *button)
Updates a data defined button to reflect the item's current properties.
QgsLayoutConfigObject(QWidget *parent SIP_TRANSFERTHIS, QgsLayoutObject *layoutObject)
Constructor for QgsLayoutConfigObject, linked with the specified layoutObject.
A common interface for layout designer dialogs and widgets.
virtual QgsMessageBar * messageBar()=0
Returns the designer's message bar.
Base class for frame items, which form a layout multiframe item.
QgsLayoutObject * layoutObject()
Returns the layout object associated with this widget.
void updateDataDefinedButton(QgsPropertyOverrideButton *button)
Updates a previously registered data defined button to reflect the item's current properties.
bool setItem(QgsLayoutItem *item)
Sets the current item to show in the widget.
QgsVectorLayer * coverageLayer() const
Returns the current layout context coverage layer (if set).
virtual bool setNewItem(QgsLayoutItem *item)
Attempts to update the widget to show the properties for the specified item.
void registerDataDefinedButton(QgsPropertyOverrideButton *button, QgsLayoutObject::DataDefinedProperty property)
Registers a data defined button, setting up its initial value, connections and description.
virtual void setDesignerInterface(QgsLayoutDesignerInterface *iface)
Sets the the layout designer interface in which the widget is being shown.
QgsLayoutAtlas * layoutAtlas() const
Returns the atlas for the layout (if available)
virtual void setMasterLayout(QgsMasterLayoutInterface *masterLayout)
Sets the master layout associated with the item.
QgsLayoutItemBaseWidget(QWidget *parent SIP_TRANSFERTHIS, QgsLayoutObject *layoutObject)
Constructor for QgsLayoutItemBaseWidget, linked with the specified layoutObject.
virtual void setReportTypeString(const QString &string)
Sets the string to use to describe the current report type (e.g.
void setMasterLayout(QgsMasterLayoutInterface *masterLayout)
Sets the master layout associated with the item.
void populateDataDefinedButtons()
Sets data defined button state to match item.
QgsLayoutItemPropertiesWidget(QWidget *parent, QgsLayoutItem *item)
Constructs a QgsLayoutItemPropertiesWidget with a parent and for the given layout item.
void initializeDataDefinedButtons()
Initializes data defined buttons to current atlas coverage layer.
void showFrameGroup(bool showGroup)
Determines if the frame of the group box shall be shown.
void setItem(QgsLayoutItem *item)
Sets the layout item.
QgsLayoutItem::ReferencePoint positionMode() const
Returns the position mode.
void updateVariables()
Updates the variables widget, refreshing the values of variables shown.
void showBackgroundGroup(bool showGroup)
Determines if the background of the group box shall be shown.
Base class for graphical items within a QgsLayout.
@ UndoIncrementalMove
Layout item incremental movement, e.g. as a result of a keypress.
@ UndoBackgroundColor
Background color adjustment.
@ UndoOpacity
Opacity adjustment.
@ UndoIncrementalResize
Incremental resize.
@ UndoRotation
Rotation adjustment.
@ UndoStrokeWidth
Stroke width adjustment.
@ UndoSetId
Change item ID.
@ UndoStrokeColor
Stroke color adjustment.
ReferencePoint
Fixed position reference point.
@ LowerMiddle
Lower center of item.
@ MiddleLeft
Middle left of item.
@ Middle
Center of item.
@ UpperRight
Upper right corner of item.
@ LowerLeft
Lower left corner of item.
@ UpperLeft
Upper left corner of item.
@ UpperMiddle
Upper center of item.
@ MiddleRight
Middle right of item.
@ LowerRight
Lower right corner of item.
void sizePositionChanged()
Emitted when the item's size or position changes.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
Abstract base class for layout items with the ability to distribute the content to several frames (Qg...
A base class for objects which belong to a layout.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the layout object property definitions.
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
DataDefinedProperty
Data defined properties for different item types.
@ Opacity
Item opacity.
@ ItemWidth
Width of item.
@ ItemHeight
Height of item.
@ BlendMode
Item blend mode.
@ PositionY
Y position on page.
@ ExcludeFromExports
Exclude item from exports.
@ BackgroundColor
Item background color.
@ ItemRotation
Rotation of item.
@ NoProperty
No property.
@ PositionX
X position on page.
@ FrameColor
Item frame color.
static bool propertyAssociatesWithParentMultiframe(DataDefinedProperty property)
Returns true if the specified property key is normally associated with the parent QgsLayoutMultiFrame...
void changed()
Emitted when pages are added or removed from the collection.
This class provides a method of storing points, consisting of an x and y coordinate,...
double x() const
Returns x coordinate of point.
QgsUnitTypes::LayoutUnit units() const
Returns the units for the point.
double y() const
Returns y coordinate of point.
void dpiChanged()
Emitted when the context's DPI is changed.
const QgsLayoutMeasurementConverter & measurementConverter() const
Returns the layout measurement converter to be used in the layout.
void layerChanged(QgsVectorLayer *layer)
Emitted when the context's layer is changed.
QgsVectorLayer * layer() const
Returns the vector layer associated with the layout's context.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:41
QgsUnitTypes::LayoutUnit units() const
Returns the units for the size.
double height() const
Returns the height of the size.
Definition: qgslayoutsize.h:90
double width() const
Returns the width of the size.
Definition: qgslayoutsize.h:76
void changed(QgsUnitTypes::LayoutUnit unit)
Emitted when the unit is changed.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:51
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
Definition: qgslayout.cpp:359
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
Definition: qgslayout.cpp:459
void variablesChanged()
Emitted whenever the expression variables stored in the layout have been changed.
QgsLayoutReportContext & reportContext()
Returns a reference to the layout's report context, which stores information relating to the current ...
Definition: qgslayout.cpp:369
QgsProject * project() const
The project associated with the layout.
Definition: qgslayout.cpp:132
Interface for master layout type objects, such as print layouts and reports.
void opacityChanged(double opacity)
Emitted when the opacity is changed in the widget, where opacity ranges from 0.0 (transparent) to 1....
Base class for any widget that can be shown as a inline panel.
Print layout, a QgsLayout subclass for static or atlas-based layouts.
QgsLayoutAtlas * atlas()
Returns the print layout's atlas.
void nameChanged(const QString &name)
Emitted when the layout's name is changed.
void metadataChanged()
Emitted when the project's metadata is changed.
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
A button for controlling property overrides which may apply to a widget.
QgsProperty toProperty() const
Returns a QgsProperty object encapsulating the current state of the widget.
void changed()
Emitted when property definition changes.
void init(int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer=nullptr, bool auxiliaryStorageEnabled=false)
Initialize a newly constructed property button (useful if button was included in a UI layout).
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
int propertyKey() const
Returns the property key linked to the button.
A button for creating and modifying QgsSymbol settings.
LayoutUnit
Layout measurement units.
Definition: qgsunittypes.h:182
void scopeChanged()
Emitted when the user has modified a scope using the widget.
Represents a vector layer which manages a vector based data sets.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:1185