QGIS API Documentation 3.41.0-Master (652554233d6)
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#include "moc_qgseffectstackpropertieswidget.cpp"
19#include "qgspainteffect.h"
20#include "qgseffectstack.h"
23#include "qgsapplication.h"
24#include "qgssymbollayerutils.h"
25#include "qgspanelwidget.h"
26#include "qgshelp.h"
27
28#include <QPicture>
29#include <QPainter>
30#include <QStandardItemModel>
31#include <QStandardItem>
32#include <QCheckBox>
33#include <QToolButton>
34
36
37static const int EFFECT_ITEM_TYPE = QStandardItem::UserType + 1;
38
39class EffectItem : public QStandardItem
40{
41 public:
42 EffectItem( QgsPaintEffect *effect, QgsEffectStackPropertiesWidget *propertiesWidget )
43 {
44 setEffect( effect );
45 setCheckable( true );
46 mWidget = propertiesWidget;
47 }
48
49 void setEffect( QgsPaintEffect *effect )
50 {
51 mEffect = effect;
52 emitDataChanged();
53 }
54
55 int type() const override { return EFFECT_ITEM_TYPE; }
56
57 QgsPaintEffect *effect()
58 {
59 return mEffect;
60 }
61
62 QVariant data( int role ) const override
63 {
64 if ( role == Qt::DisplayRole || role == Qt::EditRole )
65 {
67 }
68 if ( role == Qt::CheckStateRole )
69 {
70 return mEffect->enabled() ? Qt::Checked : Qt::Unchecked;
71 }
72 return QStandardItem::data( role );
73 }
74
75 void setData( const QVariant &value, int role ) override
76 {
77 if ( role == Qt::CheckStateRole )
78 {
79 mEffect->setEnabled( value.toBool() );
80 mWidget->updatePreview();
81 }
82 else
83 {
84 QStandardItem::setData( value, role );
85 }
86 }
87
88 protected:
89 QgsPaintEffect *mEffect = nullptr;
90 QgsEffectStackPropertiesWidget *mWidget = nullptr;
91};
93
94//
95// QgsEffectStackPropertiesWidget
96//
97
99 : QgsPanelWidget( parent )
100 , mStack( stack )
101
102{
103// TODO
104#ifdef Q_OS_MAC
105 //setWindowModality( Qt::WindowModal );
106#endif
107
108 mPresentWidget = nullptr;
109
110 setupUi( this );
111 this->layout()->setContentsMargins( 0, 0, 0, 0 );
112
113 mEffectsList->setMaximumHeight( static_cast<int>( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 7 ) );
114 mEffectsList->setMinimumHeight( mEffectsList->maximumHeight() );
115 lblPreview->setMaximumWidth( mEffectsList->maximumHeight() );
116
117 mAddButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
118 mRemoveButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
119 mUpButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
120 mDownButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
121
122 mModel = new QStandardItemModel();
123 // Set the effect
124 mEffectsList->setModel( mModel );
125
126 QItemSelectionModel *selModel = mEffectsList->selectionModel();
127 connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsEffectStackPropertiesWidget::effectChanged );
128
129 loadStack( stack );
131
132 connect( mUpButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::moveEffectUp );
133 connect( mDownButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::moveEffectDown );
134 connect( mAddButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::addEffect );
135 connect( mRemoveButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::removeEffect );
136
137 updateUi();
138
139 // set first selected effect as active item in the tree
140 int initialRow = 0;
141 for ( int i = 0; i < stack->count(); ++i )
142 {
143 // list shows effects in opposite order to stack
144 if ( stack->effect( stack->count() - i - 1 )->enabled() )
145 {
146 initialRow = i;
147 break;
148 }
149 }
150 const QModelIndex newIndex = mEffectsList->model()->index( initialRow, 0 );
151 mEffectsList->setCurrentIndex( newIndex );
152
153 setPanelTitle( tr( "Effects Properties" ) );
154}
155
157
159{
160 mPreviewPicture = picture;
162}
163
165{
166 if ( !stack )
167 {
168 return;
169 }
170
171 EffectItem *parent = static_cast<EffectItem *>( mModel->invisibleRootItem() );
172
173 const int count = stack->count();
174 for ( int i = count - 1; i >= 0; i-- )
175 {
176 EffectItem *effectItem = new EffectItem( stack->effect( i ), this );
177 effectItem->setEditable( false );
178 parent->appendRow( effectItem );
179 }
180}
181
182
188
190{
191 const QModelIndex currentIdx = mEffectsList->currentIndex();
192 if ( !currentIdx.isValid() )
193 return;
194
195 EffectItem *item = static_cast<EffectItem *>( mModel->itemFromIndex( currentIdx ) );
196
197 QStandardItem *root = mModel->invisibleRootItem();
198 const int rowCount = root->rowCount();
199 const int currentRow = item ? item->row() : 0;
200
201 mUpButton->setEnabled( currentRow > 0 );
202 mDownButton->setEnabled( currentRow < rowCount - 1 );
203 mRemoveButton->setEnabled( rowCount > 1 );
204}
205
207{
208 QPainter painter;
209 QImage previewImage( 100, 100, QImage::Format_ARGB32 );
210 previewImage.fill( Qt::transparent );
211 painter.begin( &previewImage );
212 painter.setRenderHint( QPainter::Antialiasing );
215 if ( mPreviewPicture.isNull() )
216 {
217 QPicture previewPic;
218 QPainter previewPicPainter;
219 previewPicPainter.begin( &previewPic );
220 previewPicPainter.setPen( Qt::red );
221 previewPicPainter.setBrush( QColor( 255, 100, 100, 255 ) );
222 previewPicPainter.drawEllipse( QPoint( 50, 50 ), 20, 20 );
223 previewPicPainter.end();
224 mStack->render( previewPic, context );
225 }
226 else
227 {
228 context.painter()->translate( 20, 20 );
229 mStack->render( mPreviewPicture, context );
230 }
231 painter.end();
232
233 lblPreview->setPixmap( QPixmap::fromImage( previewImage ) );
234 emit widgetChanged();
235}
236
238{
239 const QModelIndex idx = mEffectsList->currentIndex();
240 if ( !idx.isValid() )
241 return nullptr;
242
243 EffectItem *item = static_cast<EffectItem *>( mModel->itemFromIndex( idx ) );
244 return item;
245}
246
248{
249 updateUi();
250
251 EffectItem *currentItem = currentEffectItem();
252 if ( !currentItem )
253 return;
254
255 QgsPaintEffectPropertiesWidget *effectPropertiesWidget = new QgsPaintEffectPropertiesWidget( currentItem->effect() );
256 setWidget( effectPropertiesWidget );
257
260}
261
263{
264 const int index = stackedWidget->addWidget( widget );
265 stackedWidget->setCurrentIndex( index );
266 if ( mPresentWidget )
267 {
268 stackedWidget->removeWidget( mPresentWidget );
269 QWidget *dummy = mPresentWidget;
270 mPresentWidget = widget;
271 delete dummy; // auto disconnects all signals
272 }
273}
274
276{
277 QgsPaintEffect *newEffect = new QgsDrawSourceEffect();
278 mStack->insertEffect( 0, newEffect );
279
280 EffectItem *newEffectItem = new EffectItem( newEffect, this );
281 mModel->invisibleRootItem()->insertRow( mStack->count() - 1, newEffectItem );
282
283 mEffectsList->setCurrentIndex( mModel->indexFromItem( newEffectItem ) );
284 updateUi();
286}
287
289{
290 EffectItem *item = currentEffectItem();
291 const int row = item->row();
292 QStandardItem *root = mModel->invisibleRootItem();
293
294 const int layerIdx = root->rowCount() - row - 1;
295 QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
296
297 mModel->invisibleRootItem()->removeRow( row );
298
299 const int newSelection = std::min( row, root->rowCount() - 1 );
300 const QModelIndex newIdx = root->child( newSelection )->index();
301 mEffectsList->setCurrentIndex( newIdx );
302
303 updateUi();
305
306 delete tmpEffect;
307}
308
313
318
320{
321 EffectItem *item = currentEffectItem();
322 if ( !item )
323 return;
324
325 const int row = item->row();
326
327 QStandardItem *root = mModel->invisibleRootItem();
328
329 const int layerIdx = root->rowCount() - row - 1;
330 // switch effects
331 QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
332 mStack->insertEffect( layerIdx - offset, tmpEffect );
333
334 QList<QStandardItem *> toMove = root->takeRow( row );
335 root->insertRows( row + offset, toMove );
336
337 const QModelIndex newIdx = toMove[0]->index();
338 mEffectsList->setCurrentIndex( newIdx );
339
341 updateUi();
342}
343
345{
346 EffectItem *item = currentEffectItem();
347 item->setEffect( newEffect );
348
349 QStandardItem *root = mModel->invisibleRootItem();
350 const int effectIdx = root->rowCount() - item->row() - 1;
351 mStack->changeEffect( effectIdx, newEffect );
352
354 // Important: This lets the effect to have its own effect properties widget
356}
357
358
359//
360// QgsEffectStackPropertiesDialog
361//
362
364 : QgsDialog( parent, f, QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok )
365
366{
367 setWindowTitle( tr( "Effect Properties" ) );
369
370 QDialogButtonBox *buttonBox = this->findChild<QDialogButtonBox *>( QString(), Qt::FindDirectChildrenOnly );
371 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsEffectStackPropertiesDialog::showHelp );
372
373 layout()->addWidget( mPropertiesWidget );
374}
375
380
382{
384}
385
386void QgsEffectStackPropertiesDialog::showHelp()
387{
388 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#draw-effects" ) );
389}
390
391
392//
393// QgsEffectStackCompactWidget
394//
395
397 : QgsPanelWidget( parent )
398
399{
400 QHBoxLayout *layout = new QHBoxLayout();
401 layout->setContentsMargins( 0, 0, 0, 0 );
402 layout->setSpacing( 6 );
403 setLayout( layout );
404
405 mEnabledCheckBox = new QCheckBox( this );
406 mEnabledCheckBox->setText( tr( "Draw effects" ) );
407 layout->addWidget( mEnabledCheckBox );
408
409 mButton = new QToolButton( this );
410 mButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconPaintEffects.svg" ) ) );
411 mButton->setToolTip( tr( "Customize effects" ) );
412 layout->addWidget( mButton );
413
414 setFocusPolicy( Qt::StrongFocus );
415 setFocusProxy( mEnabledCheckBox );
416
417 connect( mButton, &QAbstractButton::clicked, this, &QgsEffectStackCompactWidget::showDialog );
418 connect( mEnabledCheckBox, &QAbstractButton::toggled, this, &QgsEffectStackCompactWidget::enableToggled );
419
420 setPaintEffect( effect );
421}
422
424
426{
427 if ( !effect )
428 {
429 mEnabledCheckBox->setChecked( false );
430 mEnabledCheckBox->setEnabled( false );
431 mButton->setEnabled( false );
432 mStack = nullptr;
433 return;
434 }
435
436 //is effect a stack?
437 QgsEffectStack *stack = dynamic_cast<QgsEffectStack *>( effect );
438 if ( !stack )
439 {
440 //not already a stack, so promote to stack
441 stack = new QgsEffectStack( *effect );
442 }
443
444 mStack = stack;
445 mEnabledCheckBox->setChecked( mStack->enabled() );
446 mEnabledCheckBox->setEnabled( true );
447 mButton->setEnabled( mStack->enabled() );
448}
449
451{
452 return mStack;
453}
454
456{
457 mPreviewPicture = picture;
458}
459
460void QgsEffectStackCompactWidget::showDialog()
461{
462 if ( !mStack )
463 return;
464
465 QgsEffectStack *clone = mStack->clone();
466 QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( qobject_cast<QWidget *>( parent() ) );
467 if ( panel && panel->dockMode() )
468 {
470 widget->setPreviewPicture( mPreviewPicture );
471
472 connect( widget, &QgsPanelWidget::widgetChanged, this, &QgsEffectStackCompactWidget::updateEffectLive );
473 connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsEffectStackCompactWidget::updateAcceptWidget );
474 panel->openPanel( widget );
475 }
476 else
477 {
478 QgsEffectStackPropertiesDialog dlg( clone, this );
479 dlg.setPreviewPicture( mPreviewPicture );
480
481 if ( dlg.exec() == QDialog::Accepted )
482 {
483 *mStack = *clone;
484 emit changed();
485 }
486 }
487}
488
489void QgsEffectStackCompactWidget::enableToggled( bool checked )
490{
491 if ( !mStack )
492 {
493 return;
494 }
495
496 mStack->setEnabled( checked );
497 mButton->setEnabled( checked );
498 emit changed();
499}
500
501void QgsEffectStackCompactWidget::updateAcceptWidget( QgsPanelWidget *panel )
502{
503 QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( panel );
504 *mStack = *widget->stack();
505 emit changed();
506 // delete widget->stack();
507}
508
509void QgsEffectStackCompactWidget::updateEffectLive()
510{
511 QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( sender() );
512 *mStack = *widget->stack();
513 emit changed();
514}
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition qgis.h:5833
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.
A generic dialog with layout and button box.
Definition qgsdialog.h:34
QDialogButtonBox * buttonBox()
Returns the button box.
Definition qgsdialog.h:45
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.
A dialog for modifying the properties of a QgsEffectStack, including adding and reordering effects wi...
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.
int count() const
Returns count of effects contained by the stack.
QgsEffectStack * clone() const override
Duplicates an effect by creating a deep copy of the effect.
bool insertEffect(int index, QgsPaintEffect *effect)
Inserts an effect at a specified index within the stack.
QgsPaintEffect * takeEffect(int index)
Removes an effect from the stack and returns a pointer to it.
bool changeEffect(int index, QgsPaintEffect *effect)
Replaces the effect at a specified position within the stack.
QgsPaintEffect * effect(int index) const
Returns a pointer to the effect at a specified index within the stack.
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:39
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.
bool enabled() const
Returns whether the effect is enabled.
virtual void render(const QPicture &picture, QgsRenderContext &context)
Renders a picture using the effect.
Base class for any widget that can be shown as a 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 ...
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
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.
bool dockMode()
Returns the dock mode state.
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.