QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsvectortilebasicrendererwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectortilebasicrendererwidget.cpp
3  --------------------------------------
4  Date : April 2020
5  Copyright : (C) 2020 by Martin Dobias
6  Email : wonder dot sk at gmail dot 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 
18 #include "qgsguiutils.h"
19 #include "qgssymbollayerutils.h"
21 #include "qgsvectortilelayer.h"
23 #include "qgsstyle.h"
24 
25 #include <QAbstractListModel>
26 #include <QInputDialog>
27 #include <QMenu>
28 
29 
31 
32 
33 QgsVectorTileBasicRendererListModel::QgsVectorTileBasicRendererListModel( QgsVectorTileBasicRenderer *r, QObject *parent )
34  : QAbstractListModel( parent )
35  , mRenderer( r )
36 {
37 }
38 
39 int QgsVectorTileBasicRendererListModel::rowCount( const QModelIndex &parent ) const
40 {
41  if ( parent.isValid() )
42  return 0;
43 
44  return mRenderer->styles().count();
45 }
46 
47 int QgsVectorTileBasicRendererListModel::columnCount( const QModelIndex & ) const
48 {
49  return 5;
50 }
51 
52 QVariant QgsVectorTileBasicRendererListModel::data( const QModelIndex &index, int role ) const
53 {
54  if ( index.row() < 0 || index.row() >= mRenderer->styles().count() )
55  return QVariant();
56 
57  const QList<QgsVectorTileBasicRendererStyle> styles = mRenderer->styles();
58  const QgsVectorTileBasicRendererStyle &style = styles[index.row()];
59 
60  switch ( role )
61  {
62  case Qt::DisplayRole:
63  {
64  if ( index.column() == 0 )
65  return style.styleName();
66  else if ( index.column() == 1 )
67  return style.layerName().isEmpty() ? tr( "(all layers)" ) : style.layerName();
68  else if ( index.column() == 2 )
69  return style.minZoomLevel() >= 0 ? style.minZoomLevel() : QVariant();
70  else if ( index.column() == 3 )
71  return style.maxZoomLevel() >= 0 ? style.maxZoomLevel() : QVariant();
72  else if ( index.column() == 4 )
73  return style.filterExpression().isEmpty() ? tr( "(no filter)" ) : style.filterExpression();
74 
75  break;
76  }
77 
78  case Qt::EditRole:
79  {
80  if ( index.column() == 0 )
81  return style.styleName();
82  else if ( index.column() == 1 )
83  return style.layerName();
84  else if ( index.column() == 2 )
85  return style.minZoomLevel();
86  else if ( index.column() == 3 )
87  return style.maxZoomLevel();
88  else if ( index.column() == 4 )
89  return style.filterExpression();
90 
91  break;
92  }
93 
94  case Qt::DecorationRole:
95  {
96  if ( index.column() == 0 && style.symbol() )
97  {
98  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
100  }
101  break;
102  }
103 
104  case Qt::CheckStateRole:
105  {
106  if ( index.column() != 0 )
107  return QVariant();
108  return style.isEnabled() ? Qt::Checked : Qt::Unchecked;
109  }
110 
111  }
112  return QVariant();
113 }
114 
115 QVariant QgsVectorTileBasicRendererListModel::headerData( int section, Qt::Orientation orientation, int role ) const
116 {
117  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 5 )
118  {
119  QStringList lst;
120  lst << tr( "Label" ) << tr( "Layer" ) << tr( "Min. Zoom" ) << tr( "Max. Zoom" ) << tr( "Filter" );
121  return lst[section];
122  }
123 
124  return QVariant();
125 }
126 
127 Qt::ItemFlags QgsVectorTileBasicRendererListModel::flags( const QModelIndex &index ) const
128 {
129  if ( !index.isValid() )
130  return Qt::ItemIsDropEnabled;
131 
132  Qt::ItemFlag checkable = ( index.column() == 0 ? Qt::ItemIsUserCheckable : Qt::NoItemFlags );
133 
134  return Qt::ItemIsEnabled | Qt::ItemIsSelectable |
135  Qt::ItemIsEditable | checkable |
136  Qt::ItemIsDragEnabled;
137 }
138 
139 bool QgsVectorTileBasicRendererListModel::setData( const QModelIndex &index, const QVariant &value, int role )
140 {
141  if ( !index.isValid() )
142  return false;
143 
144  QgsVectorTileBasicRendererStyle style = mRenderer->style( index.row() );
145 
146  if ( role == Qt::CheckStateRole )
147  {
148  style.setEnabled( value.toInt() == Qt::Checked );
149  mRenderer->setStyle( index.row(), style );
150  emit dataChanged( index, index );
151  return true;
152  }
153 
154  if ( role == Qt::EditRole )
155  {
156  if ( index.column() == 0 )
157  style.setStyleName( value.toString() );
158  else if ( index.column() == 1 )
159  style.setLayerName( value.toString() );
160  else if ( index.column() == 2 )
161  style.setMinZoomLevel( value.toInt() );
162  else if ( index.column() == 3 )
163  style.setMaxZoomLevel( value.toInt() );
164  else if ( index.column() == 4 )
165  style.setFilterExpression( value.toString() );
166 
167  mRenderer->setStyle( index.row(), style );
168  emit dataChanged( index, index );
169  return true;
170  }
171 
172  return false;
173 }
174 
175 bool QgsVectorTileBasicRendererListModel::removeRows( int row, int count, const QModelIndex &parent )
176 {
177  QList<QgsVectorTileBasicRendererStyle> styles = mRenderer->styles();
178 
179  if ( row < 0 || row >= styles.count() )
180  return false;
181 
182  beginRemoveRows( parent, row, row + count - 1 );
183 
184  for ( int i = 0; i < count; i++ )
185  {
186  if ( row < styles.count() )
187  {
188  styles.removeAt( row );
189  }
190  }
191 
192  mRenderer->setStyles( styles );
193 
194  endRemoveRows();
195  return true;
196 }
197 
198 void QgsVectorTileBasicRendererListModel::insertStyle( int row, const QgsVectorTileBasicRendererStyle &style )
199 {
200  beginInsertRows( QModelIndex(), row, row );
201 
202  QList<QgsVectorTileBasicRendererStyle> styles = mRenderer->styles();
203  styles.insert( row, style );
204  mRenderer->setStyles( styles );
205 
206  endInsertRows();
207 }
208 
209 Qt::DropActions QgsVectorTileBasicRendererListModel::supportedDropActions() const
210 {
211  return Qt::MoveAction;
212 }
213 
214 QStringList QgsVectorTileBasicRendererListModel::mimeTypes() const
215 {
216  QStringList types;
217  types << QStringLiteral( "application/vnd.text.list" );
218  return types;
219 }
220 
221 QMimeData *QgsVectorTileBasicRendererListModel::mimeData( const QModelIndexList &indexes ) const
222 {
223  QMimeData *mimeData = new QMimeData();
224  QByteArray encodedData;
225 
226  QDataStream stream( &encodedData, QIODevice::WriteOnly );
227 
228  const auto constIndexes = indexes;
229  for ( const QModelIndex &index : constIndexes )
230  {
231  // each item consists of several columns - let's add it with just first one
232  if ( !index.isValid() || index.column() != 0 )
233  continue;
234 
235  QgsVectorTileBasicRendererStyle style = mRenderer->style( index.row() );
236 
237  QDomDocument doc;
238  QDomElement rootElem = doc.createElement( QStringLiteral( "vector_tile_basic_renderer_style_mime" ) );
239  style.writeXml( rootElem, QgsReadWriteContext() );
240  doc.appendChild( rootElem );
241 
242  stream << doc.toString( -1 );
243  }
244 
245  mimeData->setData( QStringLiteral( "application/vnd.text.list" ), encodedData );
246  return mimeData;
247 }
248 
249 bool QgsVectorTileBasicRendererListModel::dropMimeData( const QMimeData *data,
250  Qt::DropAction action, int row, int column, const QModelIndex &parent )
251 {
252  Q_UNUSED( column )
253 
254  if ( action == Qt::IgnoreAction )
255  return true;
256 
257  if ( !data->hasFormat( QStringLiteral( "application/vnd.text.list" ) ) )
258  return false;
259 
260  if ( parent.column() > 0 )
261  return false;
262 
263  QByteArray encodedData = data->data( QStringLiteral( "application/vnd.text.list" ) );
264  QDataStream stream( &encodedData, QIODevice::ReadOnly );
265  int rows = 0;
266 
267  if ( row == -1 )
268  {
269  // the item was dropped at a parent - we may decide where to put the items - let's append them
270  row = rowCount( parent );
271  }
272 
273  while ( !stream.atEnd() )
274  {
275  QString text;
276  stream >> text;
277 
278  QDomDocument doc;
279  if ( !doc.setContent( text ) )
280  continue;
281  QDomElement rootElem = doc.documentElement();
282  if ( rootElem.tagName() != QLatin1String( "vector_tile_basic_renderer_style_mime" ) )
283  continue;
284 
286  style.readXml( rootElem, QgsReadWriteContext() );
287 
288  insertStyle( row + rows, style );
289  ++rows;
290  }
291  return true;
292 }
293 
294 
295 //
296 
297 
298 QgsVectorTileBasicRendererWidget::QgsVectorTileBasicRendererWidget( QgsVectorTileLayer *layer, QgsMapCanvas *canvas, QgsMessageBar *messageBar, QWidget *parent )
299  : QgsMapLayerConfigWidget( layer, canvas, parent )
300  , mMessageBar( messageBar )
301 {
302  setupUi( this );
303  layout()->setContentsMargins( 0, 0, 0, 0 );
304 
305  QMenu *menuAddRule = new QMenu( btnAddRule );
306  menuAddRule->addAction( tr( "Marker" ), this, [this] { addStyle( QgsWkbTypes::PointGeometry ); } );
307  menuAddRule->addAction( tr( "Line" ), this, [this] { addStyle( QgsWkbTypes::LineGeometry ); } );
308  menuAddRule->addAction( tr( "Fill" ), this, [this] { addStyle( QgsWkbTypes::PolygonGeometry ); } );
309  btnAddRule->setMenu( menuAddRule );
310 
311  connect( btnEditRule, &QPushButton::clicked, this, &QgsVectorTileBasicRendererWidget::editStyle );
312  connect( btnRemoveRule, &QAbstractButton::clicked, this, &QgsVectorTileBasicRendererWidget::removeStyle );
313 
314  connect( viewStyles, &QAbstractItemView::doubleClicked, this, &QgsVectorTileBasicRendererWidget::editStyleAtIndex );
315 
316  setLayer( layer );
317 }
318 
319 void QgsVectorTileBasicRendererWidget::setLayer( QgsVectorTileLayer *layer )
320 {
321  mVTLayer = layer;
322 
323  if ( layer && layer->renderer() && layer->renderer()->type() == QStringLiteral( "basic" ) )
324  {
325  mRenderer.reset( static_cast<QgsVectorTileBasicRenderer *>( layer->renderer()->clone() ) );
326  }
327  else
328  {
329  mRenderer.reset( new QgsVectorTileBasicRenderer() );
330  }
331 
332  mModel = new QgsVectorTileBasicRendererListModel( mRenderer.get(), viewStyles );
333  viewStyles->setModel( mModel );
334 
335  connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsPanelWidget::widgetChanged );
336  connect( mModel, &QAbstractItemModel::rowsInserted, this, &QgsPanelWidget::widgetChanged );
337  connect( mModel, &QAbstractItemModel::rowsRemoved, this, &QgsPanelWidget::widgetChanged );
338 }
339 
340 QgsVectorTileBasicRendererWidget::~QgsVectorTileBasicRendererWidget() = default;
341 
342 void QgsVectorTileBasicRendererWidget::apply()
343 {
344  mVTLayer->setRenderer( mRenderer->clone() );
345 }
346 
347 void QgsVectorTileBasicRendererWidget::addStyle( QgsWkbTypes::GeometryType geomType )
348 {
349  QgsVectorTileBasicRendererStyle style( QString(), QString(), geomType );
350  style.setSymbol( QgsSymbol::defaultSymbol( geomType ) );
351 
352  int rows = mModel->rowCount();
353  mModel->insertStyle( rows, style );
354  viewStyles->selectionModel()->setCurrentIndex( mModel->index( rows, 0 ), QItemSelectionModel::ClearAndSelect );
355 }
356 
357 void QgsVectorTileBasicRendererWidget::editStyle()
358 {
359  editStyleAtIndex( viewStyles->selectionModel()->currentIndex() );
360 }
361 
362 void QgsVectorTileBasicRendererWidget::editStyleAtIndex( const QModelIndex &index )
363 {
364  QgsVectorTileBasicRendererStyle style = mRenderer->style( index.row() );
365 
366  if ( !style.symbol() )
367  return;
368 
369  std::unique_ptr< QgsSymbol > symbol( style.symbol()->clone() );
370 
371  QgsSymbolWidgetContext context;
372  context.setMapCanvas( mMapCanvas );
373  context.setMessageBar( mMessageBar );
374 
375  QgsVectorLayer *vectorLayer = nullptr; // TODO: have a temporary vector layer with sub-layer's fields?
376 
378  if ( panel && panel->dockMode() )
379  {
380  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( symbol.release(), QgsStyle::defaultStyle(), vectorLayer, panel );
381  dlg->setContext( context );
382  dlg->setPanelTitle( style.styleName() );
383  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsVectorTileBasicRendererWidget::updateSymbolsFromWidget );
384  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsVectorTileBasicRendererWidget::cleanUpSymbolSelector );
385  openPanel( dlg );
386  }
387  else
388  {
389  QgsSymbolSelectorDialog dlg( symbol.get(), QgsStyle::defaultStyle(), vectorLayer, panel );
390  dlg.setContext( context );
391  if ( !dlg.exec() || !symbol )
392  {
393  return;
394  }
395 
396  style.setSymbol( symbol.release() );
397  mRenderer->setStyle( index.row(), style );
398  emit widgetChanged();
399  }
400 }
401 
402 void QgsVectorTileBasicRendererWidget::updateSymbolsFromWidget()
403 {
404  int index = viewStyles->selectionModel()->currentIndex().row();
405  QgsVectorTileBasicRendererStyle style = mRenderer->style( index );
406 
407  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( sender() );
408  style.setSymbol( dlg->symbol()->clone() );
409 
410  mRenderer->setStyle( index, style );
411  emit widgetChanged();
412 }
413 
414 void QgsVectorTileBasicRendererWidget::cleanUpSymbolSelector( QgsPanelWidget *container )
415 {
416  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( container );
417  if ( !dlg )
418  return;
419 
420  delete dlg->symbol();
421 }
422 
423 void QgsVectorTileBasicRendererWidget::removeStyle()
424 {
425  QItemSelection sel = viewStyles->selectionModel()->selection();
426  const auto constSel = sel;
427  for ( const QItemSelectionRange &range : constSel )
428  {
429  if ( range.isValid() )
430  mModel->removeRows( range.top(), range.bottom() - range.top() + 1, range.parent() );
431  }
432  // make sure that the selection is gone
433  viewStyles->selectionModel()->clear();
434 }
435 
QgsSymbolSelectorDialog
Definition: qgssymbolselectordialog.h:263
QgsVectorTileRenderer::type
virtual QString type() const =0
Returns unique type name of the renderer implementation.
QgsVectorTileLayer
Definition: qgsvectortilelayer.h:83
QgsVectorTileBasicRendererStyle::writeXml
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const
Writes object content to given DOM element.
Definition: qgsvectortilebasicrenderer.cpp:60
QgsReadWriteContext
Definition: qgsreadwritecontext.h:34
QgsSymbol::defaultSymbol
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:301
QgsPanelWidget::findParentPanel
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
Definition: qgspanelwidget.cpp:49
QgsVectorTileBasicRendererStyle::setEnabled
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering)
Definition: qgsvectortilebasicrenderer.h:83
QgsSymbolSelectorWidget::symbol
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
Definition: qgssymbolselectordialog.h:129
qgssymbollayerutils.h
QgsVectorTileBasicRendererStyle::setSymbol
void setSymbol(QgsSymbol *sym)
Sets symbol for rendering. Takes ownership of the symbol.
Definition: qgsvectortilebasicrenderer.cpp:55
QgsVectorTileBasicRendererStyle::isEnabled
bool isEnabled() const
Returns whether this style is enabled (used for rendering)
Definition: qgsvectortilebasicrenderer.h:85
QgsSymbolWidgetContext
Definition: qgssymbolwidgetcontext.h:35
QgsMapCanvas
Definition: qgsmapcanvas.h:83
QgsVectorTileBasicRenderer
Definition: qgsvectortilebasicrenderer.h:127
QgsSymbolSelectorWidget
Definition: qgssymbolselectordialog.h:87
QgsSymbolSelectorWidget::setContext
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
Definition: qgssymbolselectordialog.cpp:355
QgsVectorTileBasicRendererStyle::maxZoomLevel
int maxZoomLevel() const
Returns maxnimum zoom level index (negative number means no limit)
Definition: qgsvectortilebasicrenderer.h:95
QgsPanelWidget::dockMode
bool dockMode()
Returns the dock mode state.
Definition: qgspanelwidget.h:83
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:143
QgsGuiUtils::iconSize
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
Definition: qgsguiutils.cpp:264
QgsMapLayerConfigWidget
A panel widget that can be shown in the map style dock.
Definition: qgsmaplayerconfigwidget.h:33
QgsSymbolWidgetContext::setMapCanvas
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the widget.
Definition: qgssymbolwidgetcontext.cpp:49
QgsStyle::defaultStyle
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:111
qgsvectortilelayer.h
QgsVectorTileBasicRendererStyle
Definition: qgsvectortilebasicrenderer.h:47
QgsSymbolLayerUtils::symbolPreviewIcon
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr)
Returns an icon preview for a color ramp.
Definition: qgssymbollayerutils.cpp:762
QgsSymbolWidgetContext::setMessageBar
void setMessageBar(QgsMessageBar *bar)
Sets the message bar associated with the widget.
Definition: qgssymbolwidgetcontext.cpp:59
QgsVectorTileBasicRendererStyle::layerName
QString layerName() const
Returns name of the sub-layer to render (empty layer means that all layers match)
Definition: qgsvectortilebasicrenderer.h:65
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsPanelWidget::panelAccepted
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
QgsVectorTileLayer::renderer
QgsVectorTileRenderer * renderer() const
Returns currently assigned renderer.
Definition: qgsvectortilelayer.cpp:305
QgsVectorTileBasicRendererStyle::setFilterExpression
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match)
Definition: qgsvectortilebasicrenderer.h:73
QgsMessageBar
Definition: qgsmessagebar.h:60
QgsPanelWidget::widgetChanged
void widgetChanged()
Emitted when the widget state changes.
QgsPanelWidget::setPanelTitle
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
Definition: qgspanelwidget.h:44
QgsVectorTileBasicRendererStyle::setLayerName
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match)
Definition: qgsvectortilebasicrenderer.h:63
qgssymbolselectordialog.h
qgsstyle.h
QgsVectorTileBasicRendererStyle::minZoomLevel
int minZoomLevel() const
Returns minimum zoom level index (negative number means no limit)
Definition: qgsvectortilebasicrenderer.h:90
QgsSymbol::clone
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
QgsWkbTypes::LineGeometry
@ LineGeometry
Definition: qgswkbtypes.h:142
QgsWkbTypes::PointGeometry
@ PointGeometry
Definition: qgswkbtypes.h:141
QgsWkbTypes::GeometryType
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:139
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsVectorTileBasicRendererStyle::setMinZoomLevel
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit)
Definition: qgsvectortilebasicrenderer.h:88
QgsVectorTileBasicRendererStyle::symbol
QgsSymbol * symbol() const
Returns symbol for rendering.
Definition: qgsvectortilebasicrenderer.h:80
QgsVectorTileBasicRendererStyle::filterExpression
QString filterExpression() const
Returns filter expression (empty filter means that all features match)
Definition: qgsvectortilebasicrenderer.h:75
qgsvectortilebasicrenderer.h
QgsVectorTileBasicRendererStyle::setMaxZoomLevel
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit)
Definition: qgsvectortilebasicrenderer.h:93
QgsVectorTileRenderer::clone
virtual QgsVectorTileRenderer * clone() const =0
Returns a clone of the renderer.
QgsVectorTileBasicRendererStyle::setStyleName
void setStyleName(const QString &name)
Sets human readable name of this style.
Definition: qgsvectortilebasicrenderer.h:58
qgsguiutils.h
QgsGuiUtils::scaleIconSize
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
Definition: qgsguiutils.cpp:257
QgsVectorTileBasicRendererStyle::styleName
QString styleName() const
Returns human readable name of this style.
Definition: qgsvectortilebasicrenderer.h:60
QgsVectorTileBasicRendererStyle::readXml
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Reads object content from given DOM element.
Definition: qgsvectortilebasicrenderer.cpp:77
qgsvectortilebasicrendererwidget.h