QGIS API Documentation 3.41.0-Master (3440c17df1d)
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
104// TODO
105#ifdef Q_OS_MAC
106 //setWindowModality( Qt::WindowModal );
107#endif
108
109 mPresentWidget = nullptr;
110
111 setupUi( this );
112 this->layout()->setContentsMargins( 0, 0, 0, 0 );
113
114 mEffectsList->setMaximumHeight( static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().height() * 7 ) );
115 mEffectsList->setMinimumHeight( mEffectsList->maximumHeight() );
116 lblPreview->setMaximumWidth( mEffectsList->maximumHeight() );
117
118 mAddButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
119 mRemoveButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
120 mUpButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
121 mDownButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
122
123 mModel = new QStandardItemModel();
124 // Set the effect
125 mEffectsList->setModel( mModel );
126
127 QItemSelectionModel *selModel = mEffectsList->selectionModel();
128 connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsEffectStackPropertiesWidget::effectChanged );
129
130 loadStack( stack );
132
133 connect( mUpButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::moveEffectUp );
134 connect( mDownButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::moveEffectDown );
135 connect( mAddButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::addEffect );
136 connect( mRemoveButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::removeEffect );
137
138 updateUi();
139
140 // set first selected effect as active item in the tree
141 int initialRow = 0;
142 for ( int i = 0; i < stack->count(); ++i )
143 {
144 // list shows effects in opposite order to stack
145 if ( stack->effect( stack->count() - i - 1 )->enabled() )
146 {
147 initialRow = i;
148 break;
149 }
150 }
151 const QModelIndex newIndex = mEffectsList->model()->index( initialRow, 0 );
152 mEffectsList->setCurrentIndex( newIndex );
153
154 setPanelTitle( tr( "Effects Properties" ) );
155}
156
158
160{
161 mPreviewPicture = picture;
163}
164
166{
167 if ( !stack )
168 {
169 return;
170 }
171
172 EffectItem *parent = static_cast<EffectItem *>( mModel->invisibleRootItem() );
173
174 const int count = stack->count();
175 for ( int i = count - 1; i >= 0; i-- )
176 {
177 EffectItem *effectItem = new EffectItem( stack->effect( i ), this );
178 effectItem->setEditable( false );
179 parent->appendRow( effectItem );
180 }
181}
182
183
189
191{
192 const QModelIndex currentIdx = mEffectsList->currentIndex();
193 if ( !currentIdx.isValid() )
194 return;
195
196 EffectItem *item = static_cast<EffectItem *>( mModel->itemFromIndex( currentIdx ) );
197
198 QStandardItem *root = mModel->invisibleRootItem();
199 const int rowCount = root->rowCount();
200 const int currentRow = item ? item->row() : 0;
201
202 mUpButton->setEnabled( currentRow > 0 );
203 mDownButton->setEnabled( currentRow < rowCount - 1 );
204 mRemoveButton->setEnabled( rowCount > 1 );
205}
206
208{
209 QPainter painter;
210 QImage previewImage( 100, 100, QImage::Format_ARGB32 );
211 previewImage.fill( Qt::transparent );
212 painter.begin( &previewImage );
213 painter.setRenderHint( QPainter::Antialiasing );
216 if ( mPreviewPicture.isNull() )
217 {
218 QPicture previewPic;
219 QPainter previewPicPainter;
220 previewPicPainter.begin( &previewPic );
221 previewPicPainter.setPen( Qt::red );
222 previewPicPainter.setBrush( QColor( 255, 100, 100, 255 ) );
223 previewPicPainter.drawEllipse( QPoint( 50, 50 ), 20, 20 );
224 previewPicPainter.end();
225 mStack->render( previewPic, context );
226 }
227 else
228 {
229 context.painter()->translate( 20, 20 );
230 mStack->render( mPreviewPicture, context );
231 }
232 painter.end();
233
234 lblPreview->setPixmap( QPixmap::fromImage( previewImage ) );
235 emit widgetChanged();
236}
237
239{
240 const QModelIndex idx = mEffectsList->currentIndex();
241 if ( !idx.isValid() )
242 return nullptr;
243
244 EffectItem *item = static_cast<EffectItem *>( mModel->itemFromIndex( idx ) );
245 return item;
246}
247
249{
250 updateUi();
251
252 EffectItem *currentItem = currentEffectItem();
253 if ( !currentItem )
254 return;
255
256 QgsPaintEffectPropertiesWidget *effectPropertiesWidget = new QgsPaintEffectPropertiesWidget( currentItem->effect() );
257 setWidget( effectPropertiesWidget );
258
261}
262
264{
265 const int index = stackedWidget->addWidget( widget );
266 stackedWidget->setCurrentIndex( index );
267 if ( mPresentWidget )
268 {
269 stackedWidget->removeWidget( mPresentWidget );
270 QWidget *dummy = mPresentWidget;
271 mPresentWidget = widget;
272 delete dummy; // auto disconnects all signals
273 }
274}
275
277{
278 QgsPaintEffect *newEffect = new QgsDrawSourceEffect();
279 mStack->insertEffect( 0, newEffect );
280
281 EffectItem *newEffectItem = new EffectItem( newEffect, this );
282 mModel->invisibleRootItem()->insertRow( mStack->count() - 1, newEffectItem );
283
284 mEffectsList->setCurrentIndex( mModel->indexFromItem( newEffectItem ) );
285 updateUi();
287}
288
290{
291 EffectItem *item = currentEffectItem();
292 const int row = item->row();
293 QStandardItem *root = mModel->invisibleRootItem();
294
295 const int layerIdx = root->rowCount() - row - 1;
296 QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
297
298 mModel->invisibleRootItem()->removeRow( row );
299
300 const int newSelection = std::min( row, root->rowCount() - 1 );
301 const QModelIndex newIdx = root->child( newSelection )->index();
302 mEffectsList->setCurrentIndex( newIdx );
303
304 updateUi();
306
307 delete tmpEffect;
308}
309
314
319
321{
322 EffectItem *item = currentEffectItem();
323 if ( !item )
324 return;
325
326 const int row = item->row();
327
328 QStandardItem *root = mModel->invisibleRootItem();
329
330 const int layerIdx = root->rowCount() - row - 1;
331 // switch effects
332 QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
333 mStack->insertEffect( layerIdx - offset, tmpEffect );
334
335 QList<QStandardItem *> toMove = root->takeRow( row );
336 root->insertRows( row + offset, toMove );
337
338 const QModelIndex newIdx = toMove[ 0 ]->index();
339 mEffectsList->setCurrentIndex( newIdx );
340
342 updateUi();
343}
344
346{
347 EffectItem *item = currentEffectItem();
348 item->setEffect( newEffect );
349
350 QStandardItem *root = mModel->invisibleRootItem();
351 const int effectIdx = root->rowCount() - item->row() - 1;
352 mStack->changeEffect( effectIdx, newEffect );
353
355 // Important: This lets the effect to have its own effect properties widget
357}
358
359
360//
361// QgsEffectStackPropertiesDialog
362//
363
365 : QgsDialog( parent, f, QDialogButtonBox::Cancel | QDialogButtonBox::Help | QDialogButtonBox::Ok )
366
367{
368 setWindowTitle( tr( "Effect Properties" ) );
370
371 QDialogButtonBox *buttonBox = this->findChild<QDialogButtonBox *>( QString(), Qt::FindDirectChildrenOnly );
372 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsEffectStackPropertiesDialog::showHelp );
373
374 layout()->addWidget( mPropertiesWidget );
375}
376
381
383{
385}
386
387void QgsEffectStackPropertiesDialog::showHelp()
388{
389 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#draw-effects" ) );
390}
391
392
393//
394// QgsEffectStackCompactWidget
395//
396
398 : QgsPanelWidget( parent )
399
400{
401 QHBoxLayout *layout = new QHBoxLayout();
402 layout->setContentsMargins( 0, 0, 0, 0 );
403 layout->setSpacing( 6 );
404 setLayout( layout );
405
406 mEnabledCheckBox = new QCheckBox( this );
407 mEnabledCheckBox->setText( tr( "Draw effects" ) );
408 layout->addWidget( mEnabledCheckBox );
409
410 mButton = new QToolButton( this );
411 mButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconPaintEffects.svg" ) ) );
412 mButton->setToolTip( tr( "Customize effects" ) );
413 layout->addWidget( mButton );
414
415 setFocusPolicy( Qt::StrongFocus );
416 setFocusProxy( mEnabledCheckBox );
417
418 connect( mButton, &QAbstractButton::clicked, this, &QgsEffectStackCompactWidget::showDialog );
419 connect( mEnabledCheckBox, &QAbstractButton::toggled, this, &QgsEffectStackCompactWidget::enableToggled );
420
421 setPaintEffect( effect );
422}
423
425
427{
428 if ( !effect )
429 {
430 mEnabledCheckBox->setChecked( false );
431 mEnabledCheckBox->setEnabled( false );
432 mButton->setEnabled( false );
433 mStack = nullptr;
434 return;
435 }
436
437 //is effect a stack?
438 QgsEffectStack *stack = dynamic_cast<QgsEffectStack *>( effect );
439 if ( !stack )
440 {
441 //not already a stack, so promote to stack
442 stack = new QgsEffectStack( *effect );
443 }
444
445 mStack = stack;
446 mEnabledCheckBox->setChecked( mStack->enabled() );
447 mEnabledCheckBox->setEnabled( true );
448 mButton->setEnabled( mStack->enabled() );
449}
450
452{
453 return mStack;
454}
455
457{
458 mPreviewPicture = picture;
459}
460
461void QgsEffectStackCompactWidget::showDialog()
462{
463 if ( !mStack )
464 return;
465
466 QgsEffectStack *clone = mStack->clone();
467 QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( qobject_cast< QWidget * >( parent() ) );
468 if ( panel && panel->dockMode() )
469 {
471 widget->setPreviewPicture( mPreviewPicture );
472
473 connect( widget, &QgsPanelWidget::widgetChanged, this, &QgsEffectStackCompactWidget::updateEffectLive );
474 connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsEffectStackCompactWidget::updateAcceptWidget );
475 panel->openPanel( widget );
476 }
477 else
478 {
479 QgsEffectStackPropertiesDialog dlg( clone, this );
480 dlg.setPreviewPicture( mPreviewPicture );
481
482 if ( dlg.exec() == QDialog::Accepted )
483 {
484 *mStack = *clone;
485 emit changed();
486 }
487 }
488}
489
490void QgsEffectStackCompactWidget::enableToggled( bool checked )
491{
492 if ( !mStack )
493 {
494 return;
495 }
496
497 mStack->setEnabled( checked );
498 mButton->setEnabled( checked );
499 emit changed();
500}
501
502void QgsEffectStackCompactWidget::updateAcceptWidget( QgsPanelWidget *panel )
503{
504 QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( panel );
505 *mStack = *widget->stack();
506 emit changed();
507// delete widget->stack();
508}
509
510void QgsEffectStackCompactWidget::updateEffectLive()
511{
512 QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( sender() );
513 *mStack = *widget->stack();
514 emit changed();
515}
@ 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:5667
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:48
QVBoxLayout * layout()
Returns the central layout. Widgets added to it must have this dialog as parent.
Definition qgsdialog.h:46
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.
virtual void render(QPicture &picture, QgsRenderContext &context)
Renders a picture using the effect.
bool enabled() const
Returns whether the effect is enabled.
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.