QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
205
207{
208 QgsLayoutObject *oldObject = mObject;
209 QgsLayoutConfigObject *oldConfigObject = mConfigObject;
210 // have to set new mObject/mConfigObject here, because setNewItem methods require access to them
211 mObject = item;
212 mConfigObject = new QgsLayoutConfigObject( this, mObject );
213 if ( setNewItem( item ) )
214 {
215 oldConfigObject->deleteLater();
216 return true;
217 }
218 else
219 {
220 // revert object change since it was unsuccessful
221 mObject = oldObject;
222 mConfigObject->deleteLater();
223 mConfigObject = oldConfigObject;
224 return false;
225 }
226}
227
230
232{
233 const auto symbolButtonWidgets = findChildren<QgsSymbolButton *>();
234 for ( QgsSymbolButton *symbolWidget : symbolButtonWidgets )
235 {
236 symbolWidget->setMessageBar( iface->messageBar() );
237 }
238 const auto fontButtonWidgets = findChildren<QgsFontButton *>();
239 for ( QgsFontButton *fontButton : fontButtonWidgets )
240 {
241 fontButton->setMessageBar( iface->messageBar() );
242 }
243}
244
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( u"pdfExportGroup"_s ).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( u"composer"_s );
785 mFrameColorButton->setColorDialogTitle( tr( "Select Frame Color" ) );
786 mFrameColorButton->setAllowOpacity( true );
787 mFrameColorButton->setContext( u"composer"_s );
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( u"pdfExportGroup"_s ).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 { return a.localeAwareCompare( b ) < 0; } );
803
804 whileBlocking( mExportGroupNameCombo )->clear();
805 whileBlocking( mExportGroupNameCombo )->addItems( existingGroups );
806 }
807
808 setValuesForGuiPositionElements();
809 setValuesForGuiNonPositionElements();
811
813}
814
815void QgsLayoutItemPropertiesWidget::mBlendModeCombo_currentIndexChanged( int index )
816{
817 Q_UNUSED( index )
818 if ( mItem )
819 {
820 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Blend Mode" ) );
821 mItem->setBlendMode( mBlendModeCombo->blendMode() );
822 mItem->layout()->undoStack()->endCommand();
823 }
824}
825
826void QgsLayoutItemPropertiesWidget::opacityChanged( double value )
827{
828 if ( mItem )
829 {
830 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Opacity" ), QgsLayoutItem::UndoOpacity );
831 mItem->setItemOpacity( value );
832 mItem->layout()->undoStack()->endCommand();
833 }
834}
835
836void QgsLayoutItemPropertiesWidget::mItemIdLineEdit_editingFinished()
837{
838 if ( mItem )
839 {
840 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Item ID" ), QgsLayoutItem::UndoSetId );
841 mItem->setId( mItemIdLineEdit->text() );
842 mItemIdLineEdit->setText( mItem->id() );
843 mItem->layout()->undoStack()->endCommand();
844 }
845}
846
847void QgsLayoutItemPropertiesWidget::exportGroupNameEditingFinished()
848{
849 if ( mItem )
850 {
851 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Change Export Group Name" ), QgsLayoutItem::UndoExportLayerName );
852 mItem->setCustomProperty( u"pdfExportGroup"_s, mExportGroupNameCombo->currentText() );
853 mItem->layout()->undoStack()->endCommand();
854 }
855}
856
857void QgsLayoutItemPropertiesWidget::mPageSpinBox_valueChanged( int )
858{
859 mFreezePageSpin = true;
860 changeItemPosition();
861 mFreezePageSpin = false;
862}
863
864void QgsLayoutItemPropertiesWidget::mXPosSpin_valueChanged( double )
865{
866 mFreezeXPosSpin = true;
867 changeItemPosition();
868 mFreezeXPosSpin = false;
869}
870
871void QgsLayoutItemPropertiesWidget::mYPosSpin_valueChanged( double )
872{
873 mFreezeYPosSpin = true;
874 changeItemPosition();
875 mFreezeYPosSpin = false;
876}
877
878void QgsLayoutItemPropertiesWidget::positionUnitsChanged( Qgis::LayoutUnit )
879{
880 changeItemPosition();
881}
882
883void QgsLayoutItemPropertiesWidget::mWidthSpin_valueChanged( double )
884{
885 mFreezeWidthSpin = true;
886 changeItemSize();
887 mFreezeWidthSpin = false;
888}
889
890void QgsLayoutItemPropertiesWidget::mHeightSpin_valueChanged( double )
891{
892 mFreezeHeightSpin = true;
893 changeItemSize();
894 mFreezeHeightSpin = false;
895}
896
897void QgsLayoutItemPropertiesWidget::sizeUnitsChanged( Qgis::LayoutUnit )
898{
899 changeItemSize();
900}
901
902void QgsLayoutItemPropertiesWidget::mUpperLeftCheckBox_stateChanged( bool state )
903{
904 if ( !state )
905 return;
906
907 if ( mItem )
908 {
909 changeItemReference( QgsLayoutItem::UpperLeft );
910 }
911 setValuesForGuiPositionElements();
912}
913
914void QgsLayoutItemPropertiesWidget::mUpperMiddleCheckBox_stateChanged( bool state )
915{
916 if ( !state )
917 return;
918 if ( mItem )
919 {
920 changeItemReference( QgsLayoutItem::UpperMiddle );
921 }
922 setValuesForGuiPositionElements();
923}
924
925void QgsLayoutItemPropertiesWidget::mUpperRightCheckBox_stateChanged( bool state )
926{
927 if ( !state )
928 return;
929 if ( mItem )
930 {
931 changeItemReference( QgsLayoutItem::UpperRight );
932 }
933 setValuesForGuiPositionElements();
934}
935
936void QgsLayoutItemPropertiesWidget::mMiddleLeftCheckBox_stateChanged( bool state )
937{
938 if ( !state )
939 return;
940 if ( mItem )
941 {
942 changeItemReference( QgsLayoutItem::MiddleLeft );
943 }
944 setValuesForGuiPositionElements();
945}
946
947void QgsLayoutItemPropertiesWidget::mMiddleCheckBox_stateChanged( bool state )
948{
949 if ( !state )
950 return;
951 if ( mItem )
952 {
953 changeItemReference( QgsLayoutItem::Middle );
954 }
955 setValuesForGuiPositionElements();
956}
957
958void QgsLayoutItemPropertiesWidget::mMiddleRightCheckBox_stateChanged( bool state )
959{
960 if ( !state )
961 return;
962 if ( mItem )
963 {
964 changeItemReference( QgsLayoutItem::MiddleRight );
965 }
966 setValuesForGuiPositionElements();
967}
968
969void QgsLayoutItemPropertiesWidget::mLowerLeftCheckBox_stateChanged( bool state )
970{
971 if ( !state )
972 return;
973 if ( mItem )
974 {
975 changeItemReference( QgsLayoutItem::LowerLeft );
976 }
977 setValuesForGuiPositionElements();
978}
979
980void QgsLayoutItemPropertiesWidget::mLowerMiddleCheckBox_stateChanged( bool state )
981{
982 if ( !state )
983 return;
984 if ( mItem )
985 {
986 changeItemReference( QgsLayoutItem::LowerMiddle );
987 }
988 setValuesForGuiPositionElements();
989}
990
991void QgsLayoutItemPropertiesWidget::mLowerRightCheckBox_stateChanged( bool state )
992{
993 if ( !state )
994 return;
995 if ( mItem )
996 {
997 changeItemReference( QgsLayoutItem::LowerRight );
998 }
999 setValuesForGuiPositionElements();
1000}
1001
1002void QgsLayoutItemPropertiesWidget::mItemRotationSpinBox_valueChanged( double val )
1003{
1004 if ( mItem )
1005 {
1006 mItem->layout()->undoStack()->beginCommand( mItem, tr( "Rotate" ), QgsLayoutItem::UndoRotation );
1007 mItem->setItemRotation( val, true );
1008 mItem->update();
1009 mItem->layout()->undoStack()->endCommand();
1010 }
1011}
1012
1013void QgsLayoutItemPropertiesWidget::mExcludeFromPrintsCheckBox_toggled( bool checked )
1014{
1015 if ( mItem )
1016 {
1017 mItem->layout()->undoStack()->beginCommand( mItem, checked ? tr( "Exclude from Exports" ) : tr( "Include in Exports" ) );
1018 mItem->setExcludeFromExports( checked );
1019 mItem->layout()->undoStack()->endCommand();
1020 }
1021}
LayoutUnit
Layout measurement units.
Definition qgis.h:5360
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:6880