QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgseffectstackpropertieswidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgseffectstackpropertieswidget.h
3 --------------------------------
4 begin : January 2015
5 copyright : (C) 2015 by Nyall Dawson
6 email : nyall dot dawson at gmail.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
17
18#include "qgsapplication.h"
19#include "qgseffectstack.h"
20#include "qgshelp.h"
21#include "qgspainteffect.h"
25#include "qgspanelwidget.h"
26#include "qgssymbollayerutils.h"
27
28#include <QCheckBox>
29#include <QPainter>
30#include <QPicture>
31#include <QStandardItem>
32#include <QStandardItemModel>
33#include <QString>
34#include <QToolButton>
35
36#include "moc_qgseffectstackpropertieswidget.cpp"
37
38using namespace Qt::StringLiterals;
39
41
42static const int EFFECT_ITEM_TYPE = QStandardItem::UserType + 1;
43
44class EffectItem : public QStandardItem
45{
46 public:
47 EffectItem( QgsPaintEffect *effect, QgsEffectStackPropertiesWidget *propertiesWidget )
48 {
49 setEffect( effect );
50 setCheckable( true );
51 mWidget = propertiesWidget;
52 }
53
54 void setEffect( QgsPaintEffect *effect )
55 {
56 mEffect = effect;
57 emitDataChanged();
58 }
59
60 int type() const override { return EFFECT_ITEM_TYPE; }
61
62 QgsPaintEffect *effect()
63 {
64 return mEffect;
65 }
66
67 QVariant data( int role ) const override
68 {
69 if ( role == Qt::DisplayRole || role == Qt::EditRole )
70 {
72 }
73 if ( role == Qt::CheckStateRole )
74 {
75 return mEffect->enabled() ? Qt::Checked : Qt::Unchecked;
76 }
77 return QStandardItem::data( role );
78 }
79
80 void setData( const QVariant &value, int role ) override
81 {
82 if ( role == Qt::CheckStateRole )
83 {
84 mEffect->setEnabled( value.toBool() );
85 mWidget->updatePreview();
86 }
87 else
88 {
89 QStandardItem::setData( value, role );
90 }
91 }
92
93 protected:
94 QgsPaintEffect *mEffect = nullptr;
95 QgsEffectStackPropertiesWidget *mWidget = nullptr;
96};
98
99//
100// QgsEffectStackPropertiesWidget
101//
102
104 : QgsPanelWidget( parent )
105 , mStack( stack )
106
107{
108// TODO
109#ifdef Q_OS_MAC
110 //setWindowModality( Qt::WindowModal );
111#endif
112
113 mPresentWidget = nullptr;
114
115 setupUi( this );
116 this->layout()->setContentsMargins( 0, 0, 0, 0 );
117
118 mEffectsList->setMaximumHeight( static_cast<int>( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 7 ) );
119 mEffectsList->setMinimumHeight( mEffectsList->maximumHeight() );
120 lblPreview->setMaximumWidth( mEffectsList->maximumHeight() );
121
122 mAddButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
123 mRemoveButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
124 mUpButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
125 mDownButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
126
127 mModel = new QStandardItemModel();
128 // Set the effect
129 mEffectsList->setModel( mModel );
130
131 QItemSelectionModel *selModel = mEffectsList->selectionModel();
132 connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsEffectStackPropertiesWidget::effectChanged );
133
134 loadStack( stack );
136
137 connect( mUpButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::moveEffectUp );
138 connect( mDownButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::moveEffectDown );
139 connect( mAddButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::addEffect );
140 connect( mRemoveButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::removeEffect );
141
142 updateUi();
143
144 // set first selected effect as active item in the tree
145 int initialRow = 0;
146 for ( int i = 0; i < stack->count(); ++i )
147 {
148 // list shows effects in opposite order to stack
149 if ( stack->effect( stack->count() - i - 1 )->enabled() )
150 {
151 initialRow = i;
152 break;
153 }
154 }
155 const QModelIndex newIndex = mEffectsList->model()->index( initialRow, 0 );
156 mEffectsList->setCurrentIndex( newIndex );
157
158 setPanelTitle( tr( "Effects Properties" ) );
159}
160
162
164{
165 mPreviewPicture = picture;
167}
168
170{
171 if ( !stack )
172 {
173 return;
174 }
175
176 EffectItem *parent = static_cast<EffectItem *>( mModel->invisibleRootItem() );
177
178 const int count = stack->count();
179 for ( int i = count - 1; i >= 0; i-- )
180 {
181 EffectItem *effectItem = new EffectItem( stack->effect( i ), this );
182 effectItem->setEditable( false );
183 parent->appendRow( effectItem );
184 }
185}
186
187
193
195{
196 const QModelIndex currentIdx = mEffectsList->currentIndex();
197 if ( !currentIdx.isValid() )
198 return;
199
200 EffectItem *item = static_cast<EffectItem *>( mModel->itemFromIndex( currentIdx ) );
201
202 QStandardItem *root = mModel->invisibleRootItem();
203 const int rowCount = root->rowCount();
204 const int currentRow = item ? item->row() : 0;
205
206 mUpButton->setEnabled( currentRow > 0 );
207 mDownButton->setEnabled( currentRow < rowCount - 1 );
208 mRemoveButton->setEnabled( rowCount > 1 );
209}
210
212{
213 QPainter painter;
214 QImage previewImage( 100, 100, QImage::Format_ARGB32 );
215 previewImage.fill( Qt::transparent );
216 painter.begin( &previewImage );
217 painter.setRenderHint( QPainter::Antialiasing );
220 if ( mPreviewPicture.isNull() )
221 {
222 QPicture previewPic;
223 QPainter previewPicPainter;
224 previewPicPainter.begin( &previewPic );
225 previewPicPainter.setPen( Qt::red );
226 previewPicPainter.setBrush( QColor( 255, 100, 100, 255 ) );
227 previewPicPainter.drawEllipse( QPoint( 50, 50 ), 20, 20 );
228 previewPicPainter.end();
229 mStack->render( previewPic, context );
230 }
231 else
232 {
233 context.painter()->translate( 20, 20 );
234 mStack->render( mPreviewPicture, context );
235 }
236 painter.end();
237
238 lblPreview->setPixmap( QPixmap::fromImage( previewImage ) );
239 emit widgetChanged();
240}
241
243{
244 const QModelIndex idx = mEffectsList->currentIndex();
245 if ( !idx.isValid() )
246 return nullptr;
247
248 EffectItem *item = static_cast<EffectItem *>( mModel->itemFromIndex( idx ) );
249 return item;
250}
251
253{
254 updateUi();
255
256 EffectItem *currentItem = currentEffectItem();
257 if ( !currentItem )
258 return;
259
260 QgsPaintEffectPropertiesWidget *effectPropertiesWidget = new QgsPaintEffectPropertiesWidget( currentItem->effect() );
261 setWidget( effectPropertiesWidget );
262
265}
266
268{
269 const int index = stackedWidget->addWidget( widget );
270 stackedWidget->setCurrentIndex( index );
271 if ( mPresentWidget )
272 {
273 stackedWidget->removeWidget( mPresentWidget );
274 QWidget *dummy = mPresentWidget;
275 mPresentWidget = widget;
276 delete dummy; // auto disconnects all signals
277 }
278}
279
281{
282 QgsPaintEffect *newEffect = new QgsDrawSourceEffect();
283 mStack->insertEffect( 0, newEffect );
284
285 EffectItem *newEffectItem = new EffectItem( newEffect, this );
286 mModel->invisibleRootItem()->insertRow( mStack->count() - 1, newEffectItem );
287
288 mEffectsList->setCurrentIndex( mModel->indexFromItem( newEffectItem ) );
289 updateUi();
291}
292
294{
295 EffectItem *item = currentEffectItem();
296 const int row = item->row();
297 QStandardItem *root = mModel->invisibleRootItem();
298
299 const int layerIdx = root->rowCount() - row - 1;
300 QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
301
302 mModel->invisibleRootItem()->removeRow( row );
303
304 const int newSelection = std::min( row, root->rowCount() - 1 );
305 const QModelIndex newIdx = root->child( newSelection )->index();
306 mEffectsList->setCurrentIndex( newIdx );
307
308 updateUi();
310
311 delete tmpEffect;
312}
313
318
323
325{
326 EffectItem *item = currentEffectItem();
327 if ( !item )
328 return;
329
330 const int row = item->row();
331
332 QStandardItem *root = mModel->invisibleRootItem();
333
334 const int layerIdx = root->rowCount() - row - 1;
335 // switch effects
336 QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
337 mStack->insertEffect( layerIdx - offset, tmpEffect );
338
339 QList<QStandardItem *> toMove = root->takeRow( row );
340 root->insertRows( row + offset, toMove );
341
342 const QModelIndex newIdx = toMove[0]->index();
343 mEffectsList->setCurrentIndex( newIdx );
344
346 updateUi();
347}
348
350{
351 EffectItem *item = currentEffectItem();
352 item->setEffect( newEffect );
353
354 QStandardItem *root = mModel->invisibleRootItem();
355 const int effectIdx = root->rowCount() - item->row() - 1;
356 mStack->changeEffect( effectIdx, newEffect );
357
359 // Important: This lets the effect to have its own effect properties widget
361}
362
363
364//
365// QgsEffectStackPropertiesDialog
366//
367
369 : QgsDialog( parent, f, QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok )
370
371{
372 setWindowTitle( tr( "Effect Properties" ) );
374
375 QDialogButtonBox *buttonBox = this->findChild<QDialogButtonBox *>( QString(), Qt::FindDirectChildrenOnly );
376 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsEffectStackPropertiesDialog::showHelp );
377
378 layout()->addWidget( mPropertiesWidget );
379}
380
385
387{
388 mPropertiesWidget->setPreviewPicture( picture );
389}
390
391void QgsEffectStackPropertiesDialog::showHelp()
392{
393 QgsHelp::openHelp( u"working_with_vector/vector_properties.html#draw-effects"_s );
394}
395
396
397//
398// QgsEffectStackCompactWidget
399//
400
402 : QgsPanelWidget( parent )
403
404{
405 QHBoxLayout *layout = new QHBoxLayout();
406 layout->setContentsMargins( 0, 0, 0, 0 );
407 layout->setSpacing( 6 );
408 setLayout( layout );
409
410 mEnabledCheckBox = new QCheckBox( this );
411 mEnabledCheckBox->setText( tr( "Draw effects" ) );
412 layout->addWidget( mEnabledCheckBox );
413
414 mButton = new QToolButton( this );
415 mButton->setIcon( QgsApplication::getThemeIcon( u"mIconPaintEffects.svg"_s ) );
416 mButton->setToolTip( tr( "Customize effects" ) );
417 layout->addWidget( mButton );
418
419 setFocusPolicy( Qt::StrongFocus );
420 setFocusProxy( mEnabledCheckBox );
421
422 connect( mButton, &QAbstractButton::clicked, this, &QgsEffectStackCompactWidget::showDialog );
423 connect( mEnabledCheckBox, &QAbstractButton::toggled, this, &QgsEffectStackCompactWidget::enableToggled );
424
425 setPaintEffect( effect );
426}
427
429
431{
432 if ( !effect )
433 {
434 mEnabledCheckBox->setChecked( false );
435 mEnabledCheckBox->setEnabled( false );
436 mButton->setEnabled( false );
437 mStack = nullptr;
438 return;
439 }
440
441 //is effect a stack?
442 QgsEffectStack *stack = dynamic_cast<QgsEffectStack *>( effect );
443 if ( !stack )
444 {
445 //not already a stack, so promote to stack
446 stack = new QgsEffectStack( *effect );
447 }
448
449 mStack = stack;
450 mEnabledCheckBox->setChecked( mStack->enabled() );
451 mEnabledCheckBox->setEnabled( true );
452 mButton->setEnabled( mStack->enabled() );
453}
454
456{
457 return mStack;
458}
459
461{
462 mPreviewPicture = picture;
463}
464
465void QgsEffectStackCompactWidget::showDialog()
466{
467 if ( !mStack )
468 return;
469
470 QgsEffectStack *clone = mStack->clone();
471 QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( qobject_cast<QWidget *>( parent() ) );
472 if ( panel && panel->dockMode() )
473 {
475 widget->setPreviewPicture( mPreviewPicture );
476
477 connect( widget, &QgsPanelWidget::widgetChanged, this, &QgsEffectStackCompactWidget::updateEffectLive );
478 connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsEffectStackCompactWidget::updateAcceptWidget );
479 panel->openPanel( widget );
480 }
481 else
482 {
483 QgsEffectStackPropertiesDialog dlg( clone, this );
484 dlg.setPreviewPicture( mPreviewPicture );
485
486 if ( dlg.exec() == QDialog::Accepted )
487 {
488 *mStack = *clone;
489 emit changed();
490 }
491 }
492}
493
494void QgsEffectStackCompactWidget::enableToggled( bool checked )
495{
496 if ( !mStack )
497 {
498 return;
499 }
500
501 mStack->setEnabled( checked );
502 mButton->setEnabled( checked );
503 emit changed();
504}
505
506void QgsEffectStackCompactWidget::updateAcceptWidget( QgsPanelWidget *panel )
507{
508 QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( panel );
509 *mStack = *widget->stack();
510 emit changed();
511 // delete widget->stack();
512}
513
514void QgsEffectStackCompactWidget::updateEffectLive()
515{
516 QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( sender() );
517 *mStack = *widget->stack();
518 emit changed();
519}
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
Definition qgis.h:2818
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:6499
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application's paint effect registry, used for managing paint effects.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
QDialogButtonBox * buttonBox()
Returns the button box.
Definition qgsdialog.h:45
QgsDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags, QDialogButtonBox::StandardButtons buttons=QDialogButtonBox::Close, Qt::Orientation orientation=Qt::Horizontal)
Constructor for QgsDialog.
Definition qgsdialog.cpp:22
QVBoxLayout * layout()
Returns the central layout. Widgets added to it must have this dialog as parent.
Definition qgsdialog.h:43
A paint effect which draws the source picture with minor or no alterations.
~QgsEffectStackCompactWidget() override
QgsEffectStackCompactWidget(QWidget *parent=nullptr, QgsPaintEffect *effect=nullptr)
QgsEffectStackCompactWidget constructor.
void setPreviewPicture(const QPicture &picture)
Sets the picture to use for effect previews for the dialog.
QgsPaintEffect * paintEffect() const
Returns paint effect attached to the widget.
void setPaintEffect(QgsPaintEffect *effect)
Sets paint effect attached to the widget,.
void changed()
Emitted when the paint effect properties change.
void setPreviewPicture(const QPicture &picture)
Sets the picture to use for effect previews for the dialog.
QgsEffectStackPropertiesWidget * mPropertiesWidget
QgsEffectStackPropertiesDialog(QgsEffectStack *stack, QWidget *parent=nullptr, Qt::WindowFlags f=Qt::WindowFlags())
QgsEffectStackPropertiesDialog constructor.
QgsEffectStack * stack()
Returns effect stack attached to the dialog.
A widget for modifying the properties of a QgsEffectStack, including adding and reordering effects wi...
void moveEffectByOffset(int offset)
Moves the currently selected effect within the stack by a specified offset.
void loadStack()
Refreshes the widget to reflect the current state of the stack.
void moveEffectUp()
Moves the currently selected effect up in the stack.
QgsEffectStackPropertiesWidget(QgsEffectStack *stack, QWidget *parent=nullptr)
QgsEffectStackPropertiesWidget constructor.
void updateUi()
Enables or disables widgets depending on the selected effect within the stack.
void updatePreview()
Updates the effect preview icon.
void changeEffect(QgsPaintEffect *newEffect)
Updates the effect stack when the currently selected effect changes properties.
QgsEffectStack * stack()
Returns effect stack attached to the widget.
~QgsEffectStackPropertiesWidget() override
void addEffect()
Adds a new effect to the stack.
void effectChanged()
Updates the widget when the selected effect changes type.
void setPreviewPicture(const QPicture &picture)
Sets the picture to use for effect previews for the dialog.
void setWidget(QWidget *widget)
Sets the effect properties widget.
EffectItem * currentEffectItem()
Returns the currently selected effect within the stack.
void removeEffect()
Removes the currently selected effect from the stack.
void moveEffectDown()
Moves the currently selected effect down in the stack.
A paint effect which consists of a stack of other chained paint effects.
QgsEffectStack * clone() const override
Duplicates an effect by creating a deep copy of the effect.
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:41
QString visibleName() const
Returns the user visible string representing the paint effect class.
A widget which modifies the properties of a QgsPaintEffect.
void changeEffect(QgsPaintEffect *effect)
Emitted when paint effect type changes.
void changed()
Emitted when paint effect properties changes.
QgsPaintEffectAbstractMetadata * effectMetadata(const QString &name) const
Returns the metadata for a specific effect.
Base class for visual effects which can be applied to QPicture drawings.
void setEnabled(bool enabled)
Sets whether the effect is enabled.
Base class for any widget that can be shown as an inline panel.
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
bool dockMode() const
Returns the dock mode state.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an inline panel.
void widgetChanged()
Emitted when the widget state changes.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected).
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.