QGIS API Documentation  3.2.0-Bonn (bc43194)
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  if ( mPreviewPicture )
158  {
159  delete mPreviewPicture;
160  }
161 
162  mPreviewPicture = new QPicture( picture );
163  updatePreview();
164 }
165 
167 {
168  if ( !stack )
169  {
170  return;
171  }
172 
173  EffectItem *parent = static_cast<EffectItem *>( mModel->invisibleRootItem() );
174 
175  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 
186 {
187  mModel->clear();
188  loadStack( mStack );
189 }
190 
192 {
193  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  int rowCount = root->rowCount();
201  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( 150, 150, QImage::Format_ARGB32 );
212  previewImage.fill( Qt::transparent );
213  painter.begin( &previewImage );
214  painter.setRenderHint( QPainter::Antialiasing );
215  QgsRenderContext context = QgsRenderContext::fromQPainter( &painter );
216  if ( !mPreviewPicture )
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( 75, 75 ), 30, 30 );
224  previewPicPainter.end();
225  mStack->render( previewPic, context );
226  }
227  else
228  {
229  context.painter()->translate( 35, 35 );
230  mStack->render( *mPreviewPicture, context );
231  }
232  painter.end();
233 
234  lblPreview->setPixmap( QPixmap::fromImage( previewImage ) );
235  emit widgetChanged();
236 }
237 
239 {
240  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  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();
286  updatePreview();
287 }
288 
290 {
291  EffectItem *item = currentEffectItem();
292  int row = item->row();
293  QStandardItem *root = mModel->invisibleRootItem();
294 
295  int layerIdx = root->rowCount() - row - 1;
296  QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
297 
298  mModel->invisibleRootItem()->removeRow( row );
299 
300  int newSelection = std::min( row, root->rowCount() - 1 );
301  QModelIndex newIdx = root->child( newSelection )->index();
302  mEffectsList->setCurrentIndex( newIdx );
303 
304  updateUi();
305  updatePreview();
306 
307  delete tmpEffect;
308 }
309 
311 {
312  moveEffectByOffset( + 1 );
313 }
314 
316 {
317  moveEffectByOffset( -1 );
318 }
319 
321 {
322  EffectItem *item = currentEffectItem();
323  if ( !item )
324  return;
325 
326  int row = item->row();
327 
328  QStandardItem *root = mModel->invisibleRootItem();
329 
330  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  QModelIndex newIdx = toMove[ 0 ]->index();
339  mEffectsList->setCurrentIndex( newIdx );
340 
341  updatePreview();
342  updateUi();
343 }
344 
346 {
347  EffectItem *item = currentEffectItem();
348  item->setEffect( newEffect );
349 
350  QStandardItem *root = mModel->invisibleRootItem();
351  int effectIdx = root->rowCount() - item->row() - 1;
352  mStack->changeEffect( effectIdx, newEffect );
353 
354  updatePreview();
355  // Important: This lets the effect to have its own effect properties widget
356  effectChanged();
357 }
358 
359 
360 //
361 // QgsEffectStackPropertiesDialog
362 //
363 
365  : QgsDialog( parent, f, QDialogButtonBox::Ok | QDialogButtonBox::Cancel )
366 
367 {
368  setWindowTitle( tr( "Effect Properties" ) );
370  layout()->addWidget( mPropertiesWidget );
371 }
372 
374 {
375  return mPropertiesWidget->stack();
376 }
377 
379 {
381 }
382 
383 //
384 // QgsEffectStackCompactWidget
385 //
386 
388  : QgsPanelWidget( parent )
389 
390 {
391  QHBoxLayout *layout = new QHBoxLayout();
392  layout->setContentsMargins( 0, 0, 0, 0 );
393  layout->setSpacing( 6 );
394  setLayout( layout );
395 
396  mEnabledCheckBox = new QCheckBox( this );
397  mEnabledCheckBox->setText( tr( "Draw effects" ) );
398  layout->addWidget( mEnabledCheckBox );
399 
400  mButton = new QToolButton( this );
401  mButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mIconPaintEffects.svg" ) ) );
402  mButton->setToolTip( tr( "Customize effects" ) );
403  layout->addWidget( mButton );
404 
405  setFocusPolicy( Qt::StrongFocus );
406  setFocusProxy( mEnabledCheckBox );
407 
408  connect( mButton, &QAbstractButton::clicked, this, &QgsEffectStackCompactWidget::showDialog );
409  connect( mEnabledCheckBox, &QAbstractButton::toggled, this, &QgsEffectStackCompactWidget::enableToggled );
410 
411  setPaintEffect( effect );
412 }
413 
415 {
416  delete mPreviewPicture;
417 }
418 
420 {
421  if ( !effect )
422  {
423  mEnabledCheckBox->setChecked( false );
424  mEnabledCheckBox->setEnabled( false );
425  mButton->setEnabled( false );
426  mStack = nullptr;
427  return;
428  }
429 
430  //is effect a stack?
431  QgsEffectStack *stack = dynamic_cast<QgsEffectStack *>( effect );
432  if ( !stack )
433  {
434  //not already a stack, so promote to stack
435  stack = new QgsEffectStack( *effect );
436  }
437 
438  mStack = stack;
439  mEnabledCheckBox->setChecked( mStack->enabled() );
440  mEnabledCheckBox->setEnabled( true );
441  mButton->setEnabled( mStack->enabled() );
442 }
443 
445 {
446  return mStack;
447 }
448 
449 void QgsEffectStackCompactWidget::setPreviewPicture( const QPicture &picture )
450 {
451  delete mPreviewPicture;
452  mPreviewPicture = new QPicture( picture );
453 }
454 
455 void QgsEffectStackCompactWidget::showDialog()
456 {
457  if ( !mStack )
458  return;
459 
460  QgsEffectStack *clone = static_cast<QgsEffectStack *>( mStack->clone() );
461  QgsEffectStackPropertiesWidget *widget = new QgsEffectStackPropertiesWidget( clone, nullptr );
462  if ( mPreviewPicture )
463  {
464  widget->setPreviewPicture( *mPreviewPicture );
465  }
466  connect( widget, &QgsPanelWidget::widgetChanged, this, &QgsEffectStackCompactWidget::updateEffectLive );
467  connect( widget, &QgsPanelWidget::panelAccepted, this, &QgsEffectStackCompactWidget::updateAcceptWidget );
468 
469  QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( qobject_cast< QWidget * >( parent() ) );
470  if ( panel && panel->dockMode() )
471  {
472  panel->openPanel( widget );
473  }
474  else
475  {
476  openPanel( widget );
477  }
478 }
479 
480 void QgsEffectStackCompactWidget::enableToggled( bool checked )
481 {
482  if ( !mStack )
483  {
484  return;
485  }
486 
487  mStack->setEnabled( checked );
488  mButton->setEnabled( checked );
489  emit changed();
490 }
491 
492 void QgsEffectStackCompactWidget::updateAcceptWidget( QgsPanelWidget *panel )
493 {
494  QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( panel );
495  *mStack = *widget->stack();
496  emit changed();
497 // delete widget->stack();
498 }
499 
500 void QgsEffectStackCompactWidget::updateEffectLive()
501 {
502  QgsEffectStackPropertiesWidget *widget = qobject_cast<QgsEffectStackPropertiesWidget *>( sender() );
503  *mStack = *widget->stack();
504  emit changed();
505 }
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.
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...
QgsPaintEffect * effect(int index) const
Returns a pointer to the effect at a specified index within the stack.
QgsEffectStack * stack()
Returns effect stack attached to the widget.
QgsPaintEffect * paintEffect() const
Returns paint effect 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.
bool enabled() const
Returns whether the effect is enabled.
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.
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.
QgsPaintEffectAbstractMetadata * effectMetadata(const QString &name) const
Returns the metadata for a specific effect.
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.
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.