QGIS API Documentation  2.14.0-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 effect as active item in the tree
133  QModelIndex newIndex = mEffectsList->model()->index( 0, 0 );
134  mEffectsList->setCurrentIndex( newIndex );
135 }
136 
138 {
139  delete mPreviewPicture;
140 }
141 
143 {
144  if ( mPreviewPicture )
145  {
146  delete mPreviewPicture;
147  }
148 
149  mPreviewPicture = new QPicture( picture );
150  updatePreview();
151 }
152 
154 {
155  if ( !stack )
156  {
157  return;
158  }
159 
160  EffectItem* parent = static_cast<EffectItem*>( mModel->invisibleRootItem() );
161 
162  int count = stack->count();
163  for ( int i = count - 1; i >= 0; i-- )
164  {
165  EffectItem* effectItem = new EffectItem( stack->effect( i ), this );
166  effectItem->setEditable( false );
167  parent->appendRow( effectItem );
168  }
169 }
170 
171 
173 {
174  mModel->clear();
175  loadStack( mStack );
176 }
177 
179 {
180  QModelIndex currentIdx = mEffectsList->currentIndex();
181  if ( !currentIdx.isValid() )
182  return;
183 
184  EffectItem *item = static_cast<EffectItem*>( mModel->itemFromIndex( currentIdx ) );
185 
187  int rowCount = root->rowCount();
188  int currentRow = item ? item->row() : 0;
189 
190  mUpButton->setEnabled( currentRow > 0 );
191  mDownButton->setEnabled( currentRow < rowCount - 1 );
192  mRemoveButton->setEnabled( rowCount > 1 );
193 }
194 
196 {
197  QPainter painter;
198  QImage previewImage( 150, 150, QImage::Format_ARGB32 );
199  previewImage.fill( Qt::transparent );
200  painter.begin( &previewImage );
201  painter.setRenderHint( QPainter::Antialiasing );
203  if ( !mPreviewPicture )
204  {
205  QPicture previewPic;
206  QPainter previewPicPainter;
207  previewPicPainter.begin( &previewPic );
208  previewPicPainter.setPen( Qt::red );
209  previewPicPainter.setBrush( QColor( 255, 100, 100, 255 ) );
210  previewPicPainter.drawEllipse( QPoint( 75, 75 ), 30, 30 );
211  previewPicPainter.end();
212  mStack->render( previewPic, context );
213  }
214  else
215  {
216  context.painter()->translate( 35, 35 );
217  mStack->render( *mPreviewPicture, context );
218  }
219  painter.end();
220 
221  lblPreview->setPixmap( QPixmap::fromImage( previewImage ) );
222 }
223 
225 {
226  QModelIndex idx = mEffectsList->currentIndex();
227  if ( !idx.isValid() )
228  return nullptr;
229 
230  EffectItem *item = static_cast<EffectItem*>( mModel->itemFromIndex( idx ) );
231  return item;
232 }
233 
235 {
236  updateUi();
237 
238  EffectItem* currentItem = currentEffectItem();
239  if ( !currentItem )
240  return;
241 
242  QWidget *effectPropertiesWidget = new QgsPaintEffectPropertiesWidget( currentItem->effect() );
243  setWidget( effectPropertiesWidget );
244 
245  connect( effectPropertiesWidget, SIGNAL( changeEffect( QgsPaintEffect* ) ), this, SLOT( changeEffect( QgsPaintEffect* ) ) );
246  connect( effectPropertiesWidget, SIGNAL( changed() ), this, SLOT( updatePreview() ) );
247 
248 }
249 
251 {
252  int index = stackedWidget->addWidget( widget );
253  stackedWidget->setCurrentIndex( index );
254  if ( mPresentWidget )
255  {
256  stackedWidget->removeWidget( mPresentWidget );
257  QWidget *dummy = mPresentWidget;
258  mPresentWidget = widget;
259  delete dummy; // auto disconnects all signals
260  }
261 }
262 
264 {
265  QgsPaintEffect* newEffect = new QgsDrawSourceEffect();
266  mStack->insertEffect( 0, newEffect );
267 
268  EffectItem *newEffectItem = new EffectItem( newEffect, this );
269  mModel->invisibleRootItem()->insertRow( mStack->count() - 1, newEffectItem );
270 
271  mEffectsList->setCurrentIndex( mModel->indexFromItem( newEffectItem ) );
272  updateUi();
273  updatePreview();
274 }
275 
277 {
278  EffectItem *item = currentEffectItem();
279  int row = item->row();
281 
282  int layerIdx = root->rowCount() - row - 1;
283  QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
284 
286 
287  int newSelection = qMin( row, root->rowCount() - 1 );
288  QModelIndex newIdx = root->child( newSelection )->index();
289  mEffectsList->setCurrentIndex( newIdx );
290 
291  updateUi();
292  updatePreview();
293 
294  delete tmpEffect;
295 }
296 
298 {
299  moveEffectByOffset( + 1 );
300 }
301 
303 {
304  moveEffectByOffset( -1 );
305 }
306 
308 {
309  EffectItem *item = currentEffectItem();
310  if ( !item )
311  return;
312 
313  int row = item->row();
314 
316 
317  int layerIdx = root->rowCount() - row - 1;
318  // switch effects
319  QgsPaintEffect* tmpEffect = mStack->takeEffect( layerIdx );
320  mStack->insertEffect( layerIdx - offset, tmpEffect );
321 
322  QList<QStandardItem *> toMove = root->takeRow( row );
323  root->insertRows( row + offset, toMove );
324 
325  QModelIndex newIdx = toMove[ 0 ]->index();
326  mEffectsList->setCurrentIndex( newIdx );
327 
328  updatePreview();
329  updateUi();
330 }
331 
333 {
334  EffectItem *item = currentEffectItem();
335  item->setEffect( newEffect );
336 
338  int effectIdx = root->rowCount() - item->row() - 1;
339  mStack->changeEffect( effectIdx, newEffect );
340 
341  updatePreview();
342  // Important: This lets the effect to have its own effect properties widget
343  effectChanged();
344 }
345 
346 
347 //
348 // QgsEffectStackPropertiesDialog
349 //
350 
352  : QgsDialog( parent, f, QDialogButtonBox::Ok | QDialogButtonBox::Cancel )
353  , mPropertiesWidget( nullptr )
354 {
355  setWindowTitle( tr( "Effect Properties" ) );
358 }
359 
361 {
362 
363 }
364 
366 {
367  return mPropertiesWidget->stack();
368 }
369 
371 {
373 }
374 
375 //
376 // QgsEffectStackCompactWidget
377 //
378 
380  : QWidget( parent )
381  , mEnabledCheckBox( nullptr )
382  , mButton( nullptr )
383  , mPreviewPicture( nullptr )
384 {
385  QHBoxLayout* layout = new QHBoxLayout();
386  layout->setContentsMargins( 0, 0, 0, 0 );
387  layout->setSpacing( 0 );
388  setLayout( layout );
389 
390  mEnabledCheckBox = new QCheckBox( this );
391  mEnabledCheckBox->setText( tr( "Draw effects" ) );
392  layout->addWidget( mEnabledCheckBox );
393 
394  mButton = new QToolButton( this );
395  mButton->setIcon( QgsApplication::getThemeIcon( "mIconPaintEffects.svg" ) );
396  mButton->setToolTip( tr( "Customise effects" ) );
397  layout->addWidget( mButton );
398 
399  setFocusPolicy( Qt::StrongFocus );
400  setFocusProxy( mEnabledCheckBox );
401 
402  connect( mButton, SIGNAL( clicked() ), this, SLOT( showDialog() ) );
403  connect( mEnabledCheckBox, SIGNAL( toggled( bool ) ), this, SLOT( enableToggled( bool ) ) );
404 
405  setPaintEffect( effect );
406 }
407 
409 {
410  delete mPreviewPicture;
411 }
412 
414 {
415  if ( !effect )
416  {
417  mEnabledCheckBox->setChecked( false );
418  mEnabledCheckBox->setEnabled( false );
419  mButton->setEnabled( false );
420  mStack = nullptr;
421  return;
422  }
423 
424  //is effect a stack?
425  QgsEffectStack* stack = dynamic_cast<QgsEffectStack*>( effect );
426  if ( !stack )
427  {
428  //not already a stack, so promote to stack
429  stack = new QgsEffectStack( *effect );
430  }
431 
432  mStack = stack;
433  mEnabledCheckBox->setChecked( mStack->enabled() );
434  mEnabledCheckBox->setEnabled( true );
435  mButton->setEnabled( mStack->enabled() );
436 }
437 
439 {
440  delete mPreviewPicture;
441  mPreviewPicture = new QPicture( picture );
442 }
443 
444 void QgsEffectStackCompactWidget::showDialog()
445 {
446  if ( !mStack )
447  return;
448 
449  QgsEffectStack* clone = static_cast<QgsEffectStack*>( mStack->clone() );
450  QgsEffectStackPropertiesDialog dialog( clone, this );
451  if ( mPreviewPicture )
452  {
453  dialog.setPreviewPicture( *mPreviewPicture );
454  }
455  if ( dialog.exec() == QDialog::Accepted )
456  {
457  *mStack = *clone;
458  emit changed();
459  }
460 
461  delete clone;
462 }
463 
464 void QgsEffectStackCompactWidget::enableToggled( bool checked )
465 {
466  if ( !mStack )
467  {
468  return;
469  }
470 
471  mStack->setEnabled( checked );
472  mButton->setEnabled( checked );
473  emit changed();
474 }
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...
bool enabled() const
Returns whether the effect is enabled.
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 setEnabled(bool)
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)
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 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.
QgsPaintEffectAbstractMetadata * effectMetadata(const QString &name) const
Returns the metadata for a specific effect.
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.
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)
QgsPaintEffect * effect(int index) const
Returns a pointer to the effect at a specified index 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.
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.