QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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"
17
19#include "qgsfilterlineedit.h"
20#include "qgsfontbutton.h"
21#include "qgslayout.h"
22#include "qgslayoutatlas.h"
24#include "qgslayoutframe.h"
25#include "qgslayoutmultiframe.h"
27#include "qgslayoutundostack.h"
28#include "qgsprintlayout.h"
29#include "qgsproject.h"
31#include "qgssymbolbutton.h"
32
33#include <QButtonGroup>
34#include <QString>
35
36#include "moc_qgslayoutitemwidget.cpp"
37
38using namespace Qt::StringLiterals;
39
40//
41// QgsLayoutConfigObject
42//
43
45 : QObject( parent )
46 , mLayoutObject( layoutObject )
47{
48 if ( mLayoutObject->layout() )
49 {
50 connect( &mLayoutObject->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, this, [this] { updateDataDefinedButtons(); } );
51 }
52 if ( layoutAtlas() )
53 {
54 connect( layoutAtlas(), &QgsLayoutAtlas::toggled, this, &QgsLayoutConfigObject::updateDataDefinedButtons );
55 }
56}
57
58void QgsLayoutConfigObject::updateDataDefinedProperty()
59{
60 //match data defined button to item's data defined property
61 QgsPropertyOverrideButton *ddButton = qobject_cast<QgsPropertyOverrideButton *>( sender() );
62 if ( !ddButton )
63 {
64 return;
65 }
67
68 if ( ddButton->propertyKey() >= 0 )
69 key = static_cast<QgsLayoutObject::DataDefinedProperty>( ddButton->propertyKey() );
70
72 {
73 return;
74 }
75
76 const bool propertyAssociatesWithMultiFrame = QgsLayoutObject::propertyAssociatesWithParentMultiframe( key );
77
78 //set the data defined property and refresh the item
79 if ( propertyAssociatesWithMultiFrame )
80 {
81 if ( QgsLayoutFrame *frame = dynamic_cast<QgsLayoutFrame *>( mLayoutObject.data() ) )
82 {
83 if ( QgsLayoutMultiFrame *multiFrame = frame->multiFrame() )
84 {
85 multiFrame->dataDefinedProperties().setProperty( key, ddButton->toProperty() );
86 multiFrame->refresh();
87 }
88 }
89 else if ( QgsLayoutMultiFrame *multiFrame = dynamic_cast<QgsLayoutMultiFrame *>( mLayoutObject.data() ) )
90 {
91 multiFrame->dataDefinedProperties().setProperty( key, ddButton->toProperty() );
92 multiFrame->refresh();
93 }
94 }
95 else if ( mLayoutObject )
96 {
97 mLayoutObject->dataDefinedProperties().setProperty( key, ddButton->toProperty() );
98 mLayoutObject->refresh();
99 }
100}
101
102void QgsLayoutConfigObject::updateDataDefinedButtons()
103{
104 const QList<QgsPropertyOverrideButton *> buttons = findChildren<QgsPropertyOverrideButton *>();
105 for ( QgsPropertyOverrideButton *button : buttons )
106 {
107 button->setVectorLayer( coverageLayer() );
108 }
109}
110
112{
113 button->blockSignals( true );
114 button->init( static_cast<int>( key ), mLayoutObject->dataDefinedProperties(), QgsLayoutObject::propertyDefinitions(), coverageLayer() );
115 connect( button, &QgsPropertyOverrideButton::changed, this, &QgsLayoutConfigObject::updateDataDefinedProperty, Qt::UniqueConnection );
116 button->registerExpressionContextGenerator( mLayoutObject );
117 button->blockSignals( false );
118}
119
121{
122 if ( !button )
123 return;
124
125 if ( button->propertyKey() < 0 || !mLayoutObject )
126 return;
127
129 const bool propertyAssociatesWithMultiFrame = QgsLayoutObject::propertyAssociatesWithParentMultiframe( key );
130
131 //set the data defined property
132 if ( propertyAssociatesWithMultiFrame )
133 {
134 if ( QgsLayoutFrame *frame = dynamic_cast<QgsLayoutFrame *>( mLayoutObject.data() ) )
135 {
136 if ( QgsLayoutMultiFrame *multiFrame = frame->multiFrame() )
137 {
138 whileBlocking( button )->setToProperty( multiFrame->dataDefinedProperties().property( key ) );
139 }
140 }
141 else if ( QgsLayoutMultiFrame *multiFrame = dynamic_cast<QgsLayoutMultiFrame *>( mLayoutObject.data() ) )
142 {
143 whileBlocking( button )->setToProperty( multiFrame->dataDefinedProperties().property( key ) );
144 }
145 }
146 else if ( mLayoutObject )
147 {
148 whileBlocking( button )->setToProperty( mLayoutObject->dataDefinedProperties().property( key ) );
149 }
150
151 // 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 )
152 connect( button, &QgsPropertyOverrideButton::changed, this, &QgsLayoutConfigObject::updateDataDefinedProperty, Qt::UniqueConnection );
153 button->registerExpressionContextGenerator( mLayoutObject );
154}
155
157{
158 if ( !mLayoutObject )
159 {
160 return nullptr;
161 }
162
163 QgsPrintLayout *printLayout = qobject_cast<QgsPrintLayout *>( mLayoutObject->layout() );
164
165 if ( !printLayout )
166 {
167 return nullptr;
168 }
169
170 return printLayout->atlas();
171}
172
174{
175 mLayoutObject = object;
176}
177
179{
180 if ( !mLayoutObject )
181 return nullptr;
182
183 QgsLayout *layout = mLayoutObject->layout();
184 if ( !layout )
185 return nullptr;
186
187 return layout->reportContext().layer();
188}
189
190
191//
192// QgsLayoutItemBaseWidget
193//
194
196 : QgsPanelWidget( parent )
197 , mConfigObject( new QgsLayoutConfigObject( this, layoutObject ) )
198 , mObject( layoutObject )
199{
200}
201
206
208{
209 QgsLayoutObject *oldObject = mObject;
210 QgsLayoutConfigObject *oldConfigObject = mConfigObject;
211 // have to set new mObject/mConfigObject here, because setNewItem methods require access to them
212 mObject = item;
213 mConfigObject = new QgsLayoutConfigObject( this, mObject );
214 if ( setNewItem( item ) )
215 {
216 oldConfigObject->deleteLater();
217 return true;
218 }
219 else
220 {
221 // revert object change since it was unsuccessful
222 mObject = oldObject;
223 mConfigObject->deleteLater();
224 mConfigObject = oldConfigObject;
225 return false;
226 }
227}
228
230{
231}
232
234{
235 const auto symbolButtonWidgets = findChildren<QgsSymbolButton *>();
236 for ( QgsSymbolButton *symbolWidget : symbolButtonWidgets )
237 {
238 symbolWidget->setMessageBar( iface->messageBar() );
239 }
240 const auto fontButtonWidgets = findChildren<QgsFontButton *>();
241 for ( QgsFontButton *fontButton : fontButtonWidgets )
242 {
243 fontButton->setMessageBar( iface->messageBar() );
244 }
245}
246
250
252{
253 mConfigObject->initializeDataDefinedButton( button, property );
254}
255
257{
258 mConfigObject->updateDataDefinedButton( button );
259}
260
262{
263 return mConfigObject->coverageLayer();
264}
265
267{
268 return false;
269}
270
272{
273 return mConfigObject->layoutAtlas();
274}
275
276//
277
278
279//QgsLayoutItemPropertiesWidget
280
282{
283 if ( !mItem )
284 return;
285
286 mBlockVariableUpdates = true;
287 QgsExpressionContext context = mItem->createExpressionContext();
288 mVariableEditor->setContext( &context );
289
290 // here, we prefer to make the multiframe's scope the editable one. That's because most expressions are evaluated
291 // on the multiframe subclass level, not on a frame-by-frame basis. Ideally both would be editable, but for now let's go
292 // with the most useful one.
293 const int multiFrameScopeIndex = context.indexOfScope( tr( "Multiframe Item" ) );
294 const int itemScopeIndex = context.indexOfScope( tr( "Layout Item" ) );
295 if ( multiFrameScopeIndex >= 0 )
296 mVariableEditor->setEditableScopeIndex( multiFrameScopeIndex );
297 else if ( itemScopeIndex >= 0 )
298 mVariableEditor->setEditableScopeIndex( itemScopeIndex );
299 mBlockVariableUpdates = false;
300}
301
303 : QWidget( parent )
304 , mConfigObject( new QgsLayoutConfigObject( this, item ) )
305{
306 setupUi( this );
307
308 mVariableEditor->setMinimumHeight( mVariableEditor->fontMetrics().height() * 15 );
309
310 mItemRotationSpinBox->setClearValue( 0 );
311 mStrokeUnitsComboBox->linkToWidget( mStrokeWidthSpinBox );
312 mStrokeUnitsComboBox->setConverter( &item->layout()->renderContext().measurementConverter() );
313
314 QgsFilterLineEdit *exportGroupLineEdit = new QgsFilterLineEdit();
315 exportGroupLineEdit->setShowClearButton( true );
316 exportGroupLineEdit->setPlaceholderText( tr( "Not set" ) );
317 mExportGroupNameCombo->setLineEdit( exportGroupLineEdit );
318
319 mPosUnitsComboBox->linkToWidget( mXPosSpin );
320 mPosUnitsComboBox->linkToWidget( mYPosSpin );
321 mSizeUnitsComboBox->linkToWidget( mWidthSpin );
322 mSizeUnitsComboBox->linkToWidget( mHeightSpin );
323
324 mPosUnitsComboBox->setConverter( &item->layout()->renderContext().measurementConverter() );
325 mSizeUnitsComboBox->setConverter( &item->layout()->renderContext().measurementConverter() );
326
327 mPosLockAspectRatio->setWidthSpinBox( mXPosSpin );
328 mPosLockAspectRatio->setHeightSpinBox( mYPosSpin );
329 mSizeLockAspectRatio->setWidthSpinBox( mWidthSpin );
330 mSizeLockAspectRatio->setHeightSpinBox( mHeightSpin );
331
332 mItemFrameColorDDBtn->registerLinkedWidget( mFrameColorButton );
333 mItemBackgroundColorDDBtn->registerLinkedWidget( mBackgroundColorButton );
334
335 connect( mFrameColorButton, &QgsColorButton::colorChanged, this, &QgsLayoutItemPropertiesWidget::mFrameColorButton_colorChanged );
336 connect( mBackgroundColorButton, &QgsColorButton::colorChanged, this, &QgsLayoutItemPropertiesWidget::mBackgroundColorButton_colorChanged );
337 connect( mStrokeWidthSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mStrokeWidthSpinBox_valueChanged );
338 connect( mStrokeUnitsComboBox, &QgsLayoutUnitsComboBox::unitChanged, this, &QgsLayoutItemPropertiesWidget::strokeUnitChanged );
339 connect( mFrameGroupBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutItemPropertiesWidget::mFrameGroupBox_toggled );
340 connect( mFrameJoinStyleCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutItemPropertiesWidget::mFrameJoinStyleCombo_currentIndexChanged );
341 connect( mBackgroundGroupBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutItemPropertiesWidget::mBackgroundGroupBox_toggled );
342 connect( mItemIdLineEdit, &QLineEdit::editingFinished, this, &QgsLayoutItemPropertiesWidget::mItemIdLineEdit_editingFinished );
343 connect( mExportGroupNameCombo, &QComboBox::currentTextChanged, this, &QgsLayoutItemPropertiesWidget::exportGroupNameEditingFinished );
344 connect( mPageSpinBox, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mPageSpinBox_valueChanged );
345 connect( mXPosSpin, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mXPosSpin_valueChanged );
346 connect( mYPosSpin, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mYPosSpin_valueChanged );
347 connect( mPosUnitsComboBox, &QgsLayoutUnitsComboBox::unitChanged, this, &QgsLayoutItemPropertiesWidget::positionUnitsChanged );
348 connect( mWidthSpin, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mWidthSpin_valueChanged );
349 connect( mHeightSpin, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mHeightSpin_valueChanged );
350 connect( mSizeUnitsComboBox, &QgsLayoutUnitsComboBox::unitChanged, this, &QgsLayoutItemPropertiesWidget::sizeUnitsChanged );
351 connect( mUpperLeftRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mUpperLeftCheckBox_stateChanged );
352 connect( mUpperMiddleRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mUpperMiddleCheckBox_stateChanged );
353 connect( mUpperRightRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mUpperRightCheckBox_stateChanged );
354 connect( mMiddleLeftRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mMiddleLeftCheckBox_stateChanged );
355 connect( mMiddleRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mMiddleCheckBox_stateChanged );
356 connect( mMiddleRightRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mMiddleRightCheckBox_stateChanged );
357 connect( mLowerLeftRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mLowerLeftCheckBox_stateChanged );
358 connect( mLowerMiddleRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mLowerMiddleCheckBox_stateChanged );
359 connect( mLowerRightRadioButton, &QRadioButton::toggled, this, &QgsLayoutItemPropertiesWidget::mLowerRightCheckBox_stateChanged );
360 connect( mBlendModeCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutItemPropertiesWidget::mBlendModeCombo_currentIndexChanged );
361 connect( mItemRotationSpinBox, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutItemPropertiesWidget::mItemRotationSpinBox_valueChanged );
362 connect( mExcludeFromPrintsCheckBox, &QCheckBox::toggled, this, &QgsLayoutItemPropertiesWidget::mExcludeFromPrintsCheckBox_toggled );
363
364 //make button exclusive
365 QButtonGroup *buttonGroup = new QButtonGroup( this );
366 buttonGroup->addButton( mUpperLeftRadioButton );
367 buttonGroup->addButton( mUpperMiddleRadioButton );
368 buttonGroup->addButton( mUpperRightRadioButton );
369 buttonGroup->addButton( mMiddleLeftRadioButton );
370 buttonGroup->addButton( mMiddleRadioButton );
371 buttonGroup->addButton( mMiddleRightRadioButton );
372 buttonGroup->addButton( mLowerLeftRadioButton );
373 buttonGroup->addButton( mLowerMiddleRadioButton );
374 buttonGroup->addButton( mLowerRightRadioButton );
375 buttonGroup->setExclusive( true );
376
378
379 setItem( item );
380
381 connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsLayoutItemPropertiesWidget::opacityChanged );
382
384 connect( mVariableEditor, &QgsVariableEditorWidget::scopeChanged, this, [this] {
385 if ( !mBlockVariableUpdates )
386 QgsLayoutItemPropertiesWidget::variablesChanged();
387 } );
388 // listen out for variable edits
392
393 if ( item->layout() )
394 {
398 }
399}
400
402{
403 mBackgroundGroupBox->setVisible( showGroup );
404}
405
407{
408 mFrameGroupBox->setVisible( showGroup );
409}
410
412{
413 if ( mItem )
414 {
415 disconnect( mItem, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutItemPropertiesWidget::setValuesForGuiPositionElements );
416 disconnect( mItem, &QgsLayoutObject::changed, this, &QgsLayoutItemPropertiesWidget::setValuesForGuiNonPositionElements );
417 }
418 mItem = item;
419 if ( mItem )
420 {
421 connect( mItem, &QgsLayoutItem::sizePositionChanged, this, &QgsLayoutItemPropertiesWidget::setValuesForGuiPositionElements );
422 connect( mItem, &QgsLayoutObject::changed, this, &QgsLayoutItemPropertiesWidget::setValuesForGuiNonPositionElements );
423 }
424
425 mConfigObject->setObject( mItem );
426
427 setValuesForGuiElements();
428}
429
431{
432 if ( QgsPrintLayout *printLayout = dynamic_cast<QgsPrintLayout *>( masterLayout ) )
433 {
436 }
437}
438
439//slots
440
441void QgsLayoutItemPropertiesWidget::mFrameColorButton_colorChanged( const QColor &newFrameColor )
442{
443 if ( !mItem )
444 {
445 return;
446 }
447 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Frame Color" ), QgsLayoutItem::UndoStrokeColor );
448 mItem->setFrameStrokeColor( newFrameColor );
449 mItem->layout()->undoStack()->endCommand();
450 mItem->update();
451}
452
453void QgsLayoutItemPropertiesWidget::mBackgroundColorButton_colorChanged( const QColor &newBackgroundColor )
454{
455 if ( !mItem )
456 {
457 return;
458 }
459 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Background Color" ), QgsLayoutItem::UndoBackgroundColor );
460 mItem->setBackgroundColor( newBackgroundColor );
461 mItem->layout()->undoStack()->endCommand();
462 mItem->invalidateCache();
463}
464
465void QgsLayoutItemPropertiesWidget::changeItemPosition()
466{
467 if ( !mItem )
468 return;
469
470 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Move Item" ), QgsLayoutItem::UndoIncrementalMove );
471
472 const QgsLayoutPoint point( mXPosSpin->value(), mYPosSpin->value(), mPosUnitsComboBox->unit() );
473 mItem->attemptMove( point, true, false, mPageSpinBox->value() - 1 );
474
475 mItem->layout()->undoStack()->endCommand();
476}
477
478void QgsLayoutItemPropertiesWidget::changeItemReference( QgsLayoutItem::ReferencePoint point )
479{
480 if ( !mItem )
481 return;
482
483 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Item Reference" ) );
484 mItem->setReferencePoint( point );
485 mItem->layout()->undoStack()->endCommand();
486}
487
488void QgsLayoutItemPropertiesWidget::changeItemSize()
489{
490 if ( !mItem )
491 return;
492
493 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Resize Item" ), QgsLayoutItem::UndoIncrementalResize );
494
495 const QgsLayoutSize size( mWidthSpin->value(), mHeightSpin->value(), mSizeUnitsComboBox->unit() );
496 mItem->attemptResize( size );
497
498 mItem->layout()->undoStack()->endCommand();
499}
500
501void QgsLayoutItemPropertiesWidget::variablesChanged()
502{
503 if ( !mItem )
504 return;
505
506 if ( QgsLayoutFrame *frame = qobject_cast<QgsLayoutFrame *>( mItem ) )
507 {
508 if ( QgsLayoutMultiFrame *mf = frame->multiFrame() )
509 {
510 QgsExpressionContextUtils::setLayoutMultiFrameVariables( mf, mVariableEditor->variablesInActiveScope() );
511 }
512 }
513 else
514 {
515 QgsExpressionContextUtils::setLayoutItemVariables( mItem, mVariableEditor->variablesInActiveScope() );
516 }
517}
518
520{
521 if ( mUpperLeftRadioButton->isChecked() )
522 {
524 }
525 else if ( mUpperMiddleRadioButton->isChecked() )
526 {
528 }
529 else if ( mUpperRightRadioButton->isChecked() )
530 {
532 }
533 else if ( mMiddleLeftRadioButton->isChecked() )
534 {
536 }
537 else if ( mMiddleRadioButton->isChecked() )
538 {
540 }
541 else if ( mMiddleRightRadioButton->isChecked() )
542 {
544 }
545 else if ( mLowerLeftRadioButton->isChecked() )
546 {
548 }
549 else if ( mLowerMiddleRadioButton->isChecked() )
550 {
552 }
553 else if ( mLowerRightRadioButton->isChecked() )
554 {
556 }
558}
559
560void QgsLayoutItemPropertiesWidget::mStrokeWidthSpinBox_valueChanged( double d )
561{
562 if ( !mItem )
563 {
564 return;
565 }
566
567 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Frame Stroke Width" ), QgsLayoutItem::UndoStrokeWidth );
568 mItem->setFrameStrokeWidth( QgsLayoutMeasurement( d, mStrokeUnitsComboBox->unit() ) );
569 mItem->layout()->undoStack()->endCommand();
570}
571
572void QgsLayoutItemPropertiesWidget::strokeUnitChanged( Qgis::LayoutUnit unit )
573{
574 if ( !mItem )
575 {
576 return;
577 }
578
579 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Frame Stroke Width" ), QgsLayoutItem::UndoStrokeWidth );
580 mItem->setFrameStrokeWidth( QgsLayoutMeasurement( mStrokeWidthSpinBox->value(), unit ) );
581 mItem->layout()->undoStack()->endCommand();
582}
583
584void QgsLayoutItemPropertiesWidget::mFrameJoinStyleCombo_currentIndexChanged( int index )
585{
586 Q_UNUSED( index )
587 if ( !mItem )
588 {
589 return;
590 }
591
592 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Frame Join Style" ) );
593 mItem->setFrameJoinStyle( mFrameJoinStyleCombo->penJoinStyle() );
594 mItem->layout()->undoStack()->endCommand();
595}
596
597void QgsLayoutItemPropertiesWidget::mFrameGroupBox_toggled( bool state )
598{
599 if ( !mItem )
600 {
601 return;
602 }
603
604 mItem->layout()->undoStack()->beginCommand( mItem, state ? tr( "Enable Frame" ) : tr( "Disable Frame" ) );
605 mItem->setFrameEnabled( state );
606 mItem->update();
607 mItem->layout()->undoStack()->endCommand();
608}
609
610void QgsLayoutItemPropertiesWidget::mBackgroundGroupBox_toggled( bool state )
611{
612 if ( !mItem )
613 {
614 return;
615 }
616
617 mItem->layout()->undoStack()->beginCommand( mItem, state ? tr( "Enable Background" ) : tr( "Disable Background" ) );
618 mItem->setBackgroundEnabled( state );
619 mItem->layout()->undoStack()->endCommand();
620 mItem->invalidateCache();
621}
622
623
624void QgsLayoutItemPropertiesWidget::setValuesForGuiPositionElements()
625{
626 if ( !mItem )
627 {
628 return;
629 }
630
631 auto block = [this]( bool blocked ) {
632 mXPosSpin->blockSignals( blocked );
633 mYPosSpin->blockSignals( blocked );
634 mPosUnitsComboBox->blockSignals( blocked );
635 mWidthSpin->blockSignals( blocked );
636 mHeightSpin->blockSignals( blocked );
637 mSizeUnitsComboBox->blockSignals( blocked );
638 mUpperLeftRadioButton->blockSignals( blocked );
639 mUpperMiddleRadioButton->blockSignals( blocked );
640 mUpperRightRadioButton->blockSignals( blocked );
641 mMiddleLeftRadioButton->blockSignals( blocked );
642 mMiddleRadioButton->blockSignals( blocked );
643 mMiddleRightRadioButton->blockSignals( blocked );
644 mLowerLeftRadioButton->blockSignals( blocked );
645 mLowerMiddleRadioButton->blockSignals( blocked );
646 mLowerRightRadioButton->blockSignals( blocked );
647 mPageSpinBox->blockSignals( blocked );
648 };
649 block( true );
650
651 const QgsLayoutPoint point = mItem->pagePositionWithUnits();
652
653 if ( !mFreezeXPosSpin )
654 mXPosSpin->setValue( point.x() );
655 if ( !mFreezeYPosSpin )
656 mYPosSpin->setValue( point.y() );
657 mPosUnitsComboBox->setUnit( point.units() );
658
659 switch ( mItem->referencePoint() )
660 {
662 {
663 mUpperLeftRadioButton->setChecked( true );
664 break;
665 }
666
668 {
669 mUpperMiddleRadioButton->setChecked( true );
670 break;
671 }
672
674 {
675 mUpperRightRadioButton->setChecked( true );
676 break;
677 }
678
680 {
681 mMiddleLeftRadioButton->setChecked( true );
682 break;
683 }
684
686 {
687 mMiddleRadioButton->setChecked( true );
688 break;
689 }
690
692 {
693 mMiddleRightRadioButton->setChecked( true );
694 break;
695 }
696
698 {
699 mLowerLeftRadioButton->setChecked( true );
700 break;
701 }
702
704 {
705 mLowerMiddleRadioButton->setChecked( true );
706 break;
707 }
708
710 {
711 mLowerRightRadioButton->setChecked( true );
712 break;
713 }
714 }
715
716 const QgsLayoutSize size = mItem->sizeWithUnits();
717 if ( !mFreezeWidthSpin )
718 mWidthSpin->setValue( size.width() );
719 if ( !mFreezeHeightSpin )
720 mHeightSpin->setValue( size.height() );
721
722 mSizeUnitsComboBox->setUnit( size.units() );
723
724 mSizeLockAspectRatio->resetRatio();
725 mPosLockAspectRatio->resetRatio();
726
727 if ( !mFreezePageSpin )
728 mPageSpinBox->setValue( mItem->page() + 1 );
729
730 block( false );
731}
732
733void QgsLayoutItemPropertiesWidget::setValuesForGuiNonPositionElements()
734{
735 if ( !mItem )
736 {
737 return;
738 }
739
740 whileBlocking( mBackgroundColorButton )->setColor( mItem->backgroundColor( false ) );
741 whileBlocking( mFrameColorButton )->setColor( mItem->frameStrokeColor() );
742 whileBlocking( mStrokeUnitsComboBox )->setUnit( mItem->frameStrokeWidth().units() );
743 whileBlocking( mStrokeWidthSpinBox )->setValue( mItem->frameStrokeWidth().length() );
744 whileBlocking( mFrameJoinStyleCombo )->setPenJoinStyle( mItem->frameJoinStyle() );
745 whileBlocking( mItemIdLineEdit )->setText( mItem->id() );
746 whileBlocking( mFrameGroupBox )->setChecked( mItem->frameEnabled() );
747 whileBlocking( mBackgroundGroupBox )->setChecked( mItem->hasBackground() );
748 whileBlocking( mBlendModeCombo )->setBlendMode( mItem->blendMode() );
749 whileBlocking( mOpacityWidget )->setOpacity( mItem->itemOpacity() );
750 whileBlocking( mItemRotationSpinBox )->setValue( mItem->itemRotation() );
751 whileBlocking( mExcludeFromPrintsCheckBox )->setChecked( mItem->excludeFromExports() );
752 whileBlocking( mExportGroupNameCombo )->setCurrentText( mItem->customProperty( u"pdfExportGroup"_s ).toString() );
753}
754
756{
757 mConfigObject->initializeDataDefinedButton( mXPositionDDBtn, QgsLayoutObject::DataDefinedProperty::PositionX );
758 mConfigObject->initializeDataDefinedButton( mYPositionDDBtn, QgsLayoutObject::DataDefinedProperty::PositionY );
759 mConfigObject->initializeDataDefinedButton( mWidthDDBtn, QgsLayoutObject::DataDefinedProperty::ItemWidth );
760 mConfigObject->initializeDataDefinedButton( mHeightDDBtn, QgsLayoutObject::DataDefinedProperty::ItemHeight );
761 mConfigObject->initializeDataDefinedButton( mItemRotationDDBtn, QgsLayoutObject::DataDefinedProperty::ItemRotation );
762 mConfigObject->initializeDataDefinedButton( mOpacityDDBtn, QgsLayoutObject::DataDefinedProperty::Opacity );
763 mConfigObject->initializeDataDefinedButton( mBlendModeDDBtn, QgsLayoutObject::DataDefinedProperty::BlendMode );
764 mConfigObject->initializeDataDefinedButton( mExcludePrintsDDBtn, QgsLayoutObject::DataDefinedProperty::ExcludeFromExports );
765 mConfigObject->initializeDataDefinedButton( mItemFrameColorDDBtn, QgsLayoutObject::DataDefinedProperty::FrameColor );
766 mConfigObject->initializeDataDefinedButton( mItemBackgroundColorDDBtn, QgsLayoutObject::DataDefinedProperty::BackgroundColor );
767}
768
770{
771 const QList<QgsPropertyOverrideButton *> buttons = findChildren<QgsPropertyOverrideButton *>();
772 for ( QgsPropertyOverrideButton *button : buttons )
773 {
774 mConfigObject->updateDataDefinedButton( button );
775 }
776}
777
778void QgsLayoutItemPropertiesWidget::setValuesForGuiElements()
779{
780 if ( !mItem )
781 {
782 return;
783 }
784
785 mBackgroundColorButton->setColorDialogTitle( tr( "Select Background Color" ) );
786 mBackgroundColorButton->setAllowOpacity( true );
787 mBackgroundColorButton->setContext( u"composer"_s );
788 mFrameColorButton->setColorDialogTitle( tr( "Select Frame Color" ) );
789 mFrameColorButton->setAllowOpacity( true );
790 mFrameColorButton->setContext( u"composer"_s );
791
792 if ( QgsLayout *layout = mItem->layout() )
793 {
794 // collect export groups from layout, so that we can offer auto completion in the PDF export group combo
795 QList<QgsLayoutItem *> items;
796 layout->layoutItems( items );
797 QStringList existingGroups;
798 for ( const QgsLayoutItem *item : std::as_const( items ) )
799 {
800 const QString groupName = item->customProperty( u"pdfExportGroup"_s ).toString();
801 if ( !groupName.isEmpty() && !existingGroups.contains( groupName ) )
802 existingGroups.append( groupName );
803 }
804
805 std::sort( existingGroups.begin(), existingGroups.end(), []( const QString &a, const QString &b ) -> bool {
806 return a.localeAwareCompare( b ) < 0;
807 } );
808
809 whileBlocking( mExportGroupNameCombo )->clear();
810 whileBlocking( mExportGroupNameCombo )->addItems( existingGroups );
811 }
812
813 setValuesForGuiPositionElements();
814 setValuesForGuiNonPositionElements();
816
818}
819
820void QgsLayoutItemPropertiesWidget::mBlendModeCombo_currentIndexChanged( int index )
821{
822 Q_UNUSED( index )
823 if ( mItem )
824 {
825 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Blend Mode" ) );
826 mItem->setBlendMode( mBlendModeCombo->blendMode() );
827 mItem->layout()->undoStack()->endCommand();
828 }
829}
830
831void QgsLayoutItemPropertiesWidget::opacityChanged( double value )
832{
833 if ( mItem )
834 {
835 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Opacity" ), QgsLayoutItem::UndoOpacity );
836 mItem->setItemOpacity( value );
837 mItem->layout()->undoStack()->endCommand();
838 }
839}
840
841void QgsLayoutItemPropertiesWidget::mItemIdLineEdit_editingFinished()
842{
843 if ( mItem )
844 {
845 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Item ID" ), QgsLayoutItem::UndoSetId );
846 mItem->setId( mItemIdLineEdit->text() );
847 mItemIdLineEdit->setText( mItem->id() );
848 mItem->layout()->undoStack()->endCommand();
849 }
850}
851
852void QgsLayoutItemPropertiesWidget::exportGroupNameEditingFinished()
853{
854 if ( mItem )
855 {
856 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Export Group Name" ), QgsLayoutItem::UndoExportLayerName );
857 mItem->setCustomProperty( u"pdfExportGroup"_s, mExportGroupNameCombo->currentText() );
858 mItem->layout()->undoStack()->endCommand();
859 }
860}
861
862void QgsLayoutItemPropertiesWidget::mPageSpinBox_valueChanged( int )
863{
864 mFreezePageSpin = true;
865 changeItemPosition();
866 mFreezePageSpin = false;
867}
868
869void QgsLayoutItemPropertiesWidget::mXPosSpin_valueChanged( double )
870{
871 mFreezeXPosSpin = true;
872 changeItemPosition();
873 mFreezeXPosSpin = false;
874}
875
876void QgsLayoutItemPropertiesWidget::mYPosSpin_valueChanged( double )
877{
878 mFreezeYPosSpin = true;
879 changeItemPosition();
880 mFreezeYPosSpin = false;
881}
882
883void QgsLayoutItemPropertiesWidget::positionUnitsChanged( Qgis::LayoutUnit )
884{
885 changeItemPosition();
886}
887
888void QgsLayoutItemPropertiesWidget::mWidthSpin_valueChanged( double )
889{
890 mFreezeWidthSpin = true;
891 changeItemSize();
892 mFreezeWidthSpin = false;
893}
894
895void QgsLayoutItemPropertiesWidget::mHeightSpin_valueChanged( double )
896{
897 mFreezeHeightSpin = true;
898 changeItemSize();
899 mFreezeHeightSpin = false;
900}
901
902void QgsLayoutItemPropertiesWidget::sizeUnitsChanged( Qgis::LayoutUnit )
903{
904 changeItemSize();
905}
906
907void QgsLayoutItemPropertiesWidget::mUpperLeftCheckBox_stateChanged( bool state )
908{
909 if ( !state )
910 return;
911
912 if ( mItem )
913 {
914 changeItemReference( QgsLayoutItem::UpperLeft );
915 }
916 setValuesForGuiPositionElements();
917}
918
919void QgsLayoutItemPropertiesWidget::mUpperMiddleCheckBox_stateChanged( bool state )
920{
921 if ( !state )
922 return;
923 if ( mItem )
924 {
925 changeItemReference( QgsLayoutItem::UpperMiddle );
926 }
927 setValuesForGuiPositionElements();
928}
929
930void QgsLayoutItemPropertiesWidget::mUpperRightCheckBox_stateChanged( bool state )
931{
932 if ( !state )
933 return;
934 if ( mItem )
935 {
936 changeItemReference( QgsLayoutItem::UpperRight );
937 }
938 setValuesForGuiPositionElements();
939}
940
941void QgsLayoutItemPropertiesWidget::mMiddleLeftCheckBox_stateChanged( bool state )
942{
943 if ( !state )
944 return;
945 if ( mItem )
946 {
947 changeItemReference( QgsLayoutItem::MiddleLeft );
948 }
949 setValuesForGuiPositionElements();
950}
951
952void QgsLayoutItemPropertiesWidget::mMiddleCheckBox_stateChanged( bool state )
953{
954 if ( !state )
955 return;
956 if ( mItem )
957 {
958 changeItemReference( QgsLayoutItem::Middle );
959 }
960 setValuesForGuiPositionElements();
961}
962
963void QgsLayoutItemPropertiesWidget::mMiddleRightCheckBox_stateChanged( bool state )
964{
965 if ( !state )
966 return;
967 if ( mItem )
968 {
969 changeItemReference( QgsLayoutItem::MiddleRight );
970 }
971 setValuesForGuiPositionElements();
972}
973
974void QgsLayoutItemPropertiesWidget::mLowerLeftCheckBox_stateChanged( bool state )
975{
976 if ( !state )
977 return;
978 if ( mItem )
979 {
980 changeItemReference( QgsLayoutItem::LowerLeft );
981 }
982 setValuesForGuiPositionElements();
983}
984
985void QgsLayoutItemPropertiesWidget::mLowerMiddleCheckBox_stateChanged( bool state )
986{
987 if ( !state )
988 return;
989 if ( mItem )
990 {
991 changeItemReference( QgsLayoutItem::LowerMiddle );
992 }
993 setValuesForGuiPositionElements();
994}
995
996void QgsLayoutItemPropertiesWidget::mLowerRightCheckBox_stateChanged( bool state )
997{
998 if ( !state )
999 return;
1000 if ( mItem )
1001 {
1002 changeItemReference( QgsLayoutItem::LowerRight );
1003 }
1004 setValuesForGuiPositionElements();
1005}
1006
1007void QgsLayoutItemPropertiesWidget::mItemRotationSpinBox_valueChanged( double val )
1008{
1009 if ( mItem )
1010 {
1011 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Rotate" ), QgsLayoutItem::UndoRotation );
1012 mItem->setItemRotation( val, true );
1013 mItem->update();
1014 mItem->layout()->undoStack()->endCommand();
1015 }
1016}
1017
1018void QgsLayoutItemPropertiesWidget::mExcludeFromPrintsCheckBox_toggled( bool checked )
1019{
1020 if ( mItem )
1021 {
1022 mItem->layout()->undoStack()->beginCommand( mItem, checked ? tr( "Exclude from Exports" ) : tr( "Include in Exports" ) );
1023 mItem->setExcludeFromExports( checked );
1024 mItem->layout()->undoStack()->endCommand();
1025 }
1026}
LayoutUnit
Layout measurement units.
Definition qgis.h:5275
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.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
void setShowClearButton(bool visible)
Sets whether the widget's clear button is visible.
A button for customizing QgsTextFormat settings.
Used to render QgsLayout as an atlas, by iterating over the features from an associated vector layer.
void toggled(bool enabled)
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.
@ UndoExportLayerName
Export layer name.
@ 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.
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.
@ ExcludeFromExports
Exclude item from exports.
@ BackgroundColor
Item background 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.
double x() const
Returns x coordinate of point.
double y() const
Returns y coordinate of point.
Qgis::LayoutUnit units() const
Returns the units for the 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.
QgsVectorLayer * layer() const
Returns the vector layer associated with the layout's context.
double height() const
Returns the height of the size.
Qgis::LayoutUnit units() const
Returns the units for the size.
double width() const
Returns the width of the size.
void unitChanged(Qgis::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:50
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
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 ...
QgsProject * project() const
The project associated with the layout.
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....
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an 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.
void scopeChanged()
Emitted when the user has modified a scope using the widget.
Represents a vector layer which manages a vector based dataset.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:6804