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