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