QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 #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 EffectItemType = 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;
51  }
52 
53  int type() const override { return EffectItemType; }
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 QgsPaintEffectRegistry::instance()->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;
89 };
91 
92 //
93 // QgsEffectStackPropertiesWidget
94 //
95 
97  : QgsPanelWidget( parent )
98  , mStack( stack )
99  , mPreviewPicture( nullptr )
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( "symbologyUp.svg" ) ) );
114  mDownButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyDown.svg" ) ) );
115 
116  mModel = new QStandardItemModel();
117  // Set the effect
118  mEffectsList->setModel( mModel );
119 
120  QItemSelectionModel* selModel = mEffectsList->selectionModel();
121  connect( selModel, SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), this, SLOT( effectChanged() ) );
122 
123  loadStack( stack );
124  updatePreview();
125 
126  connect( mUpButton, SIGNAL( clicked() ), this, SLOT( moveEffectUp() ) );
127  connect( mDownButton, SIGNAL( clicked() ), this, SLOT( moveEffectDown() ) );
128  connect( mAddButton, SIGNAL( clicked() ), this, SLOT( addEffect() ) );
129  connect( mRemoveButton, SIGNAL( clicked() ), this, SLOT( 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 
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 );
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  QWidget *effectPropertiesWidget = new QgsPaintEffectPropertiesWidget( currentItem->effect() );
257  setWidget( effectPropertiesWidget );
258 
259  connect( effectPropertiesWidget, SIGNAL( changeEffect( QgsPaintEffect* ) ), this, SLOT( changeEffect( QgsPaintEffect* ) ) );
260  connect( effectPropertiesWidget, SIGNAL( changed() ), this, SLOT( updatePreview() ) );
261 
262 }
263 
265 {
266  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();
287  updatePreview();
288 }
289 
291 {
292  EffectItem *item = currentEffectItem();
293  int row = item->row();
295 
296  int layerIdx = root->rowCount() - row - 1;
297  QgsPaintEffect *tmpEffect = mStack->takeEffect( layerIdx );
298 
300 
301  int newSelection = qMin( row, root->rowCount() - 1 );
302  QModelIndex newIdx = root->child( newSelection )->index();
303  mEffectsList->setCurrentIndex( newIdx );
304 
305  updateUi();
306  updatePreview();
307 
308  delete tmpEffect;
309 }
310 
312 {
313  moveEffectByOffset( + 1 );
314 }
315 
317 {
318  moveEffectByOffset( -1 );
319 }
320 
322 {
323  EffectItem *item = currentEffectItem();
324  if ( !item )
325  return;
326 
327  int row = item->row();
328 
330 
331  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  QModelIndex newIdx = toMove[ 0 ]->index();
340  mEffectsList->setCurrentIndex( newIdx );
341 
342  updatePreview();
343  updateUi();
344 }
345 
347 {
348  EffectItem *item = currentEffectItem();
349  item->setEffect( newEffect );
350 
352  int effectIdx = root->rowCount() - item->row() - 1;
353  mStack->changeEffect( effectIdx, newEffect );
354 
355  updatePreview();
356  // Important: This lets the effect to have its own effect properties widget
357  effectChanged();
358 }
359 
360 
361 //
362 // QgsEffectStackPropertiesDialog
363 //
364 
366  : QgsDialog( parent, f, QDialogButtonBox::Ok | QDialogButtonBox::Cancel )
367  , mPropertiesWidget( nullptr )
368 {
369  setWindowTitle( tr( "Effect Properties" ) );
372 }
373 
375 {
376 
377 }
378 
380 {
381  return mPropertiesWidget->stack();
382 }
383 
385 {
387 }
388 
389 //
390 // QgsEffectStackCompactWidget
391 //
392 
394  : QgsPanelWidget( parent )
395  , mEnabledCheckBox( nullptr )
396  , mButton( nullptr )
397  , mPreviewPicture( nullptr )
398 {
399  QHBoxLayout* layout = new QHBoxLayout();
400  layout->setContentsMargins( 0, 0, 0, 0 );
401  layout->setSpacing( 0 );
402  setLayout( layout );
403 
404  mEnabledCheckBox = new QCheckBox( this );
405  mEnabledCheckBox->setText( tr( "Draw effects" ) );
406  layout->addWidget( mEnabledCheckBox );
407 
408  mButton = new QToolButton( this );
409  mButton->setIcon( QgsApplication::getThemeIcon( "mIconPaintEffects.svg" ) );
410  mButton->setToolTip( tr( "Customise effects" ) );
411  layout->addWidget( mButton );
412 
413  setFocusPolicy( Qt::StrongFocus );
414  setFocusProxy( mEnabledCheckBox );
415 
416  connect( mButton, SIGNAL( clicked() ), this, SLOT( showDialog() ) );
417  connect( mEnabledCheckBox, SIGNAL( toggled( bool ) ), this, SLOT( enableToggled( bool ) ) );
418 
419  setPaintEffect( effect );
420 }
421 
423 {
424  delete mPreviewPicture;
425 }
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  delete mPreviewPicture;
455  mPreviewPicture = new QPicture( picture );
456 }
457 
458 void QgsEffectStackCompactWidget::showDialog()
459 {
460  if ( !mStack )
461  return;
462 
463  QgsEffectStack* clone = static_cast<QgsEffectStack*>( mStack->clone() );
464  QgsEffectStackPropertiesWidget* widget = new QgsEffectStackPropertiesWidget( clone, nullptr );
465  if ( mPreviewPicture )
466  {
467  widget->setPreviewPicture( *mPreviewPicture );
468  }
469  connect( widget, SIGNAL( widgetChanged() ), this, SLOT( updateEffectLive() ) );
470  connect( widget, SIGNAL( panelAccepted( QgsPanelWidget* ) ), this, SLOT( updateAcceptWidget( QgsPanelWidget* ) ) );
471  openPanel( widget );
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 }
485 
486 void QgsEffectStackCompactWidget::updateAcceptWidget( QgsPanelWidget *panel )
487 {
488  QgsEffectStackPropertiesWidget* widget = qobject_cast<QgsEffectStackPropertiesWidget*>( panel );
489  *mStack = *widget->stack();
490  emit changed();
491 // delete widget->stack();
492 }
493 
494 void QgsEffectStackCompactWidget::updateEffectLive()
495 {
497  *mStack = *widget->stack();
498  emit changed();
499 }
QLayout * layout() const
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.
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)
QObject * sender() const
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.
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
QPixmap fromImage(const QImage &image, QFlags< Qt::ImageConversionFlag > flags)
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.
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
void widgetChanged()
Emitted when the widget state changes.
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
void setPanelTitle(QString panelTitle)
Set the title of the panel when shown in the interface.
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.