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