QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 "qgspainteffectregistry.h"
18 #include "qgspainteffect.h"
19 #include "qgseffectstack.h"
21 #include "qgspainteffectwidget.h"
22 #include "qgsapplication.h"
23 #include "qgssymbollayerutils.h"
24 #include "qgspanelwidget.h"
25 
26 #include <QPicture>
27 #include <QPainter>
28 #include <QStandardItemModel>
29 #include <QStandardItem>
30 #include <QCheckBox>
31 #include <QToolButton>
32 
34 
35 static const int EFFECT_ITEM_TYPE = QStandardItem::UserType + 1;
36 
37 class EffectItem : public QStandardItem
38 {
39  public:
40  EffectItem( QgsPaintEffect *effect, QgsEffectStackPropertiesWidget *propertiesWidget )
41  {
42  setEffect( effect );
43  setCheckable( true );
44  mWidget = propertiesWidget;
45  }
46 
47  void setEffect( QgsPaintEffect *effect )
48  {
49  mEffect = effect;
50  emitDataChanged();
51  }
52 
53  int type() const override { return EFFECT_ITEM_TYPE; }
54 
55  QgsPaintEffect *effect()
56  {
57  return mEffect;
58  }
59 
60  QVariant data( int role ) const override
61  {
62  if ( role == Qt::DisplayRole || role == Qt::EditRole )
63  {
64  return QgsApplication::paintEffectRegistry()->effectMetadata( mEffect->type() )->visibleName();
65  }
66  if ( role == Qt::CheckStateRole )
67  {
68  return mEffect->enabled() ? Qt::Checked : Qt::Unchecked;
69  }
70  return QStandardItem::data( role );
71  }
72 
73  void setData( const QVariant &value, int role ) override
74  {
75  if ( role == Qt::CheckStateRole )
76  {
77  mEffect->setEnabled( value.toBool() );
78  mWidget->updatePreview();
79  }
80  else
81  {
82  QStandardItem::setData( value, role );
83  }
84  }
85 
86  protected:
87  QgsPaintEffect *mEffect = nullptr;
88  QgsEffectStackPropertiesWidget *mWidget = nullptr;
89 };
91 
92 //
93 // QgsEffectStackPropertiesWidget
94 //
95 
97  : QgsPanelWidget( parent )
98  , mStack( stack )
99 
100 {
101 
102 // TODO
103 #ifdef Q_OS_MAC
104  //setWindowModality( Qt::WindowModal );
105 #endif
106 
107  mPresentWidget = nullptr;
108 
109  setupUi( this );
110 
111  mAddButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
112  mRemoveButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
113  mUpButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
114  mDownButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
115 
116  mModel = new QStandardItemModel();
117  // Set the effect
118  mEffectsList->setModel( mModel );
119 
120  QItemSelectionModel *selModel = mEffectsList->selectionModel();
121  connect( selModel, &QItemSelectionModel::currentChanged, this, &QgsEffectStackPropertiesWidget::effectChanged );
122 
123  loadStack( stack );
124  updatePreview();
125 
126  connect( mUpButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::moveEffectUp );
127  connect( mDownButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::moveEffectDown );
128  connect( mAddButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::addEffect );
129  connect( mRemoveButton, &QAbstractButton::clicked, this, &QgsEffectStackPropertiesWidget::removeEffect );
130 
131  updateUi();
132 
133  // set first selected effect as active item in the tree
134  int initialRow = 0;
135  for ( int i = 0; i < stack->count(); ++i )
136  {
137  // list shows effects in opposite order to stack
138  if ( stack->effect( stack->count() - i - 1 )->enabled() )
139  {
140  initialRow = i;
141  break;
142  }
143  }
144  QModelIndex newIndex = mEffectsList->model()->index( initialRow, 0 );
145  mEffectsList->setCurrentIndex( newIndex );
146 
147  setPanelTitle( tr( "Effects Properties" ) );
148 }
149 
151 {
152  delete mPreviewPicture;
153 }
154 
156 {
157  delete mPreviewPicture;
158  mPreviewPicture = new QPicture( picture );
159  updatePreview();
160 }
161 
163 {
164  if ( !stack )
165  {
166  return;
167  }
168 
169  EffectItem *parent = static_cast<EffectItem *>( mModel->invisibleRootItem() );
170 
171  int count = stack->count();
172  for ( int i = count - 1; i >= 0; i-- )
173  {
174  EffectItem *effectItem = new EffectItem( stack->effect( i ), this );
175  effectItem->setEditable( false );
176  parent->appendRow( effectItem );
177  }
178 }
179 
180 
182 {
183  mModel->clear();
184  loadStack( mStack );
185 }
186 
188 {
189  QModelIndex currentIdx = mEffectsList->currentIndex();
190  if ( !currentIdx.isValid() )
191  return;
192 
193  EffectItem *item = static_cast<EffectItem *>( mModel->itemFromIndex( currentIdx ) );
194 
195  QStandardItem *root = mModel->invisibleRootItem();
196  int rowCount = root->rowCount();
197  int currentRow = item ? item->row() : 0;
198 
199  mUpButton->setEnabled( currentRow > 0 );
200  mDownButton->setEnabled( currentRow < rowCount - 1 );
201  mRemoveButton->setEnabled( rowCount > 1 );
202 }
203 
205 {
206  QPainter painter;
207  QImage previewImage( 150, 150, QImage::Format_ARGB32 );
208  previewImage.fill( Qt::transparent );
209  painter.begin( &previewImage );
210  painter.setRenderHint( QPainter::Antialiasing );
211  QgsRenderContext context = QgsRenderContext::fromQPainter( &painter );
212  if ( !mPreviewPicture )
213  {
214  QPicture previewPic;
215  QPainter previewPicPainter;
216  previewPicPainter.begin( &previewPic );
217  previewPicPainter.setPen( Qt::red );
218  previewPicPainter.setBrush( QColor( 255, 100, 100, 255 ) );
219  previewPicPainter.drawEllipse( QPoint( 75, 75 ), 30, 30 );
220  previewPicPainter.end();
221  mStack->render( previewPic, context );
222  }
223  else
224  {
225  context.painter()->translate( 35, 35 );
226  mStack->render( *mPreviewPicture, context );
227  }
228  painter.end();
229 
230  lblPreview->setPixmap( QPixmap::fromImage( previewImage ) );
231  emit widgetChanged();
232 }
233 
235 {
236  QModelIndex idx = mEffectsList->currentIndex();
237  if ( !idx.isValid() )
238  return nullptr;
239 
240  EffectItem *item = static_cast<EffectItem *>( mModel->itemFromIndex( idx ) );
241  return item;
242 }
243 
245 {
246  updateUi();
247 
248  EffectItem *currentItem = currentEffectItem();
249  if ( !currentItem )
250  return;
251 
252  QgsPaintEffectPropertiesWidget *effectPropertiesWidget = new QgsPaintEffectPropertiesWidget( currentItem->effect() );
253  setWidget( effectPropertiesWidget );
254 
257 }
258 
260 {
261  int index = stackedWidget->addWidget( widget );
262  stackedWidget->setCurrentIndex( index );
263  if ( mPresentWidget )
264  {
265  stackedWidget->removeWidget( mPresentWidget );
266  QWidget *dummy = mPresentWidget;
267  mPresentWidget = widget;
268  delete dummy; // auto disconnects all signals
269  }
270 }
271 
273 {
274  QgsPaintEffect *newEffect = new QgsDrawSourceEffect();
275  mStack->insertEffect( 0, newEffect );
276 
277  EffectItem *newEffectItem = new EffectItem( newEffect, this );
278  mModel->invisibleRootItem()->insertRow( mStack->count() - 1, newEffectItem );
279 
280  mEffectsList->setCurrentIndex( mModel->indexFromItem( newEffectItem ) );
281  updateUi();
282  updatePreview();
283 }
284 
286 {
287  EffectItem *item = currentEffectItem();
288  int row = item->row();
289  QStandardItem *root = mModel->invisibleRootItem();
290 
291  int layerIdx = root->rowCount() - row - 1;
292  QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
293 
294  mModel->invisibleRootItem()->removeRow( row );
295 
296  int newSelection = std::min( row, root->rowCount() - 1 );
297  QModelIndex newIdx = root->child( newSelection )->index();
298  mEffectsList->setCurrentIndex( newIdx );
299 
300  updateUi();
301  updatePreview();
302 
303  delete tmpEffect;
304 }
305 
307 {
308  moveEffectByOffset( + 1 );
309 }
310 
312 {
313  moveEffectByOffset( -1 );
314 }
315 
317 {
318  EffectItem *item = currentEffectItem();
319  if ( !item )
320  return;
321 
322  int row = item->row();
323 
324  QStandardItem *root = mModel->invisibleRootItem();
325 
326  int layerIdx = root->rowCount() - row - 1;
327  // switch effects
328  QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
329  mStack->insertEffect( layerIdx - offset, tmpEffect );
330 
331  QList<QStandardItem *> toMove = root->takeRow( row );
332  root->insertRows( row + offset, toMove );
333 
334  QModelIndex newIdx = toMove[ 0 ]->index();
335  mEffectsList->setCurrentIndex( newIdx );
336 
337  updatePreview();
338  updateUi();
339 }
340 
342 {
343  EffectItem *item = currentEffectItem();
344  item->setEffect( newEffect );
345 
346  QStandardItem *root = mModel->invisibleRootItem();
347  int effectIdx = root->rowCount() - item->row() - 1;
348  mStack->changeEffect( effectIdx, newEffect );
349 
350  updatePreview();
351  // Important: This lets the effect to have its own effect properties widget
352  effectChanged();
353 }
354 
355 
356 //
357 // QgsEffectStackPropertiesDialog
358 //
359 
361  : QgsDialog( parent, f, QDialogButtonBox::Ok | QDialogButtonBox::Cancel )
362 
363 {
364  setWindowTitle( tr( "Effect Properties" ) );
366  layout()->addWidget( mPropertiesWidget );
367 }
368 
370 {
371  return mPropertiesWidget->stack();
372 }
373 
375 {
377 }
378 
379 //
380 // QgsEffectStackCompactWidget
381 //
382 
384  : QgsPanelWidget( parent )
385 
386 {
387  QHBoxLayout *layout = new QHBoxLayout();
388  layout->setContentsMargins( 0, 0, 0, 0 );
389  layout->setSpacing( 6 );
390  setLayout( layout );
391 
392  mEnabledCheckBox = new QCheckBox( this );
393  mEnabledCheckBox->setText( tr( "Draw effects" ) );
394  layout->addWidget( mEnabledCheckBox );
395 
396  mButton = new QToolButton( this );
397  mButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconPaintEffects.svg" ) ) );
398  mButton->setToolTip( tr( "Customize effects" ) );
399  layout->addWidget( mButton );
400 
401  setFocusPolicy( Qt::StrongFocus );
402  setFocusProxy( mEnabledCheckBox );
403 
404  connect( mButton, &QAbstractButton::clicked, this, &QgsEffectStackCompactWidget::showDialog );
405  connect( mEnabledCheckBox, &QAbstractButton::toggled, this, &QgsEffectStackCompactWidget::enableToggled );
406 
407  setPaintEffect( effect );
408 }
409 
411 {
412  delete mPreviewPicture;
413 }
414 
416 {
417  if ( !effect )
418  {
419  mEnabledCheckBox->setChecked( false );
420  mEnabledCheckBox->setEnabled( false );
421  mButton->setEnabled( false );
422  mStack = nullptr;
423  return;
424  }
425 
426  //is effect a stack?
427  QgsEffectStack *stack = dynamic_cast<QgsEffectStack *>( effect );
428  if ( !stack )
429  {
430  //not already a stack, so promote to stack
431  stack = new QgsEffectStack( *effect );
432  }
433 
434  mStack = stack;
435  mEnabledCheckBox->setChecked( mStack->enabled() );
436  mEnabledCheckBox->setEnabled( true );
437  mButton->setEnabled( mStack->enabled() );
438 }
439 
441 {
442  return mStack;
443 }
444 
445 void QgsEffectStackCompactWidget::setPreviewPicture( const QPicture &picture )
446 {
447  delete mPreviewPicture;
448  mPreviewPicture = new QPicture( picture );
449 }
450 
451 void QgsEffectStackCompactWidget::showDialog()
452 {
453  if ( !mStack )
454  return;
455 
456  QgsEffectStack *clone = static_cast<QgsEffectStack *>( mStack->clone() );
457  QgsEffectStackPropertiesWidget *widget = new QgsEffectStackPropertiesWidget( clone, nullptr );
458  if ( mPreviewPicture )
459  {
460  widget->setPreviewPicture( *mPreviewPicture );
461  }
462  connect( widget, &QgsPanelWidget::widgetChanged, this, &QgsEffectStackCompactWidget::updateEffectLive );
463  connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsEffectStackCompactWidget::updateAcceptWidget );
464 
465  QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( qobject_cast< QWidget * >( parent() ) );
466  if ( panel && panel->dockMode() )
467  {
468  panel->openPanel( widget );
469  }
470  else
471  {
472  openPanel( widget );
473  }
474 }
475 
476 void QgsEffectStackCompactWidget::enableToggled( bool checked )
477 {
478  if ( !mStack )
479  {
480  return;
481  }
482 
483  mStack->setEnabled( checked );
484  mButton->setEnabled( checked );
485  emit changed();
486 }
487 
488 void QgsEffectStackCompactWidget::updateAcceptWidget( QgsPanelWidget *panel )
489 {
490  QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( panel );
491  *mStack = *widget->stack();
492  emit changed();
493 // delete widget->stack();
494 }
495 
496 void QgsEffectStackCompactWidget::updateEffectLive()
497 {
498  QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( sender() );
499  *mStack = *widget->stack();
500  emit changed();
501 }
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 addEffect()
Adds a new effect to the stack.
EffectItem * currentEffectItem()
Returns the currently selected effect within the stack.
bool dockMode()
Returns the dock mode state.
void setPaintEffect(QgsPaintEffect *effect)
Sets paint effect attached to the widget,.
bool insertEffect(int index, QgsPaintEffect *effect)
Inserts an effect at a specified index within the stack.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
void changeEffect(QgsPaintEffect *effect)
Emitted when paint effect type changes.
bool enabled() const
Returns whether the effect is enabled.
Base class for visual effects which can be applied to QPicture drawings.
A generic dialog with layout and button box.
Definition: qgsdialog.h:33
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Base class for any widget that can be shown as a inline panel.
void updatePreview()
Updates the effect preview icon.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
QgsEffectStackCompactWidget(QWidget *parent=nullptr, QgsPaintEffect *effect=nullptr)
QgsEffectStackCompactWidget constructor.
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application&#39;s paint effect registry, used for managing paint effects. ...
A widget for modifying the properties of a QgsEffectStack, including adding and reordering effects wi...
void changeEffect(QgsPaintEffect *newEffect)
Updates the effect stack when the currently selected effect changes properties.
void setEnabled(bool enabled)
Sets whether the effect is enabled.
int count() const
Returns count of effects contained by the stack.
void setPreviewPicture(const QPicture &picture)
Sets the picture to use for effect previews for the dialog.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget...
QgsEffectStack * stack()
Returns effect stack attached to the widget.
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
A widget which modifies the properties of a QgsPaintEffect.
QgsEffectStackPropertiesWidget(QgsEffectStack *stack, QWidget *parent=nullptr)
QgsEffectStackPropertiesWidget constructor.
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.
void setPreviewPicture(const QPicture &picture)
Sets the picture to use for effect previews for the dialog.
void loadStack()
Refreshes the widget to reflect the current state of the stack.
QgsPaintEffectAbstractMetadata * effectMetadata(const QString &name) const
Returns the metadata for a specific effect.
QgsPaintEffect * paintEffect() const
Returns paint effect attached to the widget.
void widgetChanged()
Emitted when the widget state changes.
QgsEffectStackPropertiesDialog(QgsEffectStack *stack, QWidget *parent=nullptr, Qt::WindowFlags f=nullptr)
QgsEffectStackPropertiesDialog constructor.
void setPreviewPicture(const QPicture &picture)
Sets the picture to use for effect previews for the dialog.
QgsEffectStack * stack()
Returns effect stack attached to the dialog.
void updateUi()
Enables or disables widgets depending on the selected effect within the stack.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
void changed()
Emitted when the paint effect properties change.
void changed()
Emitted when paint effect properties changes.
void moveEffectUp()
Moves the currently selected effect up in the stack.
QVBoxLayout * layout()
Returns the central layout. Widgets added to it must have this dialog as parent.
Definition: qgsdialog.h:46
void setWidget(QWidget *widget)
Sets the effect properties widget.
QgsEffectStackPropertiesWidget * mPropertiesWidget
virtual void render(QPicture &picture, QgsRenderContext &context)
Renders a picture using the effect.
QgsPaintEffect * takeEffect(int index)
Removes an effect from the stack and returns a pointer to it.
void effectChanged()
Updates the widget when the selected effect changes type.
void moveEffectDown()
Moves the currently selected effect down in the stack.
A paint effect which draws the source picture with minor or no alterations.
QgsPaintEffect * effect(int index) const
Returns a pointer to the effect at a specified index within the stack.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
bool changeEffect(int index, QgsPaintEffect *effect)
Replaces the effect at a specified position within the stack.
void removeEffect()
Removes the currently selected effect from the stack.
void moveEffectByOffset(int offset)
Moves the currently selected effect within the stack by a specified offset.