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