QGIS API Documentation  2.14.11-Essen
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 "qgssymbollayerv2utils.h"
24 
25 #include <QPicture>
26 #include <QPainter>
27 #include <QStandardItemModel>
28 #include <QStandardItem>
29 #include <QCheckBox>
30 #include <QToolButton>
31 
33 
34 static const int EffectItemType = QStandardItem::UserType + 1;
35 
36 class EffectItem : public QStandardItem
37 {
38  public:
39  EffectItem( QgsPaintEffect* effect, QgsEffectStackPropertiesWidget* propertiesWidget )
40  {
41  setEffect( effect );
42  setCheckable( true );
43  mWidget = propertiesWidget;
44  }
45 
46  void setEffect( QgsPaintEffect* effect )
47  {
48  mEffect = effect;
50  }
51 
52  int type() const override { return EffectItemType; }
53 
54  QgsPaintEffect* effect()
55  {
56  return mEffect;
57  }
58 
59  QVariant data( int role ) const override
60  {
61  if ( role == Qt::DisplayRole || role == Qt::EditRole )
62  {
63  return QgsPaintEffectRegistry::instance()->effectMetadata( mEffect->type() )->visibleName();
64  }
65  if ( role == Qt::CheckStateRole )
66  {
67  return mEffect->enabled() ? Qt::Checked : Qt::Unchecked;
68  }
69  return QStandardItem::data( role );
70  }
71 
72  void setData( const QVariant & value, int role ) override
73  {
74  if ( role == Qt::CheckStateRole )
75  {
76  mEffect->setEnabled( value.toBool() );
77  mWidget->updatePreview();
78  }
79  else
80  {
81  QStandardItem::setData( value, role );
82  }
83  }
84 
85  protected:
86  QgsPaintEffect* mEffect;
88 };
90 
91 //
92 // QgsEffectStackPropertiesWidget
93 //
94 
96  : QWidget( parent )
97  , mStack( stack )
98  , mPreviewPicture( nullptr )
99 {
100 
101 // TODO
102 #ifdef Q_OS_MAC
103  //setWindowModality( Qt::WindowModal );
104 #endif
105 
106  mPresentWidget = nullptr;
107 
108  setupUi( this );
109 
110  mAddButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
111  mRemoveButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
112  mUpButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyUp.svg" ) ) );
113  mDownButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyDown.svg" ) ) );
114 
115  mModel = new QStandardItemModel();
116  // Set the effect
117  mEffectsList->setModel( mModel );
118 
119  QItemSelectionModel* selModel = mEffectsList->selectionModel();
120  connect( selModel, SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( effectChanged() ) );
121 
122  loadStack( stack );
123  updatePreview();
124 
125  connect( mUpButton, SIGNAL( clicked() ), this, SLOT( moveEffectUp() ) );
126  connect( mDownButton, SIGNAL( clicked() ), this, SLOT( moveEffectDown() ) );
127  connect( mAddButton, SIGNAL( clicked() ), this, SLOT( addEffect() ) );
128  connect( mRemoveButton, SIGNAL( clicked() ), this, SLOT( removeEffect() ) );
129 
130  updateUi();
131 
132  // set first selected effect as active item in the tree
133  int initialRow = 0;
134  for ( int i = 0; i < stack->count(); ++i )
135  {
136  // list shows effects in opposite order to stack
137  if ( stack->effect( stack->count() - i - 1 )->enabled() )
138  {
139  initialRow = i;
140  break;
141  }
142  }
143  QModelIndex newIndex = mEffectsList->model()->index( initialRow, 0 );
144  mEffectsList->setCurrentIndex( newIndex );
145 }
146 
148 {
149  delete mPreviewPicture;
150 }
151 
153 {
154  if ( mPreviewPicture )
155  {
156  delete mPreviewPicture;
157  }
158 
159  mPreviewPicture = new QPicture( picture );
160  updatePreview();
161 }
162 
164 {
165  if ( !stack )
166  {
167  return;
168  }
169 
170  EffectItem* parent = static_cast<EffectItem*>( mModel->invisibleRootItem() );
171 
172  int count = stack->count();
173  for ( int i = count - 1; i >= 0; i-- )
174  {
175  EffectItem* effectItem = new EffectItem( stack->effect( i ), this );
176  effectItem->setEditable( false );
177  parent->appendRow( effectItem );
178  }
179 }
180 
181 
183 {
184  mModel->clear();
185  loadStack( mStack );
186 }
187 
189 {
190  QModelIndex currentIdx = mEffectsList->currentIndex();
191  if ( !currentIdx.isValid() )
192  return;
193 
194  EffectItem *item = static_cast<EffectItem*>( mModel->itemFromIndex( currentIdx ) );
195 
197  int rowCount = root->rowCount();
198  int currentRow = item ? item->row() : 0;
199 
200  mUpButton->setEnabled( currentRow > 0 );
201  mDownButton->setEnabled( currentRow < rowCount - 1 );
202  mRemoveButton->setEnabled( rowCount > 1 );
203 }
204 
206 {
207  QPainter painter;
208  QImage previewImage( 150, 150, QImage::Format_ARGB32 );
209  previewImage.fill( Qt::transparent );
210  painter.begin( &previewImage );
211  painter.setRenderHint( QPainter::Antialiasing );
213  if ( !mPreviewPicture )
214  {
215  QPicture previewPic;
216  QPainter previewPicPainter;
217  previewPicPainter.begin( &previewPic );
218  previewPicPainter.setPen( Qt::red );
219  previewPicPainter.setBrush( QColor( 255, 100, 100, 255 ) );
220  previewPicPainter.drawEllipse( QPoint( 75, 75 ), 30, 30 );
221  previewPicPainter.end();
222  mStack->render( previewPic, context );
223  }
224  else
225  {
226  context.painter()->translate( 35, 35 );
227  mStack->render( *mPreviewPicture, context );
228  }
229  painter.end();
230 
231  lblPreview->setPixmap( QPixmap::fromImage( previewImage ) );
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  QWidget *effectPropertiesWidget = new QgsPaintEffectPropertiesWidget( currentItem->effect() );
253  setWidget( effectPropertiesWidget );
254 
255  connect( effectPropertiesWidget, SIGNAL( changeEffect( QgsPaintEffect* ) ), this, SLOT( changeEffect( QgsPaintEffect* ) ) );
256  connect( effectPropertiesWidget, SIGNAL( changed() ), this, SLOT( updatePreview() ) );
257 
258 }
259 
261 {
262  int index = stackedWidget->addWidget( widget );
263  stackedWidget->setCurrentIndex( index );
264  if ( mPresentWidget )
265  {
266  stackedWidget->removeWidget( mPresentWidget );
267  QWidget *dummy = mPresentWidget;
268  mPresentWidget = widget;
269  delete dummy; // auto disconnects all signals
270  }
271 }
272 
274 {
275  QgsPaintEffect* newEffect = new QgsDrawSourceEffect();
276  mStack->insertEffect( 0, newEffect );
277 
278  EffectItem *newEffectItem = new EffectItem( newEffect, this );
279  mModel->invisibleRootItem()->insertRow( mStack->count() - 1, newEffectItem );
280 
281  mEffectsList->setCurrentIndex( mModel->indexFromItem( newEffectItem ) );
282  updateUi();
283  updatePreview();
284 }
285 
287 {
288  EffectItem *item = currentEffectItem();
289  int row = item->row();
291 
292  int layerIdx = root->rowCount() - row - 1;
293  QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
294 
296 
297  int newSelection = qMin( row, root->rowCount() - 1 );
298  QModelIndex newIdx = root->child( newSelection )->index();
299  mEffectsList->setCurrentIndex( newIdx );
300 
301  updateUi();
302  updatePreview();
303 
304  delete tmpEffect;
305 }
306 
308 {
309  moveEffectByOffset( + 1 );
310 }
311 
313 {
314  moveEffectByOffset( -1 );
315 }
316 
318 {
319  EffectItem *item = currentEffectItem();
320  if ( !item )
321  return;
322 
323  int row = item->row();
324 
326 
327  int layerIdx = root->rowCount() - row - 1;
328  // switch effects
329  QgsPaintEffect* tmpEffect = mStack->takeEffect( layerIdx );
330  mStack->insertEffect( layerIdx - offset, tmpEffect );
331 
332  QList<QStandardItem *> toMove = root->takeRow( row );
333  root->insertRows( row + offset, toMove );
334 
335  QModelIndex newIdx = toMove[ 0 ]->index();
336  mEffectsList->setCurrentIndex( newIdx );
337 
338  updatePreview();
339  updateUi();
340 }
341 
343 {
344  EffectItem *item = currentEffectItem();
345  item->setEffect( newEffect );
346 
348  int effectIdx = root->rowCount() - item->row() - 1;
349  mStack->changeEffect( effectIdx, newEffect );
350 
351  updatePreview();
352  // Important: This lets the effect to have its own effect properties widget
353  effectChanged();
354 }
355 
356 
357 //
358 // QgsEffectStackPropertiesDialog
359 //
360 
362  : QgsDialog( parent, f, QDialogButtonBox::Ok | QDialogButtonBox::Cancel )
363  , mPropertiesWidget( nullptr )
364 {
365  setWindowTitle( tr( "Effect Properties" ) );
368 }
369 
371 {
372 
373 }
374 
376 {
377  return mPropertiesWidget->stack();
378 }
379 
381 {
383 }
384 
385 //
386 // QgsEffectStackCompactWidget
387 //
388 
390  : QWidget( parent )
391  , mEnabledCheckBox( nullptr )
392  , mButton( nullptr )
393  , mPreviewPicture( nullptr )
394 {
395  QHBoxLayout* layout = new QHBoxLayout();
396  layout->setContentsMargins( 0, 0, 0, 0 );
397  layout->setSpacing( 0 );
398  setLayout( layout );
399 
400  mEnabledCheckBox = new QCheckBox( this );
401  mEnabledCheckBox->setText( tr( "Draw effects" ) );
402  layout->addWidget( mEnabledCheckBox );
403 
404  mButton = new QToolButton( this );
405  mButton->setIcon( QgsApplication::getThemeIcon( "mIconPaintEffects.svg" ) );
406  mButton->setToolTip( tr( "Customise effects" ) );
407  layout->addWidget( mButton );
408 
409  setFocusPolicy( Qt::StrongFocus );
410  setFocusProxy( mEnabledCheckBox );
411 
412  connect( mButton, SIGNAL( clicked() ), this, SLOT( showDialog() ) );
413  connect( mEnabledCheckBox, SIGNAL( toggled( bool ) ), this, SLOT( enableToggled( bool ) ) );
414 
415  setPaintEffect( effect );
416 }
417 
419 {
420  delete mPreviewPicture;
421 }
422 
424 {
425  if ( !effect )
426  {
427  mEnabledCheckBox->setChecked( false );
428  mEnabledCheckBox->setEnabled( false );
429  mButton->setEnabled( false );
430  mStack = nullptr;
431  return;
432  }
433 
434  //is effect a stack?
435  QgsEffectStack* stack = dynamic_cast<QgsEffectStack*>( effect );
436  if ( !stack )
437  {
438  //not already a stack, so promote to stack
439  stack = new QgsEffectStack( *effect );
440  }
441 
442  mStack = stack;
443  mEnabledCheckBox->setChecked( mStack->enabled() );
444  mEnabledCheckBox->setEnabled( true );
445  mButton->setEnabled( mStack->enabled() );
446 }
447 
449 {
450  delete mPreviewPicture;
451  mPreviewPicture = new QPicture( picture );
452 }
453 
454 void QgsEffectStackCompactWidget::showDialog()
455 {
456  if ( !mStack )
457  return;
458 
459  QgsEffectStack* clone = static_cast<QgsEffectStack*>( mStack->clone() );
460  QgsEffectStackPropertiesDialog dialog( clone, this );
461  if ( mPreviewPicture )
462  {
463  dialog.setPreviewPicture( *mPreviewPicture );
464  }
465  if ( dialog.exec() == QDialog::Accepted )
466  {
467  *mStack = *clone;
468  emit changed();
469  }
470 
471  delete clone;
472 }
473 
474 void QgsEffectStackCompactWidget::enableToggled( bool checked )
475 {
476  if ( !mStack )
477  {
478  return;
479  }
480 
481  mStack->setEnabled( checked );
482  mButton->setEnabled( checked );
483  emit changed();
484 }
QLayout * layout() const
void addEffect()
Adds a new effect to the stack.
EffectItem * currentEffectItem()
Returns the currently selected effect within the stack.
static unsigned index
void setContentsMargins(int left, int top, int right, int bottom)
void setEnabled(const bool enabled)
Sets whether the effect is enabled.
void setupUi(QWidget *widget)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const=0
bool end()
void setRenderHint(RenderHint hint, bool on)
void setPaintEffect(QgsPaintEffect *effect)
Sets paint effect attached to the widget.
QStandardItem * invisibleRootItem() const
QList< QStandardItem * > takeRow(int row)
void emitDataChanged()
void setFocusPolicy(Qt::FocusPolicy policy)
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
A dialog for modifying the properties of a QgsEffectStack, including adding and reordering effects wi...
Base class for visual effects which can be applied to QPicture drawings.
void removeRow(int row)
A generic dialog with layout and button box.
Definition: qgsdialog.h:30
int exec()
QPixmap fromImage(const QImage &image, QFlags< Qt::ImageConversionFlag > flags)
void updatePreview()
Updates the effect preview icon.
void setIcon(const QIcon &icon)
QString tr(const char *sourceText, const char *disambiguation, int n)
QgsEffectStackCompactWidget(QWidget *parent=nullptr, QgsPaintEffect *effect=nullptr)
QgsEffectStackCompactWidget constructor.
A widget for modifying the properties of a QgsEffectStack, including adding and reordering effects wi...
virtual void setData(const QVariant &value, int role)
void changeEffect(QgsPaintEffect *newEffect)
Updates the effect stack when the currently selected effect changes properties.
bool isValid() const
static QgsRenderContext createRenderContext(QPainter *p)
Creates a render context for a pixel based device.
void addWidget(QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
int count() const
Returns count of effects contained by the stack.
void setLayout(QLayout *layout)
void setPreviewPicture(const QPicture &picture)
Sets the picture to use for effect previews for the dialog.
QModelIndex indexFromItem(const QStandardItem *item) const
void fill(uint pixelValue)
static QgsPaintEffectRegistry * instance()
Returns a reference to the singleton instance of the paint effect registry.
void setPen(const QColor &color)
void drawEllipse(const QRectF &rectangle)
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.
void insertRows(int row, const QList< QStandardItem * > &items)
void setFocusProxy(QWidget *w)
A widget which modifies the properties of a QgsPaintEffect.
void setBrush(const QBrush &brush)
QgsEffectStackPropertiesWidget(QgsEffectStack *stack, QWidget *parent=nullptr)
QgsEffectStackPropertiesWidget constructor.
A paint effect which consists of a stack of other chained paint effects.
virtual QgsEffectStack * clone() const override
Duplicates an effect by creating a deep copy of the effect.
bool changeEffect(const int index, QgsPaintEffect *effect)
Replaces the effect at a specified position within the stack.
bool enabled() const
Returns whether the effect is enabled.
bool insertEffect(const int index, QgsPaintEffect *effect)
Inserts an effect at a specified index within the stack.
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.
QStandardItem * child(int row, int column) const
virtual int type() const
QgsEffectStackPropertiesDialog(QgsEffectStack *stack, QWidget *parent=nullptr, const 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.
void setChecked(bool)
Contains information about the context of a rendering operation.
QPainter * painter()
const QAbstractItemModel * model() const
QStandardItem * itemFromIndex(const QModelIndex &index) const
void changed()
Emitted when the paint effect properties change.
void setWindowTitle(const QString &)
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:40
bool toBool() const
void translate(const QPointF &offset)
QModelIndex index() const
void setText(const QString &text)
void setWidget(QWidget *widget)
Sets the effect properties widget.
QgsEffectStackPropertiesWidget * mPropertiesWidget
int rowCount() const
typedef WindowFlags
virtual void render(QPicture &picture, QgsRenderContext &context)
Renders a picture using the effect.
void effectChanged()
Updates the widget when the selected effect changes type.
void moveEffectDown()
Moves the currently selected effect down in the stack.
void setToolTip(const QString &)
A paint effect which draws the source picture with minor or no alterations.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
bool begin(QPaintDevice *device)
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.
void setCheckable(bool checkable)
virtual QVariant data(int role) const
void insertRow(int row, const QList< QStandardItem * > &items)
void setSpacing(int spacing)
QgsPaintEffect * takeEffect(const int index)
Removes an effect from the stack and returns a pointer to it.